LCOV - code coverage report
Current view: top level - frmts/raw - ehdrdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 680 972 70.0 %
Date: 2025-01-18 12:42:00 Functions: 33 34 97.1 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  ESRI .hdr Driver
       4             :  * Purpose:  Implementation of EHdrDataset
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "ehdrdataset.h"
      16             : #include "rawdataset.h"
      17             : 
      18             : #include <algorithm>
      19             : #include <cctype>
      20             : #include <cerrno>
      21             : #include <climits>
      22             : #include <cmath>
      23             : #include <cstddef>
      24             : #include <cstdio>
      25             : #include <cstdlib>
      26             : #include <cstring>
      27             : #if HAVE_FCNTL_H
      28             : #include <fcntl.h>
      29             : #endif
      30             : 
      31             : #include <limits>
      32             : 
      33             : #include "cpl_conv.h"
      34             : #include "cpl_error.h"
      35             : #include "cpl_progress.h"
      36             : #include "cpl_string.h"
      37             : #include "cpl_vsi.h"
      38             : #include "gdal.h"
      39             : #include "gdal_frmts.h"
      40             : #include "gdal_pam.h"
      41             : #include "gdal_priv.h"
      42             : #include "ogr_core.h"
      43             : #include "ogr_spatialref.h"
      44             : 
      45             : constexpr int HAS_MIN_FLAG = 0x1;
      46             : constexpr int HAS_MAX_FLAG = 0x2;
      47             : constexpr int HAS_MEAN_FLAG = 0x4;
      48             : constexpr int HAS_STDDEV_FLAG = 0x8;
      49             : constexpr int HAS_ALL_FLAGS =
      50             :     HAS_MIN_FLAG | HAS_MAX_FLAG | HAS_MEAN_FLAG | HAS_STDDEV_FLAG;
      51             : 
      52             : /************************************************************************/
      53             : /*                           EHdrRasterBand()                           */
      54             : /************************************************************************/
      55             : 
      56         186 : EHdrRasterBand::EHdrRasterBand(GDALDataset *poDSIn, int nBandIn,
      57             :                                VSILFILE *fpRawIn, vsi_l_offset nImgOffsetIn,
      58             :                                int nPixelOffsetIn, int nLineOffsetIn,
      59             :                                GDALDataType eDataTypeIn,
      60             :                                RawRasterBand::ByteOrder eByteOrderIn,
      61         186 :                                int nBitsIn)
      62             :     : RawRasterBand(poDSIn, nBandIn, fpRawIn, nImgOffsetIn, nPixelOffsetIn,
      63             :                     nLineOffsetIn, eDataTypeIn, eByteOrderIn,
      64             :                     RawRasterBand::OwnFP::NO),
      65             :       nBits(nBitsIn), nStartBit(0), nPixelOffsetBits(0), nLineOffsetBits(0),
      66             :       bNoDataSet(FALSE), dfNoData(0.0), dfMin(0.0), dfMax(0.0), dfMean(0.0),
      67         186 :       dfStdDev(0.0), minmaxmeanstddev(0)
      68             : {
      69         186 :     m_bValid = RawRasterBand::IsValid();
      70             : 
      71         186 :     EHdrDataset *poEDS = reinterpret_cast<EHdrDataset *>(poDS);
      72             : 
      73         186 :     if (nBits < 8)
      74             :     {
      75           0 :         const int nSkipBytes = atoi(poEDS->GetKeyValue("SKIPBYTES"));
      76           0 :         if (nSkipBytes < 0 || nSkipBytes > std::numeric_limits<int>::max() / 8)
      77             :         {
      78           0 :             m_bValid = false;
      79           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid SKIPBYTES: %d",
      80             :                      nSkipBytes);
      81           0 :             nStartBit = 0;
      82             :         }
      83             :         else
      84             :         {
      85           0 :             nStartBit = static_cast<vsi_l_offset>(nSkipBytes) * 8;
      86             :         }
      87           0 :         if (nBand >= 2)
      88             :         {
      89             :             GIntBig nBandRowBytes =
      90           0 :                 CPLAtoGIntBig(poEDS->GetKeyValue("BANDROWBYTES"));
      91           0 :             if (nBandRowBytes < 0)
      92             :             {
      93           0 :                 m_bValid = false;
      94           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
      95             :                          "Invalid BANDROWBYTES: " CPL_FRMT_GIB, nBandRowBytes);
      96           0 :                 nBandRowBytes = 0;
      97             :             }
      98           0 :             vsi_l_offset nRowBytes = 0;
      99           0 :             if (nBandRowBytes == 0)
     100           0 :                 nRowBytes =
     101           0 :                     (static_cast<vsi_l_offset>(nBits) * poDS->GetRasterXSize() +
     102             :                      7) /
     103             :                     8;
     104             :             else
     105           0 :                 nRowBytes = static_cast<vsi_l_offset>(nBandRowBytes);
     106             : 
     107           0 :             nStartBit += nRowBytes * (nBand - 1) * 8;
     108             :         }
     109             : 
     110           0 :         nPixelOffsetBits = nBits;
     111             :         GIntBig nTotalRowBytes =
     112           0 :             CPLAtoGIntBig(poEDS->GetKeyValue("TOTALROWBYTES"));
     113           0 :         if (nTotalRowBytes < 0 ||
     114           0 :             nTotalRowBytes > GINTBIG_MAX / 8 / poDS->GetRasterYSize())
     115             :         {
     116           0 :             m_bValid = false;
     117           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     118             :                      "Invalid TOTALROWBYTES: " CPL_FRMT_GIB, nTotalRowBytes);
     119           0 :             nTotalRowBytes = 0;
     120             :         }
     121           0 :         if (nTotalRowBytes > 0)
     122           0 :             nLineOffsetBits = static_cast<vsi_l_offset>(nTotalRowBytes * 8);
     123             :         else
     124           0 :             nLineOffsetBits = static_cast<vsi_l_offset>(nPixelOffsetBits) *
     125           0 :                               poDS->GetRasterXSize();
     126             : 
     127           0 :         nBlockXSize = poDS->GetRasterXSize();
     128           0 :         nBlockYSize = 1;
     129             : 
     130           0 :         SetMetadataItem("NBITS", CPLString().Printf("%d", nBits),
     131             :                         "IMAGE_STRUCTURE");
     132             :     }
     133         186 : }
     134             : 
     135             : /************************************************************************/
     136             : /*                             IReadBlock()                             */
     137             : /************************************************************************/
     138             : 
     139        5350 : CPLErr EHdrRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     140             : 
     141             : {
     142        5350 :     if (nBits >= 8)
     143        5350 :         return RawRasterBand::IReadBlock(nBlockXOff, nBlockYOff, pImage);
     144             : 
     145             :     // Establish desired position.
     146           0 :     const vsi_l_offset nLineStart =
     147           0 :         (nStartBit + nLineOffsetBits * nBlockYOff) / 8;
     148           0 :     int iBitOffset =
     149           0 :         static_cast<int>((nStartBit + nLineOffsetBits * nBlockYOff) % 8);
     150           0 :     const vsi_l_offset nLineEnd =
     151           0 :         (nStartBit + nLineOffsetBits * nBlockYOff +
     152           0 :          static_cast<vsi_l_offset>(nPixelOffsetBits) * nBlockXSize - 1) /
     153             :         8;
     154           0 :     const vsi_l_offset nLineBytesBig = nLineEnd - nLineStart + 1;
     155           0 :     if (nLineBytesBig >
     156           0 :         static_cast<vsi_l_offset>(std::numeric_limits<int>::max()))
     157           0 :         return CE_Failure;
     158           0 :     const unsigned int nLineBytes = static_cast<unsigned int>(nLineBytesBig);
     159             : 
     160             :     // Read data into buffer.
     161           0 :     GByte *pabyBuffer = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nLineBytes));
     162           0 :     if (pabyBuffer == nullptr)
     163           0 :         return CE_Failure;
     164             : 
     165           0 :     if (VSIFSeekL(GetFPL(), nLineStart, SEEK_SET) != 0 ||
     166           0 :         VSIFReadL(pabyBuffer, 1, nLineBytes, GetFPL()) != nLineBytes)
     167             :     {
     168           0 :         CPLError(CE_Failure, CPLE_FileIO,
     169             :                  "Failed to read %u bytes at offset %lu.\n%s", nLineBytes,
     170           0 :                  static_cast<unsigned long>(nLineStart), VSIStrerror(errno));
     171           0 :         CPLFree(pabyBuffer);
     172           0 :         return CE_Failure;
     173             :     }
     174             : 
     175             :     // Copy data, promoting to 8bit.
     176           0 :     for (int iX = 0, iPixel = 0; iX < nBlockXSize; iX++)
     177             :     {
     178           0 :         int nOutWord = 0;
     179             : 
     180           0 :         for (int iBit = 0; iBit < nBits; iBit++)
     181             :         {
     182           0 :             if (pabyBuffer[iBitOffset >> 3] & (0x80 >> (iBitOffset & 7)))
     183           0 :                 nOutWord |= (1 << (nBits - 1 - iBit));
     184           0 :             iBitOffset++;
     185             :         }
     186             : 
     187           0 :         iBitOffset = iBitOffset + nPixelOffsetBits - nBits;
     188             : 
     189           0 :         reinterpret_cast<GByte *>(pImage)[iPixel++] =
     190             :             static_cast<GByte>(nOutWord);
     191             :     }
     192             : 
     193           0 :     CPLFree(pabyBuffer);
     194             : 
     195           0 :     return CE_None;
     196             : }
     197             : 
     198             : /************************************************************************/
     199             : /*                            IWriteBlock()                             */
     200             : /************************************************************************/
     201             : 
     202        3300 : CPLErr EHdrRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     203             : 
     204             : {
     205        3300 :     if (nBits >= 8)
     206        3300 :         return RawRasterBand::IWriteBlock(nBlockXOff, nBlockYOff, pImage);
     207             : 
     208             :     // Establish desired position.
     209           0 :     const vsi_l_offset nLineStart =
     210           0 :         (nStartBit + nLineOffsetBits * nBlockYOff) / 8;
     211           0 :     int iBitOffset =
     212           0 :         static_cast<int>((nStartBit + nLineOffsetBits * nBlockYOff) % 8);
     213           0 :     const vsi_l_offset nLineEnd =
     214           0 :         (nStartBit + nLineOffsetBits * nBlockYOff +
     215           0 :          static_cast<vsi_l_offset>(nPixelOffsetBits) * nBlockXSize - 1) /
     216             :         8;
     217           0 :     const vsi_l_offset nLineBytesBig = nLineEnd - nLineStart + 1;
     218           0 :     if (nLineBytesBig >
     219           0 :         static_cast<vsi_l_offset>(std::numeric_limits<int>::max()))
     220           0 :         return CE_Failure;
     221           0 :     const unsigned int nLineBytes = static_cast<unsigned int>(nLineBytesBig);
     222             : 
     223             :     // Read data into buffer.
     224           0 :     GByte *pabyBuffer = static_cast<GByte *>(VSI_CALLOC_VERBOSE(nLineBytes, 1));
     225           0 :     if (pabyBuffer == nullptr)
     226           0 :         return CE_Failure;
     227             : 
     228           0 :     if (VSIFSeekL(GetFPL(), nLineStart, SEEK_SET) != 0)
     229             :     {
     230           0 :         CPLError(CE_Failure, CPLE_FileIO,
     231             :                  "Failed to read %u bytes at offset %lu.\n%s", nLineBytes,
     232           0 :                  static_cast<unsigned long>(nLineStart), VSIStrerror(errno));
     233           0 :         CPLFree(pabyBuffer);
     234           0 :         return CE_Failure;
     235             :     }
     236             : 
     237           0 :     CPL_IGNORE_RET_VAL(VSIFReadL(pabyBuffer, nLineBytes, 1, GetFPL()));
     238             : 
     239             :     // Copy data, promoting to 8bit.
     240           0 :     for (int iX = 0, iPixel = 0; iX < nBlockXSize; iX++)
     241             :     {
     242           0 :         const int nOutWord = reinterpret_cast<GByte *>(pImage)[iPixel++];
     243             : 
     244           0 :         for (int iBit = 0; iBit < nBits; iBit++)
     245             :         {
     246           0 :             if (nOutWord & (1 << (nBits - 1 - iBit)))
     247           0 :                 pabyBuffer[iBitOffset >> 3] |= (0x80 >> (iBitOffset & 7));
     248             :             else
     249           0 :                 pabyBuffer[iBitOffset >> 3] &= ~((0x80 >> (iBitOffset & 7)));
     250             : 
     251           0 :             iBitOffset++;
     252             :         }
     253             : 
     254           0 :         iBitOffset = iBitOffset + nPixelOffsetBits - nBits;
     255             :     }
     256             : 
     257             :     // Write the data back out.
     258           0 :     if (VSIFSeekL(GetFPL(), nLineStart, SEEK_SET) != 0 ||
     259           0 :         VSIFWriteL(pabyBuffer, 1, nLineBytes, GetFPL()) != nLineBytes)
     260             :     {
     261           0 :         CPLError(CE_Failure, CPLE_FileIO,
     262             :                  "Failed to write %u bytes at offset %lu.\n%s", nLineBytes,
     263           0 :                  static_cast<unsigned long>(nLineStart), VSIStrerror(errno));
     264           0 :         return CE_Failure;
     265             :     }
     266             : 
     267           0 :     CPLFree(pabyBuffer);
     268             : 
     269           0 :     return CE_None;
     270             : }
     271             : 
     272             : /************************************************************************/
     273             : /*                             IRasterIO()                              */
     274             : /************************************************************************/
     275             : 
     276        1742 : CPLErr EHdrRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
     277             :                                  int nXSize, int nYSize, void *pData,
     278             :                                  int nBufXSize, int nBufYSize,
     279             :                                  GDALDataType eBufType, GSpacing nPixelSpace,
     280             :                                  GSpacing nLineSpace,
     281             :                                  GDALRasterIOExtraArg *psExtraArg)
     282             : 
     283             : {
     284             :     // Defer to RawRasterBand
     285        1742 :     if (nBits >= 8)
     286        1742 :         return RawRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
     287             :                                         pData, nBufXSize, nBufYSize, eBufType,
     288        1742 :                                         nPixelSpace, nLineSpace, psExtraArg);
     289             : 
     290             :     // Force use of IReadBlock() and IWriteBlock()
     291           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
     292             :                                      pData, nBufXSize, nBufYSize, eBufType,
     293           0 :                                      nPixelSpace, nLineSpace, psExtraArg);
     294             : }
     295             : 
     296             : /************************************************************************/
     297             : /*                              OSR_GDS()                               */
     298             : /************************************************************************/
     299             : 
     300          18 : static const char *OSR_GDS(char *pszResult, int nResultLen, char **papszNV,
     301             :                            const char *pszField, const char *pszDefaultValue)
     302             : 
     303             : {
     304          18 :     if (papszNV == nullptr || papszNV[0] == nullptr)
     305           0 :         return pszDefaultValue;
     306             : 
     307          18 :     int iLine = 0;  // Used after for.
     308          66 :     for (; papszNV[iLine] != nullptr &&
     309          49 :            !EQUALN(papszNV[iLine], pszField, strlen(pszField));
     310             :          iLine++)
     311             :     {
     312             :     }
     313             : 
     314          18 :     if (papszNV[iLine] == nullptr)
     315          17 :         return pszDefaultValue;
     316             : 
     317           1 :     char **papszTokens = CSLTokenizeString(papszNV[iLine]);
     318             : 
     319           1 :     if (CSLCount(papszTokens) > 1)
     320           1 :         strncpy(pszResult, papszTokens[1], nResultLen - 1);
     321             :     else
     322           0 :         strncpy(pszResult, pszDefaultValue, nResultLen - 1);
     323           1 :     pszResult[nResultLen - 1] = '\0';
     324             : 
     325           1 :     CSLDestroy(papszTokens);
     326           1 :     return pszResult;
     327             : }
     328             : 
     329             : /************************************************************************/
     330             : /* ==================================================================== */
     331             : /*                            EHdrDataset                               */
     332             : /* ==================================================================== */
     333             : /************************************************************************/
     334             : 
     335             : /************************************************************************/
     336             : /*                            EHdrDataset()                             */
     337             : /************************************************************************/
     338             : 
     339         118 : EHdrDataset::EHdrDataset()
     340             :     : fpImage(nullptr), osHeaderExt("hdr"), bGotTransform(false),
     341         118 :       bHDRDirty(false), papszHDR(nullptr), bCLRDirty(false)
     342             : {
     343         118 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     344         118 :     adfGeoTransform[0] = 0.0;
     345         118 :     adfGeoTransform[1] = 1.0;
     346         118 :     adfGeoTransform[2] = 0.0;
     347         118 :     adfGeoTransform[3] = 0.0;
     348         118 :     adfGeoTransform[4] = 0.0;
     349         118 :     adfGeoTransform[5] = 1.0;
     350         118 : }
     351             : 
     352             : /************************************************************************/
     353             : /*                            ~EHdrDataset()                            */
     354             : /************************************************************************/
     355             : 
     356         236 : EHdrDataset::~EHdrDataset()
     357             : 
     358             : {
     359         118 :     EHdrDataset::Close();
     360         236 : }
     361             : 
     362             : /************************************************************************/
     363             : /*                              Close()                                 */
     364             : /************************************************************************/
     365             : 
     366         231 : CPLErr EHdrDataset::Close()
     367             : {
     368         231 :     CPLErr eErr = CE_None;
     369         231 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     370             :     {
     371         118 :         if (EHdrDataset::FlushCache(true) != CE_None)
     372           0 :             eErr = CE_Failure;
     373             : 
     374         118 :         if (nBands > 0 && GetAccess() == GA_Update)
     375             :         {
     376             :             int bNoDataSet;
     377             :             RawRasterBand *poBand =
     378          51 :                 reinterpret_cast<RawRasterBand *>(GetRasterBand(1));
     379             : 
     380          51 :             const double dfNoData = poBand->GetNoDataValue(&bNoDataSet);
     381          51 :             if (bNoDataSet)
     382             :             {
     383           1 :                 ResetKeyValue("NODATA", CPLString().Printf("%.8g", dfNoData));
     384             :             }
     385             : 
     386          51 :             if (bCLRDirty)
     387           4 :                 RewriteCLR(poBand);
     388             : 
     389          51 :             if (bHDRDirty)
     390             :             {
     391          38 :                 if (RewriteHDR() != CE_None)
     392           2 :                     eErr = CE_Failure;
     393             :             }
     394             :         }
     395             : 
     396         118 :         if (fpImage)
     397             :         {
     398         118 :             if (VSIFCloseL(fpImage) != 0)
     399             :             {
     400           0 :                 CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     401           0 :                 eErr = CE_Failure;
     402             :             }
     403             :         }
     404             : 
     405         118 :         CSLDestroy(papszHDR);
     406         118 :         if (GDALPamDataset::Close() != CE_None)
     407           0 :             eErr = CE_Failure;
     408             :     }
     409         231 :     return eErr;
     410             : }
     411             : 
     412             : /************************************************************************/
     413             : /*                            GetKeyValue()                             */
     414             : /************************************************************************/
     415             : 
     416           0 : const char *EHdrDataset::GetKeyValue(const char *pszKey, const char *pszDefault)
     417             : 
     418             : {
     419           0 :     for (int i = 0; papszHDR[i] != nullptr; i++)
     420             :     {
     421           0 :         if (EQUALN(pszKey, papszHDR[i], strlen(pszKey)) &&
     422           0 :             isspace(static_cast<unsigned char>(papszHDR[i][strlen(pszKey)])))
     423             :         {
     424           0 :             const char *pszValue = papszHDR[i] + strlen(pszKey);
     425           0 :             while (isspace(static_cast<unsigned char>(*pszValue)))
     426           0 :                 pszValue++;
     427             : 
     428           0 :             return pszValue;
     429             :         }
     430             :     }
     431             : 
     432           0 :     return pszDefault;
     433             : }
     434             : 
     435             : /************************************************************************/
     436             : /*                           ResetKeyValue()                            */
     437             : /*                                                                      */
     438             : /*      Replace or add the keyword with the indicated value in the      */
     439             : /*      papszHDR list.                                                  */
     440             : /************************************************************************/
     441             : 
     442         149 : void EHdrDataset::ResetKeyValue(const char *pszKey, const char *pszValue)
     443             : 
     444             : {
     445         149 :     if (strlen(pszValue) > 65)
     446             :     {
     447           0 :         CPLAssert(strlen(pszValue) <= 65);
     448           0 :         return;
     449             :     }
     450             : 
     451         149 :     char szNewLine[82] = {'\0'};
     452         149 :     snprintf(szNewLine, sizeof(szNewLine), "%-15s%s", pszKey, pszValue);
     453             : 
     454        1676 :     for (int i = CSLCount(papszHDR) - 1; i >= 0; i--)
     455             :     {
     456        1527 :         if (EQUALN(papszHDR[i], szNewLine, strlen(pszKey) + 1))
     457             :         {
     458           0 :             if (strcmp(papszHDR[i], szNewLine) != 0)
     459             :             {
     460           0 :                 CPLFree(papszHDR[i]);
     461           0 :                 papszHDR[i] = CPLStrdup(szNewLine);
     462           0 :                 bHDRDirty = true;
     463             :             }
     464           0 :             return;
     465             :         }
     466             :     }
     467             : 
     468         149 :     bHDRDirty = true;
     469         149 :     papszHDR = CSLAddString(papszHDR, szNewLine);
     470             : }
     471             : 
     472             : /************************************************************************/
     473             : /*                           RewriteCLR()                               */
     474             : /************************************************************************/
     475             : 
     476           4 : void EHdrDataset::RewriteCLR(GDALRasterBand *poBand) const
     477             : 
     478             : {
     479           4 :     CPLString osCLRFilename = CPLResetExtensionSafe(GetDescription(), "clr");
     480           4 :     GDALColorTable *poTable = poBand->GetColorTable();
     481           4 :     GDALRasterAttributeTable *poRAT = poBand->GetDefaultRAT();
     482           4 :     if (poTable || poRAT)
     483             :     {
     484           3 :         VSILFILE *fp = VSIFOpenL(osCLRFilename, "wt");
     485           3 :         if (fp != nullptr)
     486             :         {
     487             :             // Write RAT in priority if both are defined
     488           3 :             if (poRAT)
     489             :             {
     490          26 :                 for (int iEntry = 0; iEntry < poRAT->GetRowCount(); iEntry++)
     491             :                 {
     492          25 :                     CPLString oLine;
     493             :                     oLine.Printf("%3d %3d %3d %3d\n",
     494          25 :                                  poRAT->GetValueAsInt(iEntry, 0),
     495          25 :                                  poRAT->GetValueAsInt(iEntry, 1),
     496          25 :                                  poRAT->GetValueAsInt(iEntry, 2),
     497          25 :                                  poRAT->GetValueAsInt(iEntry, 3));
     498          50 :                     if (VSIFWriteL(reinterpret_cast<void *>(
     499          25 :                                        const_cast<char *>(oLine.c_str())),
     500          25 :                                    strlen(oLine), 1, fp) != 1)
     501             :                     {
     502           0 :                         CPLError(CE_Failure, CPLE_FileIO,
     503             :                                  "Error while write color table");
     504           0 :                         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     505           0 :                         return;
     506             :                     }
     507             :                 }
     508             :             }
     509             :             else
     510             :             {
     511           8 :                 for (int iColor = 0; iColor < poTable->GetColorEntryCount();
     512             :                      iColor++)
     513             :                 {
     514             :                     GDALColorEntry sEntry;
     515           6 :                     poTable->GetColorEntryAsRGB(iColor, &sEntry);
     516             : 
     517             :                     // I wish we had a way to mark transparency.
     518           6 :                     CPLString oLine;
     519           6 :                     oLine.Printf("%3d %3d %3d %3d\n", iColor, sEntry.c1,
     520           6 :                                  sEntry.c2, sEntry.c3);
     521          12 :                     if (VSIFWriteL(reinterpret_cast<void *>(
     522           6 :                                        const_cast<char *>(oLine.c_str())),
     523           6 :                                    strlen(oLine), 1, fp) != 1)
     524             :                     {
     525           0 :                         CPLError(CE_Failure, CPLE_FileIO,
     526             :                                  "Error while write color table");
     527           0 :                         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     528           0 :                         return;
     529             :                     }
     530             :                 }
     531             :             }
     532           3 :             if (VSIFCloseL(fp) != 0)
     533             :             {
     534           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     535             :                          "Error while write color table");
     536             :             }
     537             :         }
     538             :         else
     539             :         {
     540           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     541             :                      "Unable to create color file %s.", osCLRFilename.c_str());
     542           3 :         }
     543             :     }
     544             :     else
     545             :     {
     546           1 :         VSIUnlink(osCLRFilename);
     547             :     }
     548             : }
     549             : 
     550             : /************************************************************************/
     551             : /*                           SetSpatialRef()                            */
     552             : /************************************************************************/
     553             : 
     554          35 : CPLErr EHdrDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
     555             : 
     556             : {
     557             :     // Reset coordinate system on the dataset.
     558          35 :     m_oSRS.Clear();
     559          35 :     if (poSRS == nullptr)
     560           0 :         return CE_None;
     561             : 
     562          35 :     m_oSRS = *poSRS;
     563             :     // Convert to ESRI WKT.
     564          35 :     char *pszESRI_SRS = nullptr;
     565          35 :     const char *const apszOptions[] = {"FORMAT=WKT1_ESRI", nullptr};
     566          35 :     m_oSRS.exportToWkt(&pszESRI_SRS, apszOptions);
     567             : 
     568          35 :     if (pszESRI_SRS)
     569             :     {
     570             :         // Write to .prj file.
     571             :         CPLString osPrjFilename =
     572          35 :             CPLResetExtensionSafe(GetDescription(), "prj");
     573          35 :         VSILFILE *fp = VSIFOpenL(osPrjFilename.c_str(), "wt");
     574          35 :         if (fp != nullptr)
     575             :         {
     576          35 :             size_t nCount = VSIFWriteL(pszESRI_SRS, strlen(pszESRI_SRS), 1, fp);
     577          35 :             nCount += VSIFWriteL("\n", 1, 1, fp);
     578          35 :             if (VSIFCloseL(fp) != 0 || nCount != 2)
     579             :             {
     580           2 :                 CPLFree(pszESRI_SRS);
     581           2 :                 return CE_Failure;
     582             :             }
     583             :         }
     584             : 
     585          33 :         CPLFree(pszESRI_SRS);
     586             :     }
     587             : 
     588          33 :     return CE_None;
     589             : }
     590             : 
     591             : /************************************************************************/
     592             : /*                          GetGeoTransform()                           */
     593             : /************************************************************************/
     594             : 
     595          29 : CPLErr EHdrDataset::GetGeoTransform(double *padfTransform)
     596             : 
     597             : {
     598          29 :     if (bGotTransform)
     599             :     {
     600          29 :         memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
     601          29 :         return CE_None;
     602             :     }
     603             : 
     604           0 :     return GDALPamDataset::GetGeoTransform(padfTransform);
     605             : }
     606             : 
     607             : /************************************************************************/
     608             : /*                          SetGeoTransform()                           */
     609             : /************************************************************************/
     610             : 
     611          37 : CPLErr EHdrDataset::SetGeoTransform(double *padfGeoTransform)
     612             : 
     613             : {
     614             :     // We only support non-rotated images with info in the .HDR file.
     615          37 :     if (padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0)
     616             :     {
     617           0 :         return GDALPamDataset::SetGeoTransform(padfGeoTransform);
     618             :     }
     619             : 
     620             :     // Record new geotransform.
     621          37 :     bGotTransform = true;
     622          37 :     memcpy(adfGeoTransform, padfGeoTransform, sizeof(double) * 6);
     623             : 
     624             :     // Strip out all old geotransform keywords from HDR records.
     625         361 :     for (int i = CSLCount(papszHDR) - 1; i >= 0; i--)
     626             :     {
     627         324 :         if (STARTS_WITH_CI(papszHDR[i], "ul") ||
     628         324 :             STARTS_WITH_CI(papszHDR[i] + 1, "ll") ||
     629         324 :             STARTS_WITH_CI(papszHDR[i], "cell") ||
     630         324 :             STARTS_WITH_CI(papszHDR[i] + 1, "dim"))
     631             :         {
     632           0 :             papszHDR = CSLRemoveStrings(papszHDR, i, 1, nullptr);
     633             :         }
     634             :     }
     635             : 
     636             :     // Set the transformation information.
     637          37 :     CPLString oValue;
     638             : 
     639          37 :     oValue.Printf("%.15g", adfGeoTransform[0] + adfGeoTransform[1] * 0.5);
     640          37 :     ResetKeyValue("ULXMAP", oValue);
     641             : 
     642          37 :     oValue.Printf("%.15g", adfGeoTransform[3] + adfGeoTransform[5] * 0.5);
     643          37 :     ResetKeyValue("ULYMAP", oValue);
     644             : 
     645          37 :     oValue.Printf("%.15g", adfGeoTransform[1]);
     646          37 :     ResetKeyValue("XDIM", oValue);
     647             : 
     648          37 :     oValue.Printf("%.15g", fabs(adfGeoTransform[5]));
     649          37 :     ResetKeyValue("YDIM", oValue);
     650             : 
     651          37 :     return CE_None;
     652             : }
     653             : 
     654             : /************************************************************************/
     655             : /*                             RewriteHDR()                             */
     656             : /************************************************************************/
     657             : 
     658          38 : CPLErr EHdrDataset::RewriteHDR()
     659             : 
     660             : {
     661          76 :     const CPLString osPath = CPLGetPathSafe(GetDescription());
     662          76 :     const CPLString osName = CPLGetBasenameSafe(GetDescription());
     663             :     const CPLString osHDRFilename =
     664          76 :         CPLFormCIFilenameSafe(osPath, osName, osHeaderExt);
     665             : 
     666             :     // Write .hdr file.
     667          38 :     VSILFILE *fp = VSIFOpenL(osHDRFilename, "wt");
     668             : 
     669          38 :     if (fp == nullptr)
     670             :     {
     671           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Failed to rewrite .hdr file %s.",
     672             :                  osHDRFilename.c_str());
     673           0 :         return CE_Failure;
     674             :     }
     675             : 
     676         512 :     for (int i = 0; papszHDR[i] != nullptr; i++)
     677             :     {
     678         476 :         size_t nCount = VSIFWriteL(papszHDR[i], strlen(papszHDR[i]), 1, fp);
     679         476 :         nCount += VSIFWriteL("\n", 1, 1, fp);
     680         476 :         if (nCount != 2)
     681             :         {
     682           2 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     683           2 :             return CE_Failure;
     684             :         }
     685             :     }
     686             : 
     687          36 :     bHDRDirty = false;
     688             : 
     689          36 :     if (VSIFCloseL(fp) != 0)
     690           0 :         return CE_Failure;
     691             : 
     692          36 :     return CE_None;
     693             : }
     694             : 
     695             : /************************************************************************/
     696             : /*                             RewriteSTX()                             */
     697             : /************************************************************************/
     698             : 
     699           4 : CPLErr EHdrDataset::RewriteSTX() const
     700             : {
     701           8 :     const CPLString osPath = CPLGetPathSafe(GetDescription());
     702           8 :     const CPLString osName = CPLGetBasenameSafe(GetDescription());
     703             :     const CPLString osSTXFilename =
     704           8 :         CPLFormCIFilenameSafe(osPath, osName, "stx");
     705             : 
     706           4 :     VSILFILE *fp = VSIFOpenL(osSTXFilename, "wt");
     707           4 :     if (fp == nullptr)
     708             :     {
     709           0 :         CPLDebug("EHDR", "Failed to rewrite .stx file %s.",
     710             :                  osSTXFilename.c_str());
     711           0 :         return CE_Failure;
     712             :     }
     713             : 
     714           4 :     bool bOK = true;
     715           8 :     for (int i = 0; bOK && i < nBands; ++i)
     716             :     {
     717           4 :         EHdrRasterBand *poBand =
     718           4 :             reinterpret_cast<EHdrRasterBand *>(papoBands[i]);
     719           4 :         bOK &= VSIFPrintfL(fp, "%d %.10f %.10f ", i + 1, poBand->dfMin,
     720           4 :                            poBand->dfMax) >= 0;
     721           4 :         if (poBand->minmaxmeanstddev & HAS_MEAN_FLAG)
     722           4 :             bOK &= VSIFPrintfL(fp, "%.10f ", poBand->dfMean) >= 0;
     723             :         else
     724           0 :             bOK &= VSIFPrintfL(fp, "# ") >= 0;
     725             : 
     726           4 :         if (poBand->minmaxmeanstddev & HAS_STDDEV_FLAG)
     727           4 :             bOK &= VSIFPrintfL(fp, "%.10f\n", poBand->dfStdDev) >= 0;
     728             :         else
     729           0 :             bOK &= VSIFPrintfL(fp, "#\n") >= 0;
     730             :     }
     731             : 
     732           4 :     if (VSIFCloseL(fp) != 0)
     733           0 :         bOK = false;
     734             : 
     735           4 :     return bOK ? CE_None : CE_Failure;
     736             : }
     737             : 
     738             : /************************************************************************/
     739             : /*                              ReadSTX()                               */
     740             : /************************************************************************/
     741             : 
     742         118 : CPLErr EHdrDataset::ReadSTX() const
     743             : {
     744         236 :     const CPLString osPath = CPLGetPathSafe(GetDescription());
     745         236 :     const CPLString osName = CPLGetBasenameSafe(GetDescription());
     746             :     const CPLString osSTXFilename =
     747         236 :         CPLFormCIFilenameSafe(osPath, osName, "stx");
     748             : 
     749         118 :     VSILFILE *fp = VSIFOpenL(osSTXFilename, "rt");
     750         118 :     if (fp == nullptr)
     751         115 :         return CE_None;
     752             : 
     753           3 :     const char *pszLine = nullptr;
     754           6 :     while ((pszLine = CPLReadLineL(fp)) != nullptr)
     755             :     {
     756             :         char **papszTokens =
     757           3 :             CSLTokenizeStringComplex(pszLine, " \t", TRUE, FALSE);
     758           3 :         const int nTokens = CSLCount(papszTokens);
     759           3 :         if (nTokens >= 5)
     760             :         {
     761           3 :             const int i = atoi(papszTokens[0]);
     762           3 :             if (i > 0 && i <= nBands)
     763             :             {
     764           3 :                 EHdrRasterBand *poBand =
     765           3 :                     reinterpret_cast<EHdrRasterBand *>(papoBands[i - 1]);
     766           3 :                 poBand->dfMin = CPLAtof(papszTokens[1]);
     767           3 :                 poBand->dfMax = CPLAtof(papszTokens[2]);
     768             : 
     769           3 :                 int bNoDataSet = FALSE;
     770           3 :                 const double dfNoData = poBand->GetNoDataValue(&bNoDataSet);
     771           3 :                 if (bNoDataSet && dfNoData == poBand->dfMin)
     772             :                 {
     773             :                     // Triggered by
     774             :                     // /vsicurl/http://eros.usgs.gov/archive/nslrsda/GeoTowns/HongKong/srtm/n22e113.zip/n22e113.bil
     775           0 :                     CPLDebug(
     776             :                         "EHDr",
     777             :                         "Ignoring .stx file where min == nodata. "
     778             :                         "The nodata value should not be taken into account "
     779             :                         "in minimum value computation.");
     780           0 :                     CSLDestroy(papszTokens);
     781           0 :                     papszTokens = nullptr;
     782           0 :                     break;
     783             :                 }
     784             : 
     785           3 :                 poBand->minmaxmeanstddev = HAS_MIN_FLAG | HAS_MAX_FLAG;
     786             :                 // Reads optional mean and stddev.
     787           3 :                 if (!EQUAL(papszTokens[3], "#"))
     788             :                 {
     789           3 :                     poBand->dfMean = CPLAtof(papszTokens[3]);
     790           3 :                     poBand->minmaxmeanstddev |= HAS_MEAN_FLAG;
     791             :                 }
     792           3 :                 if (!EQUAL(papszTokens[4], "#"))
     793             :                 {
     794           3 :                     poBand->dfStdDev = CPLAtof(papszTokens[4]);
     795           3 :                     poBand->minmaxmeanstddev |= HAS_STDDEV_FLAG;
     796             :                 }
     797             : 
     798           3 :                 if (nTokens >= 6 && !EQUAL(papszTokens[5], "#"))
     799           0 :                     poBand->SetMetadataItem("STRETCHMIN", papszTokens[5],
     800           0 :                                             "RENDERING_HINTS");
     801             : 
     802           3 :                 if (nTokens >= 7 && !EQUAL(papszTokens[6], "#"))
     803           0 :                     poBand->SetMetadataItem("STRETCHMAX", papszTokens[6],
     804           0 :                                             "RENDERING_HINTS");
     805             :             }
     806             :         }
     807             : 
     808           3 :         CSLDestroy(papszTokens);
     809             :     }
     810             : 
     811           3 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     812             : 
     813           3 :     return CE_None;
     814             : }
     815             : 
     816             : /************************************************************************/
     817             : /*                      GetImageRepFilename()                           */
     818             : /************************************************************************/
     819             : 
     820             : // Check for IMAGE.REP (Spatiocarte Defense 1.0) or name_of_image.rep
     821             : // if it is a GIS-GeoSPOT image.
     822             : // For the specification of SPDF (in French), see
     823             : //   http://eden.ign.fr/download/pub/doc/emabgi/spdf10.pdf/download
     824             : 
     825         102 : CPLString EHdrDataset::GetImageRepFilename(const char *pszFilename)
     826             : {
     827             : 
     828         204 :     const CPLString osPath = CPLGetPathSafe(pszFilename);
     829         204 :     const CPLString osName = CPLGetBasenameSafe(pszFilename);
     830             :     const CPLString osREPFilename =
     831         204 :         CPLFormCIFilenameSafe(osPath, osName, "rep");
     832             : 
     833             :     VSIStatBufL sStatBuf;
     834         102 :     if (VSIStatExL(osREPFilename.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
     835           0 :         return osREPFilename;
     836             : 
     837         204 :     if (EQUAL(CPLGetFilename(pszFilename), "imspatio.bil") ||
     838         102 :         EQUAL(CPLGetFilename(pszFilename), "haspatio.bil"))
     839             :     {
     840             :         CPLString osImageRepFilename(
     841           0 :             CPLFormCIFilenameSafe(osPath, "image", "rep"));
     842           0 :         if (VSIStatExL(osImageRepFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) ==
     843             :             0)
     844           0 :             return osImageRepFilename;
     845             : 
     846             :         // Try in the upper directories if not found in the BIL image directory.
     847           0 :         CPLString dirName(CPLGetDirnameSafe(osPath));
     848           0 :         if (CPLIsFilenameRelative(osPath.c_str()))
     849             :         {
     850           0 :             char *cwd = CPLGetCurrentDir();
     851           0 :             if (cwd)
     852             :             {
     853           0 :                 dirName = CPLFormFilenameSafe(cwd, dirName.c_str(), nullptr);
     854           0 :                 CPLFree(cwd);
     855             :             }
     856             :         }
     857           0 :         while (dirName[0] != 0 && EQUAL(dirName, ".") == FALSE &&
     858           0 :                EQUAL(dirName, "/") == FALSE)
     859             :         {
     860             :             osImageRepFilename =
     861           0 :                 CPLFormCIFilenameSafe(dirName.c_str(), "image", "rep");
     862           0 :             if (VSIStatExL(osImageRepFilename.c_str(), &sStatBuf,
     863           0 :                            VSI_STAT_EXISTS_FLAG) == 0)
     864           0 :                 return osImageRepFilename;
     865             : 
     866             :             // Don't try to recurse above the 'image' subdirectory.
     867           0 :             if (EQUAL(dirName, "image"))
     868             :             {
     869           0 :                 break;
     870             :             }
     871           0 :             dirName = CPLString(CPLGetDirnameSafe(dirName));
     872             :         }
     873             :     }
     874         102 :     return CPLString();
     875             : }
     876             : 
     877             : /************************************************************************/
     878             : /*                            GetFileList()                             */
     879             : /************************************************************************/
     880             : 
     881          22 : char **EHdrDataset::GetFileList()
     882             : 
     883             : {
     884          44 :     const CPLString osPath = CPLGetPathSafe(GetDescription());
     885          44 :     const CPLString osName = CPLGetBasenameSafe(GetDescription());
     886             : 
     887             :     // Main data file, etc.
     888          22 :     char **papszFileList = GDALPamDataset::GetFileList();
     889             : 
     890             :     // Header file.
     891          44 :     CPLString osFilename = CPLFormCIFilenameSafe(osPath, osName, osHeaderExt);
     892          22 :     papszFileList = CSLAddString(papszFileList, osFilename);
     893             : 
     894             :     // Statistics file
     895          22 :     osFilename = CPLFormCIFilenameSafe(osPath, osName, "stx");
     896             :     VSIStatBufL sStatBuf;
     897          22 :     if (VSIStatExL(osFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
     898           2 :         papszFileList = CSLAddString(papszFileList, osFilename);
     899             : 
     900             :     // color table file.
     901          22 :     osFilename = CPLFormCIFilenameSafe(osPath, osName, "clr");
     902          22 :     if (VSIStatExL(osFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
     903           2 :         papszFileList = CSLAddString(papszFileList, osFilename);
     904             : 
     905             :     // projections file.
     906          22 :     osFilename = CPLFormCIFilenameSafe(osPath, osName, "prj");
     907          22 :     if (VSIStatExL(osFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
     908           8 :         papszFileList = CSLAddString(papszFileList, osFilename);
     909             : 
     910          22 :     const CPLString imageRepFilename = GetImageRepFilename(GetDescription());
     911          22 :     if (!imageRepFilename.empty())
     912           0 :         papszFileList = CSLAddString(papszFileList, imageRepFilename.c_str());
     913             : 
     914          44 :     return papszFileList;
     915             : }
     916             : 
     917             : /************************************************************************/
     918             : /*                                Open()                                */
     919             : /************************************************************************/
     920             : 
     921       28139 : GDALDataset *EHdrDataset::Open(GDALOpenInfo *poOpenInfo)
     922             : 
     923             : {
     924       28139 :     return Open(poOpenInfo, true);
     925             : }
     926             : 
     927       28195 : GDALDataset *EHdrDataset::Open(GDALOpenInfo *poOpenInfo, bool bFileSizeCheck)
     928             : 
     929             : {
     930             :     // Assume the caller is pointing to the binary (i.e. .bil) file.
     931       28195 :     if (poOpenInfo->nHeaderBytes < 2 || poOpenInfo->fpL == nullptr)
     932       26904 :         return nullptr;
     933             : 
     934             :     // Tear apart the filename to form a .HDR filename.
     935        2582 :     const CPLString osPath = CPLGetPathSafe(poOpenInfo->pszFilename);
     936        2582 :     const CPLString osName = CPLGetBasenameSafe(poOpenInfo->pszFilename);
     937             : 
     938        1291 :     const char *pszHeaderExt = "hdr";
     939        1291 :     if (poOpenInfo->IsExtensionEqualToCI("SRC") && osName.size() == 7 &&
     940           0 :         (osName[0] == 'e' || osName[0] == 'E' || osName[0] == 'w' ||
     941        1291 :          osName[0] == 'W') &&
     942           0 :         (osName[4] == 'n' || osName[4] == 'N' || osName[4] == 's' ||
     943           0 :          osName[4] == 'S'))
     944             :     {
     945             :         // It is a GTOPO30 or SRTM30 source file, whose header extension is .sch
     946             :         // see http://dds.cr.usgs.gov/srtm/version1/SRTM30/GTOPO30_Documentation
     947           0 :         pszHeaderExt = "sch";
     948             :     }
     949             : 
     950        1291 :     char **papszSiblingFiles = poOpenInfo->GetSiblingFiles();
     951        2582 :     CPLString osHDRFilename;
     952        1291 :     if (papszSiblingFiles)
     953             :     {
     954        1286 :         const int iFile = CSLFindString(
     955             :             papszSiblingFiles,
     956        2572 :             CPLFormFilenameSafe(nullptr, osName, pszHeaderExt).c_str());
     957        1286 :         if (iFile < 0)  // Return if there is no corresponding .hdr file.
     958        1146 :             return nullptr;
     959             : 
     960             :         osHDRFilename =
     961         140 :             CPLFormFilenameSafe(osPath, papszSiblingFiles[iFile], nullptr);
     962             :     }
     963             :     else
     964             :     {
     965           5 :         osHDRFilename = CPLFormCIFilenameSafe(osPath, osName, pszHeaderExt);
     966             :     }
     967             : 
     968         145 :     const bool bSelectedHDR = EQUAL(osHDRFilename, poOpenInfo->pszFilename);
     969             : 
     970             :     // Do we have a .hdr file?
     971         145 :     VSILFILE *fp = VSIFOpenL(osHDRFilename, "r");
     972         145 :     if (fp == nullptr)
     973             :     {
     974           5 :         return nullptr;
     975             :     }
     976             : 
     977             :     // Is this file an ESRI header file?  Read a few lines of text
     978             :     // searching for something starting with nrows or ncols.
     979         140 :     int nRows = -1;
     980         140 :     int nCols = -1;
     981         140 :     int l_nBands = 1;
     982         140 :     int nSkipBytes = 0;
     983         140 :     double dfULXMap = 0.5;
     984         140 :     double dfULYMap = 0.5;
     985         140 :     double dfYLLCorner = -123.456;
     986         140 :     int bCenter = TRUE;
     987         140 :     double dfXDim = 1.0;
     988         140 :     double dfYDim = 1.0;
     989         140 :     double dfNoData = 0.0;
     990         140 :     int nLineCount = 0;
     991         140 :     int bNoDataSet = FALSE;
     992         140 :     GDALDataType eDataType = GDT_Byte;
     993         140 :     int nBits = -1;
     994         140 :     char chByteOrder = 'M';
     995         140 :     char chPixelType = 'N';  // Not defined.
     996         140 :     char szLayout[10] = "BIL";
     997         140 :     char **papszHDR = nullptr;
     998         140 :     int bHasInternalProjection = FALSE;
     999         140 :     int bHasMin = FALSE;
    1000         140 :     int bHasMax = FALSE;
    1001         140 :     double dfMin = 0;
    1002         140 :     double dfMax = 0;
    1003             : 
    1004         140 :     const char *pszLine = nullptr;
    1005        1619 :     while ((pszLine = CPLReadLineL(fp)) != nullptr)
    1006             :     {
    1007        1480 :         nLineCount++;
    1008             : 
    1009        1480 :         if (nLineCount > 50 || strlen(pszLine) > 1000)
    1010             :             break;
    1011             : 
    1012        1479 :         papszHDR = CSLAddString(papszHDR, pszLine);
    1013             : 
    1014             :         char **papszTokens =
    1015        1479 :             CSLTokenizeStringComplex(pszLine, " \t", TRUE, FALSE);
    1016        1479 :         if (CSLCount(papszTokens) < 2)
    1017             :         {
    1018          45 :             CSLDestroy(papszTokens);
    1019          45 :             continue;
    1020             :         }
    1021             : 
    1022        1434 :         if (EQUAL(papszTokens[0], "ncols"))
    1023             :         {
    1024         118 :             nCols = atoi(papszTokens[1]);
    1025             :         }
    1026        1316 :         else if (EQUAL(papszTokens[0], "nrows"))
    1027             :         {
    1028         120 :             nRows = atoi(papszTokens[1]);
    1029             :         }
    1030        1196 :         else if (EQUAL(papszTokens[0], "skipbytes"))
    1031             :         {
    1032           0 :             nSkipBytes = atoi(papszTokens[1]);
    1033             :         }
    1034        1196 :         else if (EQUAL(papszTokens[0], "ulxmap") ||
    1035        1153 :                  EQUAL(papszTokens[0], "xllcorner") ||
    1036        1149 :                  EQUAL(papszTokens[0], "xllcenter"))
    1037             :         {
    1038          47 :             dfULXMap = CPLAtofM(papszTokens[1]);
    1039          47 :             if (EQUAL(papszTokens[0], "xllcorner"))
    1040           4 :                 bCenter = FALSE;
    1041             :         }
    1042        1149 :         else if (EQUAL(papszTokens[0], "ulymap"))
    1043             :         {
    1044          43 :             dfULYMap = CPLAtofM(papszTokens[1]);
    1045             :         }
    1046        1106 :         else if (EQUAL(papszTokens[0], "yllcorner") ||
    1047        1102 :                  EQUAL(papszTokens[0], "yllcenter"))
    1048             :         {
    1049           4 :             dfYLLCorner = CPLAtofM(papszTokens[1]);
    1050           4 :             if (EQUAL(papszTokens[0], "yllcorner"))
    1051           4 :                 bCenter = FALSE;
    1052             :         }
    1053        1102 :         else if (EQUAL(papszTokens[0], "xdim"))
    1054             :         {
    1055          43 :             dfXDim = CPLAtofM(papszTokens[1]);
    1056             :         }
    1057        1059 :         else if (EQUAL(papszTokens[0], "ydim"))
    1058             :         {
    1059          43 :             dfYDim = CPLAtofM(papszTokens[1]);
    1060             :         }
    1061        1016 :         else if (EQUAL(papszTokens[0], "cellsize"))
    1062             :         {
    1063           4 :             dfXDim = CPLAtofM(papszTokens[1]);
    1064           4 :             dfYDim = dfXDim;
    1065             :         }
    1066        1012 :         else if (EQUAL(papszTokens[0], "nbands"))
    1067             :         {
    1068         112 :             l_nBands = atoi(papszTokens[1]);
    1069             :         }
    1070         900 :         else if (EQUAL(papszTokens[0], "layout"))
    1071             :         {
    1072         118 :             snprintf(szLayout, sizeof(szLayout), "%s", papszTokens[1]);
    1073             :         }
    1074         782 :         else if (EQUAL(papszTokens[0], "NODATA_value") ||
    1075         782 :                  EQUAL(papszTokens[0], "NODATA"))
    1076             :         {
    1077           4 :             dfNoData = CPLAtofM(papszTokens[1]);
    1078           4 :             bNoDataSet = TRUE;
    1079             :         }
    1080         778 :         else if (EQUAL(papszTokens[0], "NBITS"))
    1081             :         {
    1082         112 :             nBits = atoi(papszTokens[1]);
    1083             :         }
    1084         666 :         else if (EQUAL(papszTokens[0], "PIXELTYPE"))
    1085             :         {
    1086         108 :             chPixelType = static_cast<char>(
    1087         108 :                 toupper(static_cast<unsigned char>(papszTokens[1][0])));
    1088             :         }
    1089         558 :         else if (EQUAL(papszTokens[0], "byteorder"))
    1090             :         {
    1091         124 :             chByteOrder = static_cast<char>(
    1092         124 :                 toupper(static_cast<unsigned char>(papszTokens[1][0])));
    1093             :         }
    1094             : 
    1095             :         // http://www.worldclim.org/futdown.htm have the projection extensions
    1096         434 :         else if (EQUAL(papszTokens[0], "Projection"))
    1097             :         {
    1098           3 :             bHasInternalProjection = TRUE;
    1099             :         }
    1100         431 :         else if (EQUAL(papszTokens[0], "MinValue") ||
    1101         430 :                  EQUAL(papszTokens[0], "MIN_VALUE"))
    1102             :         {
    1103           1 :             dfMin = CPLAtofM(papszTokens[1]);
    1104           1 :             bHasMin = TRUE;
    1105             :         }
    1106         430 :         else if (EQUAL(papszTokens[0], "MaxValue") ||
    1107         429 :                  EQUAL(papszTokens[0], "MAX_VALUE"))
    1108             :         {
    1109           1 :             dfMax = CPLAtofM(papszTokens[1]);
    1110           1 :             bHasMax = TRUE;
    1111             :         }
    1112             : 
    1113        1434 :         CSLDestroy(papszTokens);
    1114             :     }
    1115             : 
    1116         140 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    1117             : 
    1118             :     // Did we get the required keywords?  If not, return with this never having
    1119             :     // been considered to be a match. This isn't an error!
    1120         140 :     if (nRows == -1 || nCols == -1)
    1121             :     {
    1122          22 :         CSLDestroy(papszHDR);
    1123          22 :         return nullptr;
    1124             :     }
    1125             : 
    1126         236 :     if (!GDALCheckDatasetDimensions(nCols, nRows) ||
    1127         118 :         !GDALCheckBandCount(l_nBands, FALSE))
    1128             :     {
    1129           0 :         CSLDestroy(papszHDR);
    1130           0 :         return nullptr;
    1131             :     }
    1132             : 
    1133             :     // Has the caller selected the .hdr file to open?
    1134         118 :     if (bSelectedHDR)
    1135             :     {
    1136           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1137             :                  "The selected file is an ESRI BIL header file, but to "
    1138             :                  "open ESRI BIL datasets, the data file should be selected "
    1139             :                  "instead of the .hdr file.  Please try again selecting "
    1140             :                  "the data file (often with the extension .bil) corresponding "
    1141             :                  "to the header file: %s",
    1142             :                  poOpenInfo->pszFilename);
    1143           0 :         CSLDestroy(papszHDR);
    1144           0 :         return nullptr;
    1145             :     }
    1146             : 
    1147             :     // If we aren't sure of the file type, check the data file size.  If it is 4
    1148             :     // bytes or more per pixel then we assume it is floating point data.
    1149         118 :     if (nBits == -1 && chPixelType == 'N')
    1150             :     {
    1151             :         VSIStatBufL sStatBuf;
    1152           6 :         if (VSIStatL(poOpenInfo->pszFilename, &sStatBuf) == 0)
    1153             :         {
    1154           6 :             const size_t nBytes = static_cast<size_t>(sStatBuf.st_size / nCols /
    1155           6 :                                                       nRows / l_nBands);
    1156           6 :             if (nBytes > 0 && nBytes != 3)
    1157           2 :                 nBits = static_cast<int>(nBytes * 8);
    1158             : 
    1159           6 :             if (nBytes == 4)
    1160           2 :                 chPixelType = 'F';
    1161             :         }
    1162             :     }
    1163             : 
    1164             :     // If the extension is FLT it is likely a floating point file.
    1165         118 :     if (chPixelType == 'N')
    1166             :     {
    1167           8 :         if (poOpenInfo->IsExtensionEqualToCI("FLT"))
    1168           2 :             chPixelType = 'F';
    1169             :     }
    1170             : 
    1171             :     // If we have a negative nodata value, assume that the
    1172             :     // pixel type is signed. This is necessary for datasets from
    1173             :     // http://www.worldclim.org/futdown.htm
    1174             : 
    1175         118 :     if (bNoDataSet && dfNoData < 0 && chPixelType == 'N')
    1176             :     {
    1177           2 :         chPixelType = 'S';
    1178             :     }
    1179             : 
    1180         236 :     auto poDS = std::make_unique<EHdrDataset>();
    1181             : 
    1182         118 :     poDS->osHeaderExt = pszHeaderExt;
    1183             : 
    1184         118 :     poDS->nRasterXSize = nCols;
    1185         118 :     poDS->nRasterYSize = nRows;
    1186         118 :     poDS->papszHDR = papszHDR;
    1187         118 :     std::swap(poDS->fpImage, poOpenInfo->fpL);
    1188         118 :     poDS->eAccess = poOpenInfo->eAccess;
    1189             : 
    1190             :     // Figure out the data type.
    1191         118 :     if (nBits == 16)
    1192             :     {
    1193          21 :         if (chPixelType == 'S')
    1194          13 :             eDataType = GDT_Int16;
    1195             :         else
    1196           8 :             eDataType = GDT_UInt16;  // Default
    1197             :     }
    1198          97 :     else if (nBits == 32)
    1199             :     {
    1200          34 :         if (chPixelType == 'S')
    1201           8 :             eDataType = GDT_Int32;
    1202          26 :         else if (chPixelType == 'F')
    1203          21 :             eDataType = GDT_Float32;
    1204             :         else
    1205           5 :             eDataType = GDT_UInt32;  // Default
    1206             :     }
    1207          63 :     else if (nBits >= 1 && nBits <= 8)
    1208             :     {
    1209          59 :         if (chPixelType == 'S')
    1210           6 :             eDataType = GDT_Int8;
    1211             :         else
    1212          53 :             eDataType = GDT_Byte;
    1213          59 :         nBits = 8;
    1214             :     }
    1215           4 :     else if (nBits == -1)
    1216             :     {
    1217           4 :         if (chPixelType == 'F')
    1218             :         {
    1219           0 :             eDataType = GDT_Float32;
    1220           0 :             nBits = 32;
    1221             :         }
    1222             :         else
    1223             :         {
    1224           4 :             eDataType = GDT_Byte;
    1225           4 :             nBits = 8;
    1226             :         }
    1227             :     }
    1228             :     else
    1229             :     {
    1230           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1231             :                  "EHdr driver does not support %d NBITS value.", nBits);
    1232           0 :         return nullptr;
    1233             :     }
    1234             : 
    1235             :     // Compute the line offset.
    1236         118 :     const int nItemSize = GDALGetDataTypeSizeBytes(eDataType);
    1237         118 :     CPLAssert(nItemSize != 0);
    1238         118 :     CPLAssert(l_nBands != 0);
    1239             : 
    1240         118 :     int nPixelOffset = 0;
    1241         118 :     int nLineOffset = 0;
    1242         118 :     vsi_l_offset nBandOffset = 0;
    1243             : 
    1244         118 :     if (EQUAL(szLayout, "BIP"))
    1245             :     {
    1246           0 :         if (nCols > std::numeric_limits<int>::max() / (nItemSize * l_nBands))
    1247             :         {
    1248           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Int overflow occurred.");
    1249           0 :             return nullptr;
    1250             :         }
    1251           0 :         nPixelOffset = nItemSize * l_nBands;
    1252           0 :         nLineOffset = nPixelOffset * nCols;
    1253           0 :         nBandOffset = static_cast<vsi_l_offset>(nItemSize);
    1254             :     }
    1255         118 :     else if (EQUAL(szLayout, "BSQ"))
    1256             :     {
    1257           0 :         if (nCols > std::numeric_limits<int>::max() / nItemSize)
    1258             :         {
    1259           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Int overflow occurred.");
    1260           0 :             return nullptr;
    1261             :         }
    1262           0 :         nPixelOffset = nItemSize;
    1263           0 :         nLineOffset = nPixelOffset * nCols;
    1264           0 :         nBandOffset = static_cast<vsi_l_offset>(nLineOffset) * nRows;
    1265             :     }
    1266             :     else
    1267             :     {
    1268             :         // Assume BIL.
    1269         118 :         if (nCols > std::numeric_limits<int>::max() / (nItemSize * l_nBands))
    1270             :         {
    1271           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Int overflow occurred.");
    1272           0 :             return nullptr;
    1273             :         }
    1274         118 :         nPixelOffset = nItemSize;
    1275         118 :         nLineOffset = nItemSize * l_nBands * nCols;
    1276         118 :         nBandOffset = static_cast<vsi_l_offset>(nItemSize) * nCols;
    1277             :     }
    1278             : 
    1279         189 :     if (nBits >= 8 && bFileSizeCheck &&
    1280          71 :         !RAWDatasetCheckMemoryUsage(
    1281          71 :             poDS->nRasterXSize, poDS->nRasterYSize, l_nBands, nItemSize,
    1282          71 :             nPixelOffset, nLineOffset, nSkipBytes, nBandOffset, poDS->fpImage))
    1283             :     {
    1284           0 :         return nullptr;
    1285             :     }
    1286             : 
    1287         118 :     poDS->SetDescription(poOpenInfo->pszFilename);
    1288         118 :     poDS->PamInitialize();
    1289             : 
    1290             :     // Create band information objects.
    1291         304 :     for (int i = 0; i < l_nBands; i++)
    1292             :     {
    1293             :         auto poBand = std::make_unique<EHdrRasterBand>(
    1294         186 :             poDS.get(), i + 1, poDS->fpImage, nSkipBytes + nBandOffset * i,
    1295             :             nPixelOffset, nLineOffset, eDataType,
    1296             :             chByteOrder == 'I' || chByteOrder == 'L'
    1297         186 :                 ? RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN
    1298             :                 : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
    1299         186 :             nBits);
    1300         186 :         if (!poBand->IsValid())
    1301           0 :             return nullptr;
    1302             : 
    1303         186 :         poBand->bNoDataSet = bNoDataSet;
    1304         186 :         poBand->dfNoData = dfNoData;
    1305             : 
    1306         186 :         if (bHasMin && bHasMax)
    1307             :         {
    1308           1 :             poBand->dfMin = dfMin;
    1309           1 :             poBand->dfMax = dfMax;
    1310           1 :             poBand->minmaxmeanstddev = HAS_MIN_FLAG | HAS_MAX_FLAG;
    1311             :         }
    1312             : 
    1313         186 :         poDS->SetBand(i + 1, std::move(poBand));
    1314             :     }
    1315             : 
    1316             :     // If we didn't get bounds in the .hdr, look for a worldfile.
    1317         118 :     if (dfYLLCorner != -123.456)
    1318             :     {
    1319           4 :         if (bCenter)
    1320           0 :             dfULYMap = dfYLLCorner + (nRows - 1) * dfYDim;
    1321             :         else
    1322           4 :             dfULYMap = dfYLLCorner + nRows * dfYDim;
    1323             :     }
    1324             : 
    1325         118 :     if (dfULXMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0)
    1326             :     {
    1327          47 :         poDS->bGotTransform = true;
    1328             : 
    1329          47 :         if (bCenter)
    1330             :         {
    1331          43 :             poDS->adfGeoTransform[0] = dfULXMap - dfXDim * 0.5;
    1332          43 :             poDS->adfGeoTransform[1] = dfXDim;
    1333          43 :             poDS->adfGeoTransform[2] = 0.0;
    1334          43 :             poDS->adfGeoTransform[3] = dfULYMap + dfYDim * 0.5;
    1335          43 :             poDS->adfGeoTransform[4] = 0.0;
    1336          43 :             poDS->adfGeoTransform[5] = -dfYDim;
    1337             :         }
    1338             :         else
    1339             :         {
    1340           4 :             poDS->adfGeoTransform[0] = dfULXMap;
    1341           4 :             poDS->adfGeoTransform[1] = dfXDim;
    1342           4 :             poDS->adfGeoTransform[2] = 0.0;
    1343           4 :             poDS->adfGeoTransform[3] = dfULYMap;
    1344           4 :             poDS->adfGeoTransform[4] = 0.0;
    1345           4 :             poDS->adfGeoTransform[5] = -dfYDim;
    1346             :         }
    1347             :     }
    1348             : 
    1349         118 :     if (!poDS->bGotTransform)
    1350          71 :         poDS->bGotTransform = CPL_TO_BOOL(GDALReadWorldFile(
    1351          71 :             poOpenInfo->pszFilename, nullptr, poDS->adfGeoTransform));
    1352             : 
    1353         118 :     if (!poDS->bGotTransform)
    1354          71 :         poDS->bGotTransform = CPL_TO_BOOL(GDALReadWorldFile(
    1355          71 :             poOpenInfo->pszFilename, "wld", poDS->adfGeoTransform));
    1356             : 
    1357             :     // Check for a .prj file.
    1358         236 :     std::string osPrjFilename = CPLFormCIFilenameSafe(osPath, osName, "prj");
    1359             : 
    1360         118 :     fp = VSIFOpenL(osPrjFilename.c_str(), "r");
    1361             : 
    1362             :     // .hdr files from http://www.worldclim.org/futdown.htm have the projection
    1363             :     // info in the .hdr file itself.
    1364         118 :     if (fp == nullptr && bHasInternalProjection)
    1365             :     {
    1366           1 :         osPrjFilename = osHDRFilename;
    1367           1 :         fp = VSIFOpenL(osPrjFilename.c_str(), "r");
    1368             :     }
    1369             : 
    1370         118 :     if (fp != nullptr)
    1371             :     {
    1372          38 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    1373          38 :         fp = nullptr;
    1374             : 
    1375          38 :         char **papszLines = CSLLoad(osPrjFilename.c_str());
    1376             : 
    1377          38 :         if (poDS->m_oSRS.importFromESRI(papszLines) == OGRERR_NONE)
    1378             :         {
    1379             :             // If geographic values are in seconds, we must transform.
    1380             :             // Is there a code for minutes too?
    1381          36 :             char szResult[80] = {'\0'};
    1382          54 :             if (poDS->m_oSRS.IsGeographic() &&
    1383          18 :                 EQUAL(OSR_GDS(szResult, sizeof(szResult), papszLines, "Units",
    1384             :                               ""),
    1385             :                       "DS"))
    1386             :             {
    1387           0 :                 poDS->adfGeoTransform[0] /= 3600.0;
    1388           0 :                 poDS->adfGeoTransform[1] /= 3600.0;
    1389           0 :                 poDS->adfGeoTransform[2] /= 3600.0;
    1390           0 :                 poDS->adfGeoTransform[3] /= 3600.0;
    1391           0 :                 poDS->adfGeoTransform[4] /= 3600.0;
    1392           0 :                 poDS->adfGeoTransform[5] /= 3600.0;
    1393             :             }
    1394             :         }
    1395             :         else
    1396             :         {
    1397           2 :             poDS->m_oSRS.Clear();
    1398             :         }
    1399             : 
    1400          38 :         CSLDestroy(papszLines);
    1401             :     }
    1402             :     else
    1403             :     {
    1404             :         // Check for IMAGE.REP (Spatiocarte Defense 1.0) or name_of_image.rep
    1405             :         // if it is a GIS-GeoSPOT image
    1406             :         // For the specification of SPDF (in French), see
    1407             :         //   http://eden.ign.fr/download/pub/doc/emabgi/spdf10.pdf/download
    1408             :         const CPLString szImageRepFilename =
    1409         160 :             GetImageRepFilename(poOpenInfo->pszFilename);
    1410          80 :         if (!szImageRepFilename.empty())
    1411             :         {
    1412           0 :             fp = VSIFOpenL(szImageRepFilename.c_str(), "r");
    1413             :         }
    1414          80 :         if (fp != nullptr)
    1415             :         {
    1416           0 :             bool bUTM = false;
    1417           0 :             bool bWGS84 = false;
    1418           0 :             int bNorth = FALSE;
    1419           0 :             bool bSouth = false;
    1420           0 :             int utmZone = 0;
    1421             : 
    1422           0 :             while ((pszLine = CPLReadLineL(fp)) != nullptr)
    1423             :             {
    1424           0 :                 if (STARTS_WITH(pszLine, "PROJ_ID") && strstr(pszLine, "UTM"))
    1425             :                 {
    1426           0 :                     bUTM = true;
    1427             :                 }
    1428           0 :                 else if (STARTS_WITH(pszLine, "PROJ_ZONE"))
    1429             :                 {
    1430           0 :                     const char *c = strchr(pszLine, '"');
    1431           0 :                     if (c)
    1432             :                     {
    1433           0 :                         c++;
    1434           0 :                         if (*c >= '0' && *c <= '9')
    1435             :                         {
    1436           0 :                             utmZone = atoi(c);
    1437           0 :                             if (utmZone >= 1 && utmZone <= 60)
    1438             :                             {
    1439           0 :                                 if (strstr(pszLine, "Nord") ||
    1440           0 :                                     strstr(pszLine, "NORD"))
    1441             :                                 {
    1442           0 :                                     bNorth = TRUE;
    1443             :                                 }
    1444           0 :                                 else if (strstr(pszLine, "Sud") ||
    1445           0 :                                          strstr(pszLine, "SUD"))
    1446             :                                 {
    1447           0 :                                     bSouth = true;
    1448             :                                 }
    1449             :                             }
    1450             :                         }
    1451             :                     }
    1452             :                 }
    1453           0 :                 else if (STARTS_WITH(pszLine, "PROJ_CODE") &&
    1454           0 :                          strstr(pszLine, "FR-MINDEF"))
    1455             :                 {
    1456           0 :                     const char *c = strchr(pszLine, 'A');
    1457           0 :                     if (c)
    1458             :                     {
    1459           0 :                         c++;
    1460           0 :                         if (*c >= '0' && *c <= '9')
    1461             :                         {
    1462           0 :                             utmZone = atoi(c);
    1463           0 :                             if (utmZone >= 1 && utmZone <= 60)
    1464             :                             {
    1465           0 :                                 if (c[1] == 'N' ||
    1466           0 :                                     (c[1] != '\0' && c[2] == 'N'))
    1467             :                                 {
    1468           0 :                                     bNorth = TRUE;
    1469             :                                 }
    1470           0 :                                 else if (c[1] == 'S' ||
    1471           0 :                                          (c[1] != '\0' && c[2] == 'S'))
    1472             :                                 {
    1473           0 :                                     bSouth = true;
    1474             :                                 }
    1475             :                             }
    1476             :                         }
    1477           0 :                     }
    1478             :                 }
    1479           0 :                 else if (STARTS_WITH(pszLine, "HORIZ_DATUM") &&
    1480           0 :                          (strstr(pszLine, "WGS 84") ||
    1481           0 :                           strstr(pszLine, "WGS84")))
    1482             :                 {
    1483           0 :                     bWGS84 = true;
    1484             :                 }
    1485           0 :                 else if (STARTS_WITH(pszLine, "MAP_NUMBER"))
    1486             :                 {
    1487           0 :                     const char *c = strchr(pszLine, '"');
    1488           0 :                     if (c)
    1489             :                     {
    1490           0 :                         char *pszMapNumber = CPLStrdup(c + 1);
    1491           0 :                         char *c2 = strchr(pszMapNumber, '"');
    1492           0 :                         if (c2)
    1493           0 :                             *c2 = 0;
    1494           0 :                         poDS->SetMetadataItem("SPDF_MAP_NUMBER", pszMapNumber);
    1495           0 :                         CPLFree(pszMapNumber);
    1496             :                     }
    1497             :                 }
    1498           0 :                 else if (STARTS_WITH(pszLine, "PRODUCTION_DATE"))
    1499             :                 {
    1500           0 :                     const char *c = pszLine + strlen("PRODUCTION_DATE");
    1501           0 :                     while (*c == ' ')
    1502           0 :                         c++;
    1503           0 :                     if (*c)
    1504             :                     {
    1505           0 :                         poDS->SetMetadataItem("SPDF_PRODUCTION_DATE", c);
    1506             :                     }
    1507             :                 }
    1508             :             }
    1509             : 
    1510           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    1511             : 
    1512           0 :             if (utmZone >= 1 && utmZone <= 60 && bUTM && bWGS84 &&
    1513           0 :                 (bNorth || bSouth))
    1514             :             {
    1515           0 :                 char projCSStr[64] = {'\0'};
    1516           0 :                 snprintf(projCSStr, sizeof(projCSStr), "WGS 84 / UTM zone %d%c",
    1517             :                          utmZone, (bNorth) ? 'N' : 'S');
    1518             : 
    1519           0 :                 poDS->m_oSRS.SetProjCS(projCSStr);
    1520           0 :                 poDS->m_oSRS.SetWellKnownGeogCS("WGS84");
    1521           0 :                 poDS->m_oSRS.SetUTM(utmZone, bNorth);
    1522           0 :                 poDS->m_oSRS.SetAuthority("PROJCS", "EPSG",
    1523           0 :                                           (bNorth ? 32600 : 32700) + utmZone);
    1524           0 :                 poDS->m_oSRS.AutoIdentifyEPSG();
    1525             :             }
    1526             :             else
    1527             :             {
    1528           0 :                 CPLError(CE_Warning, CPLE_NotSupported,
    1529             :                          "Cannot retrieve projection from IMAGE.REP");
    1530             :             }
    1531             :         }
    1532             :     }
    1533             : 
    1534             :     // Check for a color table.
    1535             :     const std::string osCLRFilename =
    1536         236 :         CPLFormCIFilenameSafe(osPath, osName, "clr");
    1537             : 
    1538             :     // Only read the .clr for byte, int16 or uint16 bands.
    1539         118 :     if (nItemSize <= 2)
    1540          84 :         fp = VSIFOpenL(osCLRFilename.c_str(), "r");
    1541             :     else
    1542          34 :         fp = nullptr;
    1543             : 
    1544         118 :     if (fp != nullptr)
    1545             :     {
    1546             :         std::shared_ptr<GDALRasterAttributeTable> poRat(
    1547           7 :             new GDALDefaultRasterAttributeTable());
    1548           7 :         poRat->CreateColumn("Value", GFT_Integer, GFU_Generic);
    1549           7 :         poRat->CreateColumn("Red", GFT_Integer, GFU_Red);
    1550           7 :         poRat->CreateColumn("Green", GFT_Integer, GFU_Green);
    1551           7 :         poRat->CreateColumn("Blue", GFT_Integer, GFU_Blue);
    1552             : 
    1553           7 :         poDS->m_poColorTable.reset(new GDALColorTable());
    1554             : 
    1555           7 :         bool bHasFoundNonCTValues = false;
    1556           7 :         int nRatRow = 0;
    1557             : 
    1558             :         while (true)
    1559             :         {
    1560          94 :             pszLine = CPLReadLineL(fp);
    1561          94 :             if (!pszLine)
    1562           7 :                 break;
    1563             : 
    1564          87 :             if (*pszLine == '#' || *pszLine == '!')
    1565           0 :                 continue;
    1566             : 
    1567             :             char **papszValues =
    1568          87 :                 CSLTokenizeString2(pszLine, "\t ", CSLT_HONOURSTRINGS);
    1569             : 
    1570          87 :             if (CSLCount(papszValues) >= 4)
    1571             :             {
    1572          87 :                 const int nIndex = atoi(papszValues[0]);
    1573          87 :                 poRat->SetValue(nRatRow, 0, nIndex);
    1574          87 :                 poRat->SetValue(nRatRow, 1, atoi(papszValues[1]));
    1575          87 :                 poRat->SetValue(nRatRow, 2, atoi(papszValues[2]));
    1576          87 :                 poRat->SetValue(nRatRow, 3, atoi(papszValues[3]));
    1577          87 :                 nRatRow++;
    1578             : 
    1579          87 :                 if (nIndex >= 0 && nIndex < 65536)
    1580             :                 {
    1581          72 :                     const GDALColorEntry oEntry = {
    1582          72 :                         static_cast<short>(atoi(papszValues[1])),  // Red
    1583          72 :                         static_cast<short>(atoi(papszValues[2])),  // Green
    1584          72 :                         static_cast<short>(atoi(papszValues[3])),  // Blue
    1585          72 :                         255};
    1586             : 
    1587          72 :                     poDS->m_poColorTable->SetColorEntry(nIndex, &oEntry);
    1588             :                 }
    1589             :                 else
    1590             :                 {
    1591             :                     // Negative values are valid. At least we can find use of
    1592             :                     // them here:
    1593             :                     //   http://www.ngdc.noaa.gov/mgg/topo/elev/esri/clr/
    1594             :                     // But, there's no way of representing them with GDAL color
    1595             :                     // table model.
    1596          15 :                     if (!bHasFoundNonCTValues)
    1597           3 :                         CPLDebug("EHdr", "Ignoring color index : %d", nIndex);
    1598          15 :                     bHasFoundNonCTValues = true;
    1599             :                 }
    1600             :             }
    1601             : 
    1602          87 :             CSLDestroy(papszValues);
    1603          87 :         }
    1604             : 
    1605           7 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    1606             : 
    1607           7 :         if (bHasFoundNonCTValues)
    1608             :         {
    1609           3 :             poDS->m_poRAT.swap(poRat);
    1610             :         }
    1611             : 
    1612          14 :         for (int i = 1; i <= poDS->nBands; i++)
    1613             :         {
    1614             :             EHdrRasterBand *poBand =
    1615           7 :                 cpl::down_cast<EHdrRasterBand *>(poDS->GetRasterBand(i));
    1616           7 :             poBand->m_poColorTable = poDS->m_poColorTable;
    1617           7 :             poBand->m_poRAT = poDS->m_poRAT;
    1618           7 :             poBand->SetColorInterpretation(GCI_PaletteIndex);
    1619             :         }
    1620             : 
    1621           7 :         poDS->bCLRDirty = false;
    1622             :     }
    1623             : 
    1624             :     // Read statistics (.STX).
    1625         118 :     poDS->ReadSTX();
    1626             : 
    1627             :     // Initialize any PAM information.
    1628         118 :     poDS->TryLoadXML();
    1629             : 
    1630             :     // Check for overviews.
    1631         118 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
    1632             : 
    1633         118 :     return poDS.release();
    1634             : }
    1635             : 
    1636             : /************************************************************************/
    1637             : /*                               Create()                               */
    1638             : /************************************************************************/
    1639             : 
    1640          78 : GDALDataset *EHdrDataset::Create(const char *pszFilename, int nXSize,
    1641             :                                  int nYSize, int nBandsIn, GDALDataType eType,
    1642             :                                  char **papszParamList)
    1643             : 
    1644             : {
    1645             :     // Verify input options.
    1646          78 :     if (nBandsIn <= 0)
    1647             :     {
    1648           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1649             :                  "EHdr driver does not support %d bands.", nBandsIn);
    1650           1 :         return nullptr;
    1651             :     }
    1652             : 
    1653          77 :     if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_Float32 &&
    1654          30 :         eType != GDT_UInt16 && eType != GDT_Int16 && eType != GDT_Int32 &&
    1655             :         eType != GDT_UInt32)
    1656             :     {
    1657          19 :         CPLError(CE_Failure, CPLE_AppDefined,
    1658             :                  "Attempt to create ESRI .hdr labelled dataset with an illegal"
    1659             :                  "data type (%s).",
    1660             :                  GDALGetDataTypeName(eType));
    1661             : 
    1662          19 :         return nullptr;
    1663             :     }
    1664             : 
    1665             :     // Try to create the file.
    1666          58 :     VSILFILE *fp = VSIFOpenL(pszFilename, "wb");
    1667             : 
    1668          58 :     if (fp == nullptr)
    1669             :     {
    1670           3 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1671             :                  "Attempt to create file `%s' failed.", pszFilename);
    1672           3 :         return nullptr;
    1673             :     }
    1674             : 
    1675             :     // Just write out a couple of bytes to establish the binary
    1676             :     // file, and then close it.
    1677          55 :     bool bOK = VSIFWriteL(reinterpret_cast<void *>(const_cast<char *>("\0\0")),
    1678          55 :                           2, 1, fp) == 1;
    1679          55 :     if (VSIFCloseL(fp) != 0)
    1680           0 :         bOK = false;
    1681          55 :     fp = nullptr;
    1682          55 :     if (!bOK)
    1683           1 :         return nullptr;
    1684             : 
    1685             :     // Create the hdr filename.
    1686             :     char *const pszHdrFilename =
    1687          54 :         CPLStrdup(CPLResetExtensionSafe(pszFilename, "hdr").c_str());
    1688             : 
    1689             :     // Open the file.
    1690          54 :     fp = VSIFOpenL(pszHdrFilename, "wt");
    1691          54 :     if (fp == nullptr)
    1692             :     {
    1693           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1694             :                  "Attempt to create file `%s' failed.", pszHdrFilename);
    1695           0 :         CPLFree(pszHdrFilename);
    1696           0 :         return nullptr;
    1697             :     }
    1698             : 
    1699             :     // Decide how many bits the file should have.
    1700          54 :     int nBits = GDALGetDataTypeSize(eType);
    1701             : 
    1702          54 :     if (CSLFetchNameValue(papszParamList, "NBITS") != nullptr)
    1703           1 :         nBits = atoi(CSLFetchNameValue(papszParamList, "NBITS"));
    1704             : 
    1705          54 :     const int nRowBytes = (nBits * nXSize + 7) / 8;
    1706             : 
    1707             :     // Check for signed byte.
    1708          54 :     const char *pszPixelType = CSLFetchNameValue(papszParamList, "PIXELTYPE");
    1709          54 :     if (pszPixelType == nullptr)
    1710          53 :         pszPixelType = "";
    1711             : 
    1712             :     // Write out the raw definition for the dataset as a whole.
    1713          54 :     bOK &= VSIFPrintfL(fp, "BYTEORDER      I\n") >= 0;
    1714          54 :     bOK &= VSIFPrintfL(fp, "LAYOUT         BIL\n") >= 0;
    1715          54 :     bOK &= VSIFPrintfL(fp, "NROWS          %d\n", nYSize) >= 0;
    1716          54 :     bOK &= VSIFPrintfL(fp, "NCOLS          %d\n", nXSize) >= 0;
    1717          54 :     bOK &= VSIFPrintfL(fp, "NBANDS         %d\n", nBandsIn) >= 0;
    1718          54 :     bOK &= VSIFPrintfL(fp, "NBITS          %d\n", nBits) >= 0;
    1719          54 :     bOK &= VSIFPrintfL(fp, "BANDROWBYTES   %d\n", nRowBytes) >= 0;
    1720          54 :     bOK &= VSIFPrintfL(fp, "TOTALROWBYTES  %d\n", nRowBytes * nBandsIn) >= 0;
    1721             : 
    1722          54 :     if (eType == GDT_Float32)
    1723           5 :         bOK &= VSIFPrintfL(fp, "PIXELTYPE      FLOAT\n") >= 0;
    1724          49 :     else if (eType == GDT_Int8 || eType == GDT_Int16 || eType == GDT_Int32)
    1725          10 :         bOK &= VSIFPrintfL(fp, "PIXELTYPE      SIGNEDINT\n") >= 0;
    1726          39 :     else if (eType == GDT_Byte && EQUAL(pszPixelType, "SIGNEDBYTE"))
    1727           1 :         bOK &= VSIFPrintfL(fp, "PIXELTYPE      SIGNEDINT\n") >= 0;
    1728             :     else
    1729          38 :         bOK &= VSIFPrintfL(fp, "PIXELTYPE      UNSIGNEDINT\n") >= 0;
    1730             : 
    1731          54 :     if (VSIFCloseL(fp) != 0)
    1732           0 :         bOK = false;
    1733             : 
    1734          54 :     CPLFree(pszHdrFilename);
    1735             : 
    1736          54 :     if (!bOK)
    1737           0 :         return nullptr;
    1738             : 
    1739         108 :     GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
    1740          54 :     return Open(&oOpenInfo, false);
    1741             : }
    1742             : 
    1743             : /************************************************************************/
    1744             : /*                             CreateCopy()                             */
    1745             : /************************************************************************/
    1746             : 
    1747          39 : GDALDataset *EHdrDataset::CreateCopy(const char *pszFilename,
    1748             :                                      GDALDataset *poSrcDS, int bStrict,
    1749             :                                      char **papszOptions,
    1750             :                                      GDALProgressFunc pfnProgress,
    1751             :                                      void *pProgressData)
    1752             : 
    1753             : {
    1754          39 :     const int nBands = poSrcDS->GetRasterCount();
    1755          39 :     if (nBands == 0)
    1756             :     {
    1757           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1758             :                  "EHdr driver does not support source dataset without any "
    1759             :                  "bands.");
    1760           1 :         return nullptr;
    1761             :     }
    1762             : 
    1763          38 :     char **papszAdjustedOptions = CSLDuplicate(papszOptions);
    1764             : 
    1765             :     // Ensure we pass on NBITS and PIXELTYPE structure information.
    1766          38 :     auto poSrcBand = poSrcDS->GetRasterBand(1);
    1767          38 :     if (poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE") != nullptr &&
    1768           0 :         CSLFetchNameValue(papszOptions, "NBITS") == nullptr)
    1769             :     {
    1770           0 :         papszAdjustedOptions = CSLSetNameValue(
    1771             :             papszAdjustedOptions, "NBITS",
    1772           0 :             poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE"));
    1773             :     }
    1774             : 
    1775          62 :     if (poSrcBand->GetRasterDataType() == GDT_Byte &&
    1776          24 :         CSLFetchNameValue(papszOptions, "PIXELTYPE") == nullptr)
    1777             :     {
    1778          24 :         poSrcBand->EnablePixelTypeSignedByteWarning(false);
    1779             :         const char *pszPixelType =
    1780          24 :             poSrcBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
    1781          24 :         poSrcBand->EnablePixelTypeSignedByteWarning(true);
    1782          24 :         if (pszPixelType != nullptr)
    1783             :         {
    1784           1 :             papszAdjustedOptions = CSLSetNameValue(papszAdjustedOptions,
    1785             :                                                    "PIXELTYPE", pszPixelType);
    1786             :         }
    1787             :     }
    1788             : 
    1789             :     // Proceed with normal copying using the default createcopy  operators.
    1790             :     GDALDriver *poDriver =
    1791          38 :         reinterpret_cast<GDALDriver *>(GDALGetDriverByName("EHdr"));
    1792             : 
    1793          38 :     GDALDataset *poOutDS = poDriver->DefaultCreateCopy(
    1794             :         pszFilename, poSrcDS, bStrict, papszAdjustedOptions, pfnProgress,
    1795             :         pProgressData);
    1796          38 :     CSLDestroy(papszAdjustedOptions);
    1797             : 
    1798          38 :     if (poOutDS != nullptr)
    1799          20 :         poOutDS->FlushCache(false);
    1800             : 
    1801          38 :     return poOutDS;
    1802             : }
    1803             : 
    1804             : /************************************************************************/
    1805             : /*                        GetNoDataValue()                              */
    1806             : /************************************************************************/
    1807             : 
    1808         100 : double EHdrRasterBand::GetNoDataValue(int *pbSuccess)
    1809             : {
    1810         100 :     if (pbSuccess)
    1811         100 :         *pbSuccess = bNoDataSet;
    1812             : 
    1813         100 :     if (bNoDataSet)
    1814           1 :         return dfNoData;
    1815             : 
    1816          99 :     return RawRasterBand::GetNoDataValue(pbSuccess);
    1817             : }
    1818             : 
    1819             : /************************************************************************/
    1820             : /*                           GetMinimum()                               */
    1821             : /************************************************************************/
    1822             : 
    1823           3 : double EHdrRasterBand::GetMinimum(int *pbSuccess)
    1824             : {
    1825           3 :     if (pbSuccess != nullptr)
    1826           3 :         *pbSuccess = (minmaxmeanstddev & HAS_MIN_FLAG) != 0;
    1827             : 
    1828           3 :     if (minmaxmeanstddev & HAS_MIN_FLAG)
    1829           2 :         return dfMin;
    1830             : 
    1831           1 :     return RawRasterBand::GetMinimum(pbSuccess);
    1832             : }
    1833             : 
    1834             : /************************************************************************/
    1835             : /*                           GetMaximum()                               */
    1836             : /************************************************************************/
    1837             : 
    1838           2 : double EHdrRasterBand::GetMaximum(int *pbSuccess)
    1839             : {
    1840           2 :     if (pbSuccess != nullptr)
    1841           2 :         *pbSuccess = (minmaxmeanstddev & HAS_MAX_FLAG) != 0;
    1842             : 
    1843           2 :     if (minmaxmeanstddev & HAS_MAX_FLAG)
    1844           1 :         return dfMax;
    1845             : 
    1846           1 :     return RawRasterBand::GetMaximum(pbSuccess);
    1847             : }
    1848             : 
    1849             : /************************************************************************/
    1850             : /*                           GetStatistics()                            */
    1851             : /************************************************************************/
    1852             : 
    1853           6 : CPLErr EHdrRasterBand::GetStatistics(int bApproxOK, int bForce, double *pdfMin,
    1854             :                                      double *pdfMax, double *pdfMean,
    1855             :                                      double *pdfStdDev)
    1856             : {
    1857           6 :     if (!(GetMetadataItem("STATISTICS_APPROXIMATE") && !bApproxOK))
    1858             :     {
    1859           5 :         if ((minmaxmeanstddev & HAS_ALL_FLAGS) == HAS_ALL_FLAGS)
    1860             :         {
    1861           1 :             if (pdfMin)
    1862           1 :                 *pdfMin = dfMin;
    1863           1 :             if (pdfMax)
    1864           1 :                 *pdfMax = dfMax;
    1865           1 :             if (pdfMean)
    1866           1 :                 *pdfMean = dfMean;
    1867           1 :             if (pdfStdDev)
    1868           1 :                 *pdfStdDev = dfStdDev;
    1869           1 :             return CE_None;
    1870             :         }
    1871             :     }
    1872             : 
    1873           5 :     const CPLErr eErr = RawRasterBand::GetStatistics(
    1874             :         bApproxOK, bForce, &dfMin, &dfMax, &dfMean, &dfStdDev);
    1875           5 :     if (eErr != CE_None)
    1876           2 :         return eErr;
    1877             : 
    1878           3 :     EHdrDataset *poEDS = reinterpret_cast<EHdrDataset *>(poDS);
    1879             : 
    1880           3 :     minmaxmeanstddev = HAS_ALL_FLAGS;
    1881             : 
    1882           3 :     if (!bApproxOK && poEDS->RewriteSTX() != CE_None)
    1883           0 :         RawRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev);
    1884             : 
    1885           3 :     if (pdfMin)
    1886           3 :         *pdfMin = dfMin;
    1887           3 :     if (pdfMax)
    1888           3 :         *pdfMax = dfMax;
    1889           3 :     if (pdfMean)
    1890           3 :         *pdfMean = dfMean;
    1891           3 :     if (pdfStdDev)
    1892           3 :         *pdfStdDev = dfStdDev;
    1893             : 
    1894           3 :     return CE_None;
    1895             : }
    1896             : 
    1897             : /************************************************************************/
    1898             : /*                           SetStatistics()                            */
    1899             : /************************************************************************/
    1900             : 
    1901           3 : CPLErr EHdrRasterBand::SetStatistics(double dfMinIn, double dfMaxIn,
    1902             :                                      double dfMeanIn, double dfStdDevIn)
    1903             : {
    1904             :     // Avoid churn if nothing is changing.
    1905           3 :     if (dfMin == dfMinIn && dfMax == dfMaxIn && dfMean == dfMeanIn &&
    1906           1 :         dfStdDev == dfStdDevIn)
    1907           1 :         return CE_None;
    1908             : 
    1909           2 :     dfMin = dfMinIn;
    1910           2 :     dfMax = dfMaxIn;
    1911           2 :     dfMean = dfMeanIn;
    1912           2 :     dfStdDev = dfStdDevIn;
    1913             : 
    1914             :     // marks stats valid
    1915           2 :     minmaxmeanstddev = HAS_ALL_FLAGS;
    1916             : 
    1917           2 :     EHdrDataset *poEDS = reinterpret_cast<EHdrDataset *>(poDS);
    1918             : 
    1919           2 :     if (GetMetadataItem("STATISTICS_APPROXIMATE") == nullptr)
    1920             :     {
    1921           2 :         if (GetMetadataItem("STATISTICS_MINIMUM"))
    1922             :         {
    1923           0 :             SetMetadataItem("STATISTICS_MINIMUM", nullptr);
    1924           0 :             SetMetadataItem("STATISTICS_MAXIMUM", nullptr);
    1925           0 :             SetMetadataItem("STATISTICS_MEAN", nullptr);
    1926           0 :             SetMetadataItem("STATISTICS_STDDEV", nullptr);
    1927             :         }
    1928           2 :         return poEDS->RewriteSTX();
    1929             :     }
    1930             : 
    1931           0 :     return RawRasterBand::SetStatistics(dfMinIn, dfMaxIn, dfMeanIn, dfStdDevIn);
    1932             : }
    1933             : 
    1934             : /************************************************************************/
    1935             : /*                           GetColorTable()                            */
    1936             : /************************************************************************/
    1937             : 
    1938          13 : GDALColorTable *EHdrRasterBand::GetColorTable()
    1939             : {
    1940          13 :     return m_poColorTable.get();
    1941             : }
    1942             : 
    1943             : /************************************************************************/
    1944             : /*                           SetColorTable()                            */
    1945             : /************************************************************************/
    1946             : 
    1947           6 : CPLErr EHdrRasterBand::SetColorTable(GDALColorTable *poNewCT)
    1948             : {
    1949           6 :     if (poNewCT == nullptr)
    1950           2 :         m_poColorTable.reset();
    1951             :     else
    1952           4 :         m_poColorTable.reset(poNewCT->Clone());
    1953             : 
    1954           6 :     reinterpret_cast<EHdrDataset *>(poDS)->bCLRDirty = true;
    1955             : 
    1956           6 :     return CE_None;
    1957             : }
    1958             : 
    1959             : /************************************************************************/
    1960             : /*                           GetDefaultRAT()                            */
    1961             : /************************************************************************/
    1962             : 
    1963          13 : GDALRasterAttributeTable *EHdrRasterBand::GetDefaultRAT()
    1964             : {
    1965          13 :     return m_poRAT.get();
    1966             : }
    1967             : 
    1968             : /************************************************************************/
    1969             : /*                            SetDefaultRAT()                           */
    1970             : /************************************************************************/
    1971             : 
    1972           3 : CPLErr EHdrRasterBand::SetDefaultRAT(const GDALRasterAttributeTable *poRAT)
    1973             : {
    1974           3 :     if (poRAT)
    1975             :     {
    1976           3 :         if (!(poRAT->GetColumnCount() == 4 &&
    1977           1 :               poRAT->GetTypeOfCol(0) == GFT_Integer &&
    1978           1 :               poRAT->GetTypeOfCol(1) == GFT_Integer &&
    1979           1 :               poRAT->GetTypeOfCol(2) == GFT_Integer &&
    1980           1 :               poRAT->GetTypeOfCol(3) == GFT_Integer &&
    1981           1 :               poRAT->GetUsageOfCol(0) == GFU_Generic &&
    1982           1 :               poRAT->GetUsageOfCol(1) == GFU_Red &&
    1983           1 :               poRAT->GetUsageOfCol(2) == GFU_Green &&
    1984           1 :               poRAT->GetUsageOfCol(3) == GFU_Blue))
    1985             :         {
    1986           1 :             CPLError(CE_Warning, CPLE_NotSupported,
    1987             :                      "Unsupported type of RAT: "
    1988             :                      "only value,R,G,B ones are supported");
    1989           1 :             return CE_Failure;
    1990             :         }
    1991             :     }
    1992             : 
    1993           2 :     if (poRAT == nullptr)
    1994           1 :         m_poRAT.reset();
    1995             :     else
    1996           1 :         m_poRAT.reset(poRAT->Clone());
    1997             : 
    1998           2 :     reinterpret_cast<EHdrDataset *>(poDS)->bCLRDirty = true;
    1999             : 
    2000           2 :     return CE_None;
    2001             : }
    2002             : 
    2003             : /************************************************************************/
    2004             : /*                         GDALRegister_EHdr()                          */
    2005             : /************************************************************************/
    2006             : 
    2007        1682 : void GDALRegister_EHdr()
    2008             : 
    2009             : {
    2010        1682 :     if (GDALGetDriverByName("EHdr") != nullptr)
    2011         301 :         return;
    2012             : 
    2013        1381 :     GDALDriver *poDriver = new GDALDriver();
    2014             : 
    2015        1381 :     poDriver->SetDescription("EHdr");
    2016        1381 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    2017        1381 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "ESRI .hdr Labelled");
    2018        1381 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/ehdr.html");
    2019        1381 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "bil");
    2020        1381 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
    2021        1381 :                               "Byte Int8 Int16 UInt16 Int32 UInt32 Float32");
    2022             : 
    2023        1381 :     poDriver->SetMetadataItem(
    2024             :         GDAL_DMD_CREATIONOPTIONLIST,
    2025             :         "<CreationOptionList>"
    2026             :         "   <Option name='NBITS' type='int' description='Special pixel bits "
    2027             :         "(1-7)'/>"
    2028             :         "   <Option name='PIXELTYPE' type='string' description='By setting "
    2029             :         "this to SIGNEDBYTE, a new Byte file can be forced to be written as "
    2030             :         "signed byte'/>"
    2031        1381 :         "</CreationOptionList>");
    2032             : 
    2033        1381 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    2034        1381 :     poDriver->pfnOpen = EHdrDataset::Open;
    2035        1381 :     poDriver->pfnCreate = EHdrDataset::Create;
    2036        1381 :     poDriver->pfnCreateCopy = EHdrDataset::CreateCopy;
    2037             : 
    2038        1381 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    2039             : }

Generated by: LCOV version 1.14