LCOV - code coverage report
Current view: top level - frmts/miramon - miramon_band.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 483 627 77.0 %
Date: 2026-02-12 23:49:34 Functions: 28 29 96.6 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  MiraMonRaster driver
       4             :  * Purpose:  Implements MMRBand class: This class manages the metadata of each
       5             :  *           band to be processed. It is useful for maintaining a list of bands
       6             :  *           and for determining the number of subdatasets that need to be
       7             :  *           generated.
       8             :  * Author:   Abel Pau
       9             :  *
      10             :  ******************************************************************************
      11             :  * Copyright (c) 2025, Xavier Pons
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : #include <algorithm>
      16             : #include <limits>
      17             : #include "gdal_rat.h"
      18             : 
      19             : #include "miramon_rel.h"
      20             : #include "miramon_band.h"
      21             : 
      22             : #include "../miramon_common/mm_gdal_functions.h"  // For MM_CreateDBFHeader
      23             : 
      24             : /************************************************************************/
      25             : /*                              MMRBand()                               */
      26             : /************************************************************************/
      27         108 : MMRBand::MMRBand(MMRRel &fRel, const CPLString &osBandSectionIn)
      28             :     : m_pfRel(&fRel), m_nWidth(0), m_nHeight(0),
      29         108 :       m_osBandSection(osBandSectionIn)
      30             : {
      31             :     // Getting band and band file name from metadata.
      32         108 :     CPLString osNomFitxer;
      33         108 :     osNomFitxer = SECTION_ATTRIBUTE_DATA;
      34         108 :     osNomFitxer.append(":");
      35         108 :     osNomFitxer.append(osBandSectionIn);
      36         108 :     if (!m_pfRel->GetMetadataValue(osNomFitxer, KEY_NomFitxer,
      37         161 :                                    m_osRawBandFileName) ||
      38          53 :         m_osRawBandFileName.empty())
      39             :     {
      40             :         // A band name may be empty only if it is the only band present
      41             :         // in the REL file. Otherwise, inferring the band name from the
      42             :         // REL filename is considered an error.
      43             :         // Consequently, for a REL file containing exactly one band, if
      44             :         // the band name is empty, it shall be inferred from the REL
      45             :         // filename.
      46             :         // Example: REL: testI.rel  -->  IMG: test.img
      47          58 :         if (m_pfRel->GetNBands() >= 1)
      48           0 :             m_osBandFileName = "";
      49             :         else
      50             :         {
      51         232 :             m_osBandFileName = m_pfRel->MMRGetFileNameFromRelName(
      52         174 :                 m_pfRel->GetRELName(), pszExtRaster);
      53             :         }
      54             : 
      55          58 :         if (m_osBandFileName.empty())
      56             :         {
      57           0 :             m_nWidth = 0;
      58           0 :             m_nHeight = 0;
      59           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
      60             :                      "The REL file '%s' contains a documented \
      61             :                 band with no explicit or wrong name. Section [%s] or [%s:%s].",
      62           0 :                      m_pfRel->GetRELNameChar(), SECTION_ATTRIBUTE_DATA,
      63             :                      SECTION_ATTRIBUTE_DATA, m_osBandSection.c_str());
      64           0 :             return;
      65             :         }
      66          58 :         m_osBandName = CPLGetBasenameSafe(m_osBandFileName);
      67          58 :         m_osRawBandFileName = m_osBandName;
      68             :     }
      69             :     else
      70             :     {
      71          50 :         m_osBandName = CPLGetBasenameSafe(m_osRawBandFileName);
      72          50 :         CPLString osAux = CPLGetPathSafe(m_pfRel->GetRELNameChar());
      73             :         m_osBandFileName =
      74          50 :             CPLFormFilenameSafe(osAux.c_str(), m_osRawBandFileName.c_str(), "");
      75             : 
      76             :         CPLString osExtension =
      77          50 :             CPLString(CPLGetExtensionSafe(m_osBandFileName).c_str());
      78          50 :         if (!EQUAL(osExtension, pszExtRaster + 1))
      79           0 :             return;
      80             :     }
      81             : 
      82             :     // There is a band file documented?
      83         108 :     if (m_osBandName.empty())
      84             :     {
      85           0 :         m_nWidth = 0;
      86           0 :         m_nHeight = 0;
      87           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
      88             :                  "The REL file '%s' contains a documented \
      89             :             band with no explicit name. Section [%s] or [%s:%s].",
      90           0 :                  m_pfRel->GetRELNameChar(), SECTION_ATTRIBUTE_DATA,
      91             :                  SECTION_ATTRIBUTE_DATA, m_osBandSection.c_str());
      92           0 :         return;
      93             :     }
      94             : 
      95             :     // Getting essential metadata documented at
      96             :     // https://www.miramon.cat/new_note/eng/notes/MiraMon_raster_file_format.pdf
      97             : 
      98             :     // Getting number of columns and rows
      99         108 :     if (!UpdateColumnsNumberFromREL(m_osBandSection))
     100             :     {
     101           1 :         m_nWidth = 0;
     102           1 :         m_nHeight = 0;
     103           1 :         return;
     104             :     }
     105             : 
     106         107 :     if (!UpdateRowsNumberFromREL(m_osBandSection))
     107             :     {
     108           1 :         m_nWidth = 0;
     109           1 :         m_nHeight = 0;
     110           1 :         return;
     111             :     }
     112             : 
     113         106 :     if (m_nWidth <= 0 || m_nHeight <= 0)
     114             :     {
     115           1 :         m_nWidth = 0;
     116           1 :         m_nHeight = 0;
     117           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     118             :                  "MMRBand::MMRBand : (nWidth <= 0 || nHeight <= 0)");
     119           1 :         return;
     120             :     }
     121             : 
     122             :     // Getting data type and compression.
     123             :     // If error, message given inside.
     124         105 :     if (!UpdateDataTypeFromREL(m_osBandSection))
     125           2 :         return;
     126             : 
     127             :     // Let's see if there is RLE compression
     128         103 :     m_bIsCompressed =
     129         154 :         (((m_eMMDataType >= MMDataType::DATATYPE_AND_COMPR_BYTE_RLE) &&
     130         155 :           (m_eMMDataType <= MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE)) ||
     131          52 :          m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BIT);
     132             : 
     133             :     // Getting min and max values
     134         103 :     UpdateMinMaxValuesFromREL(m_osBandSection);
     135             : 
     136             :     // Getting unit type
     137         103 :     UpdateUnitTypeValueFromREL(m_osBandSection);
     138             : 
     139             :     // Getting min and max values for simbolization
     140         103 :     UpdateMinMaxVisuValuesFromREL(m_osBandSection);
     141         103 :     if (!m_bMinVisuSet)
     142             :     {
     143          98 :         if (m_bMinSet)
     144             :         {
     145          95 :             m_dfVisuMin = m_dfMin;
     146          95 :             m_bMinVisuSet = true;
     147             :         }
     148             :     }
     149         103 :     if (!m_bMaxVisuSet)
     150             :     {
     151          98 :         if (m_bMaxSet)
     152             :         {
     153          95 :             m_dfVisuMax = m_dfMax;
     154          95 :             m_bMaxVisuSet = true;
     155             :         }
     156             :     }
     157             : 
     158             :     // Getting the friendly description of the band
     159         103 :     UpdateFriendlyDescriptionFromREL(m_osBandSection);
     160             : 
     161             :     // Getting NoData value and definition
     162         103 :     UpdateNoDataValue(m_osBandSection);
     163             : 
     164             :     // Getting reference system and coordinates of the geographic bounding box
     165         103 :     UpdateReferenceSystemFromREL();
     166             : 
     167             :     // Getting the bounding box: coordinates in the terrain
     168         103 :     UpdateBoundingBoxFromREL(m_osBandSection);
     169             : 
     170             :     // Getting all information about simbolization
     171         103 :     UpdateSimbolizationInfo(m_osBandSection);
     172             : 
     173             :     // Getting all information about RAT
     174         103 :     UpdateRATInfo(m_osBandSection);
     175             : 
     176             :     // MiraMon IMG files are efficient in going to an specified row.
     177             :     // So le'ts configurate the blocks as line blocks.
     178         103 :     m_nBlockXSize = m_nWidth;
     179         103 :     m_nBlockYSize = 1;
     180         103 :     m_nNRowsPerBlock = 1;
     181             : 
     182             :     // Can the binary file that contains all data for this band be opened?
     183         103 :     m_pfIMG = VSIFOpenL(m_osBandFileName, "rb");
     184         103 :     if (!m_pfIMG)
     185             :     {
     186           1 :         m_nWidth = 0;
     187           1 :         m_nHeight = 0;
     188           1 :         CPLError(CE_Failure, CPLE_OpenFailed,
     189             :                  "Failed to open MiraMon band file `%s' with access 'rb'.",
     190             :                  m_osBandFileName.c_str());
     191           1 :         return;
     192             :     }
     193             : 
     194             :     // We have a valid MMRBand.
     195         102 :     m_bIsValid = true;
     196             : }
     197             : 
     198             : /************************************************************************/
     199             : /*                              ~MMRBand()                              */
     200             : /************************************************************************/
     201         198 : MMRBand::~MMRBand()
     202             : {
     203         108 :     if (m_pfIMG == nullptr)
     204           6 :         return;
     205             : 
     206         102 :     CPL_IGNORE_RET_VAL(VSIFCloseL(m_pfIMG));
     207         102 :     m_pfIMG = nullptr;
     208         108 : }
     209             : 
     210          40 : const CPLString MMRBand::GetRELFileName() const
     211             : {
     212          40 :     if (!m_pfRel)
     213           0 :         return "";
     214          40 :     return m_pfRel->GetRELName();
     215             : }
     216             : 
     217             : /************************************************************************/
     218             : /*                           GetRasterBlock()                           */
     219             : /************************************************************************/
     220         115 : CPLErr MMRBand::GetRasterBlock(int /*nXBlock*/, int nYBlock, void *pData,
     221             :                                int nDataSize)
     222             : 
     223             : {
     224         115 :     if (nYBlock > INT_MAX / (std::max(1, m_nNRowsPerBlock)))
     225             :     {
     226           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "Error in GetRasterBlock");
     227           0 :         return CE_Failure;
     228             :     }
     229         115 :     const int iBlock = nYBlock * m_nNRowsPerBlock;
     230             : 
     231         115 :     if (m_nBlockXSize > INT_MAX / (std::max(1, m_nDataTypeSizeBytes)))
     232             :     {
     233           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "Error in GetRasterBlock");
     234           0 :         return CE_Failure;
     235             :     }
     236             : 
     237         230 :     if (m_nBlockYSize >
     238         115 :         INT_MAX / (std::max(1, m_nDataTypeSizeBytes * m_nBlockXSize)))
     239             :     {
     240           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "Error in GetRasterBlock");
     241           0 :         return CE_Failure;
     242             :     }
     243             : 
     244         115 :     const int nGDALBlockSize =
     245         115 :         m_nDataTypeSizeBytes * m_nBlockXSize * m_nBlockYSize;
     246             : 
     247             :     // Calculate block offset in case we have spill file. Use predefined
     248             :     // block map otherwise.
     249             : 
     250         115 :     if (nDataSize != -1 && nGDALBlockSize > nDataSize)
     251             :     {
     252           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid block size: %d",
     253             :                  nGDALBlockSize);
     254           0 :         return CE_Failure;
     255             :     }
     256             : 
     257             :     // Getting the row offsets to optimize access.
     258         115 :     if (FillRowOffsets() == false || m_aFileOffsets.empty())
     259             :     {
     260           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     261             :                  "Some error in offsets calculation");
     262           0 :         return CE_Failure;
     263             :     }
     264             : 
     265             :     // Read the block in the documented or deduced offset
     266         115 :     if (VSIFSeekL(m_pfIMG, m_aFileOffsets[iBlock], SEEK_SET))
     267             :     {
     268           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     269             :                  "Read from invalid offset for grid block.");
     270           0 :         return CE_Failure;
     271             :     }
     272             : 
     273             :     size_t nCompressedRawSize;
     274         115 :     if (iBlock == m_nHeight - 1)
     275          35 :         nCompressedRawSize = SIZE_MAX;  // We don't know it
     276             :     else
     277         160 :         nCompressedRawSize = static_cast<size_t>(m_aFileOffsets[iBlock + 1] -
     278          80 :                                                  m_aFileOffsets[iBlock]);
     279             : 
     280         115 :     return GetBlockData(pData, nCompressedRawSize);
     281             : }
     282             : 
     283          61 : void MMRBand::UpdateGeoTransform()
     284             : {
     285          61 :     m_gt.xorig = GetBoundingBoxMinX();
     286          61 :     m_gt.xscale = (GetBoundingBoxMaxX() - m_gt.xorig) / GetWidth();
     287          61 :     m_gt.xrot = 0.0;  // No rotation in MiraMon rasters
     288          61 :     m_gt.yorig = GetBoundingBoxMaxY();
     289          61 :     m_gt.yrot = 0.0;
     290          61 :     m_gt.yscale = (GetBoundingBoxMinY() - m_gt.yorig) / GetHeight();
     291          61 : }
     292             : 
     293             : /************************************************************************/
     294             : /*                           Other functions                            */
     295             : /************************************************************************/
     296             : 
     297             : // [ATTRIBUTE_DATA:xxxx] or [OVERVIEW:ASPECTES_TECNICS]
     298         215 : bool MMRBand::Get_ATTRIBUTE_DATA_or_OVERVIEW_ASPECTES_TECNICS_int(
     299             :     const CPLString &osSection, const char *pszKey, int *nValue,
     300             :     const char *pszErrorMessage)
     301             : {
     302         215 :     if (osSection.empty() || !pszKey || !nValue)
     303           0 :         return false;
     304             : 
     305         430 :     CPLString osValue;
     306         215 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, pszKey,
     307         217 :                                    osValue) ||
     308           2 :         osValue.empty())
     309             :     {
     310         213 :         if (m_pfRel->GetMetadataValue(SECTION_OVERVIEW,
     311             :                                       SECTION_ASPECTES_TECNICS, pszKey,
     312         424 :                                       osValue) == false ||
     313         211 :             osValue.empty())
     314             :         {
     315           2 :             if (pszErrorMessage)
     316           2 :                 CPLError(CE_Failure, CPLE_AppDefined, "%s", pszErrorMessage);
     317           2 :             return false;
     318             :         }
     319             :     }
     320             : 
     321         213 :     if (1 != sscanf(osValue, "%d", nValue))
     322             :     {
     323           0 :         if (pszErrorMessage)
     324           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", pszErrorMessage);
     325           0 :         return false;
     326             :     }
     327         213 :     return true;
     328             : }
     329             : 
     330         104 : bool MMRBand::GetDataTypeAndBytesPerPixel(const char *pszCompType,
     331             :                                           MMDataType *nCompressionType,
     332             :                                           MMBytesPerPixel *nBytesPerPixel)
     333             : {
     334         104 :     if (!nCompressionType || !nBytesPerPixel || !pszCompType)
     335           0 :         return false;
     336             : 
     337         104 :     if (EQUAL(pszCompType, "bit"))
     338             :     {
     339           2 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_BIT;
     340           2 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_BYTE_I_RLE;
     341           2 :         return true;
     342             :     }
     343         102 :     if (EQUAL(pszCompType, "byte"))
     344             :     {
     345          35 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_BYTE;
     346          35 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_BYTE_I_RLE;
     347          35 :         return true;
     348             :     }
     349          67 :     if (EQUAL(pszCompType, "byte-RLE"))
     350             :     {
     351          25 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_BYTE_RLE;
     352          25 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_BYTE_I_RLE;
     353          25 :         return true;
     354             :     }
     355          42 :     if (EQUAL(pszCompType, "integer"))
     356             :     {
     357           3 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_INTEGER;
     358           3 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
     359           3 :         return true;
     360             :     }
     361          39 :     if (EQUAL(pszCompType, "integer-RLE"))
     362             :     {
     363          16 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE;
     364          16 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
     365          16 :         return true;
     366             :     }
     367          23 :     if (EQUAL(pszCompType, "uinteger"))
     368             :     {
     369           4 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_UINTEGER;
     370           4 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
     371           4 :         return true;
     372             :     }
     373          19 :     if (EQUAL(pszCompType, "uinteger-RLE"))
     374             :     {
     375           2 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE;
     376           2 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
     377           2 :         return true;
     378             :     }
     379          17 :     if (EQUAL(pszCompType, "long"))
     380             :     {
     381           3 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_LONG;
     382           3 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
     383           3 :         return true;
     384             :     }
     385          14 :     if (EQUAL(pszCompType, "long-RLE"))
     386             :     {
     387           2 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_LONG_RLE;
     388           2 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
     389           2 :         return true;
     390             :     }
     391          12 :     if (EQUAL(pszCompType, "real"))
     392             :     {
     393           3 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_REAL;
     394           3 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
     395           3 :         return true;
     396             :     }
     397           9 :     if (EQUAL(pszCompType, "real-RLE"))
     398             :     {
     399           2 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_REAL_RLE;
     400           2 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
     401           2 :         return true;
     402             :     }
     403           7 :     if (EQUAL(pszCompType, "double"))
     404             :     {
     405           2 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_DOUBLE;
     406           2 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_DOUBLE_I_RLE;
     407           2 :         return true;
     408             :     }
     409           5 :     if (EQUAL(pszCompType, "double-RLE"))
     410             :     {
     411           4 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE;
     412           4 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_DOUBLE_I_RLE;
     413           4 :         return true;
     414             :     }
     415             : 
     416           1 :     return false;
     417             : }
     418             : 
     419             : // Getting data type from metadata
     420         105 : bool MMRBand::UpdateDataTypeFromREL(const CPLString osSection)
     421             : {
     422         105 :     m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_UNDEFINED;
     423         105 :     m_eMMBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_UNDEFINED;
     424             : 
     425         210 :     CPLString osValue;
     426         105 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     427         210 :                                    "TipusCompressio", osValue) ||
     428         105 :         osValue.empty())
     429             :     {
     430           1 :         m_nWidth = 0;
     431           1 :         m_nHeight = 0;
     432           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     433             :                  "MiraMonRaster: no nDataType documented");
     434           1 :         return false;
     435             :     }
     436             : 
     437         104 :     if (!GetDataTypeAndBytesPerPixel(osValue.c_str(), &m_eMMDataType,
     438             :                                      &m_eMMBytesPerPixel))
     439             :     {
     440           1 :         m_nWidth = 0;
     441           1 :         m_nHeight = 0;
     442           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     443             :                  "MiraMonRaster: data type unhandled");
     444           1 :         return false;
     445             :     }
     446             : 
     447         103 :     m_nDataTypeSizeBytes = std::max(1, static_cast<int>(m_eMMBytesPerPixel));
     448         103 :     return true;
     449             : }
     450             : 
     451             : // Getting number of columns from metadata
     452         108 : bool MMRBand::UpdateColumnsNumberFromREL(const CPLString &osSection)
     453             : {
     454         108 :     return Get_ATTRIBUTE_DATA_or_OVERVIEW_ASPECTES_TECNICS_int(
     455             :         osSection, "columns", &m_nWidth,
     456         108 :         "MMRBand::MMRBand : No number of columns documented");
     457             : }
     458             : 
     459         107 : bool MMRBand::UpdateRowsNumberFromREL(const CPLString &osSection)
     460             : {
     461         107 :     return Get_ATTRIBUTE_DATA_or_OVERVIEW_ASPECTES_TECNICS_int(
     462             :         osSection, "rows", &m_nHeight,
     463         107 :         "MMRBand::MMRBand : No number of rows documented");
     464             : }
     465             : 
     466             : // Getting nodata value from metadata
     467         103 : void MMRBand::UpdateNoDataValue(const CPLString &osSection)
     468             : {
     469         206 :     CPLString osValue;
     470         103 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, "NODATA",
     471         158 :                                    osValue) ||
     472          55 :         osValue.empty())
     473             :     {
     474          62 :         m_dfNoData = 0;  // No a valid value.
     475          62 :         m_bNoDataSet = false;
     476             :     }
     477             :     else
     478             :     {
     479          41 :         m_dfNoData = CPLAtof(osValue);
     480          41 :         m_bNoDataSet = true;
     481             :     }
     482         103 : }
     483             : 
     484         103 : void MMRBand::UpdateMinMaxValuesFromREL(const CPLString &osSection)
     485             : {
     486         103 :     m_bMinSet = false;
     487             : 
     488         206 :     CPLString osValue;
     489             : 
     490         206 :     CPLString osAuxSection = SECTION_ATTRIBUTE_DATA;
     491         103 :     osAuxSection.append(":");
     492         103 :     osAuxSection.append(osSection);
     493         206 :     if (m_pfRel->GetMetadataValue(osAuxSection, "min", osValue) &&
     494         103 :         !osValue.empty())
     495             :     {
     496         100 :         if (1 == CPLsscanf(osValue, "%lf", &m_dfMin))
     497         100 :             m_bMinSet = true;
     498             :     }
     499             : 
     500         103 :     m_bMaxSet = false;
     501         206 :     if (m_pfRel->GetMetadataValue(osAuxSection, "max", osValue) &&
     502         103 :         !osValue.empty())
     503             :     {
     504         100 :         if (1 == CPLsscanf(osValue, "%lf", &m_dfMax))
     505         100 :             m_bMaxSet = true;
     506             :     }
     507             : 
     508             :     // Special case: dfMin > dfMax
     509         103 :     if (m_bMinSet && m_bMaxSet && m_dfMin > m_dfMax)
     510             :     {
     511           0 :         m_bMinSet = false;
     512           0 :         m_bMaxSet = false;
     513             :     }
     514         103 : }
     515             : 
     516         103 : void MMRBand::UpdateUnitTypeValueFromREL(const CPLString &osSection)
     517             : {
     518         206 :     CPLString osValue;
     519             : 
     520         206 :     CPLString osAuxSection = SECTION_ATTRIBUTE_DATA;
     521         103 :     osAuxSection.append(":");
     522         103 :     osAuxSection.append(osSection);
     523         109 :     if (m_pfRel->GetMetadataValue(osAuxSection, "unitats", osValue) &&
     524           6 :         !osValue.empty())
     525             :     {
     526           6 :         m_osBandUnitType = osValue;
     527             :     }
     528         103 : }
     529             : 
     530         103 : void MMRBand::UpdateMinMaxVisuValuesFromREL(const CPLString &osSection)
     531             : {
     532         103 :     m_bMinVisuSet = false;
     533         103 :     m_dfVisuMin = 1;
     534             : 
     535         206 :     CPLString osValue;
     536         103 :     if (m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
     537         108 :                                   "Color_ValorColor_0", osValue) &&
     538           5 :         !osValue.empty())
     539             :     {
     540           5 :         if (1 == CPLsscanf(osValue, "%lf", &m_dfVisuMin))
     541           5 :             m_bMinVisuSet = true;
     542             :     }
     543             : 
     544         103 :     m_bMaxVisuSet = false;
     545         103 :     m_dfVisuMax = 1;
     546             : 
     547         103 :     if (m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
     548         108 :                                   "Color_ValorColor_n_1", osValue) &&
     549           5 :         !osValue.empty())
     550             :     {
     551           5 :         if (1 == CPLsscanf(osValue, "%lf", &m_dfVisuMax))
     552           5 :             m_bMaxVisuSet = true;
     553             :     }
     554         103 : }
     555             : 
     556         103 : void MMRBand::UpdateFriendlyDescriptionFromREL(const CPLString &osSection)
     557             : {
     558             :     // This "if" is due to CID 1620830 in Coverity Scan
     559         103 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     560         103 :                                    KEY_descriptor, m_osFriendlyDescription))
     561          10 :         m_osFriendlyDescription = "";
     562         103 : }
     563             : 
     564         103 : void MMRBand::UpdateReferenceSystemFromREL()
     565             : {
     566             :     // This "if" is due to CID 1620842 in Coverity Scan
     567         103 :     if (!m_pfRel->GetMetadataValue("SPATIAL_REFERENCE_SYSTEM:HORIZONTAL",
     568         103 :                                    "HorizontalSystemIdentifier", m_osRefSystem))
     569           0 :         m_osRefSystem = "";
     570         103 : }
     571             : 
     572         103 : void MMRBand::UpdateBoundingBoxFromREL(const CPLString &osSection)
     573             : {
     574             :     // Bounding box of the band
     575             :     // [ATTRIBUTE_DATA:xxxx:EXTENT] or [EXTENT]
     576         206 :     CPLString osValue;
     577         103 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     578         196 :                                    SECTION_EXTENT, "MinX", osValue) ||
     579          93 :         osValue.empty())
     580             :     {
     581          10 :         m_dfBBMinX = 0;
     582             :     }
     583             :     else
     584             :     {
     585          93 :         if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMinX))
     586           0 :             m_dfBBMinX = 0;
     587             :     }
     588             : 
     589         103 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     590         196 :                                    SECTION_EXTENT, "MaxX", osValue) ||
     591          93 :         osValue.empty())
     592             :     {
     593          10 :         m_dfBBMaxX = m_nWidth;
     594             :     }
     595             :     else
     596             :     {
     597          93 :         if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMaxX))
     598             :         {
     599             :             // If the value is something that cannot be scanned,
     600             :             // we silently continue as it was undefined.
     601           0 :             m_dfBBMaxX = m_nWidth;
     602             :         }
     603             :     }
     604             : 
     605         103 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     606         196 :                                    SECTION_EXTENT, "MinY", osValue) ||
     607          93 :         osValue.empty())
     608             :     {
     609          10 :         m_dfBBMinY = 0;
     610             :     }
     611             :     else
     612             :     {
     613          93 :         if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMinY))
     614           0 :             m_dfBBMinY = 0;
     615             :     }
     616             : 
     617         103 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     618         196 :                                    SECTION_EXTENT, "MaxY", osValue) ||
     619          93 :         osValue.empty())
     620             :     {
     621          10 :         m_dfBBMaxY = m_nHeight;
     622             :     }
     623             :     else
     624             :     {
     625          93 :         if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMaxY))
     626             :         {
     627             :             // If the value is something that cannot be scanned,
     628             :             // we silently continue as it was undefined.
     629           0 :             m_dfBBMaxY = m_nHeight;
     630             :         }
     631             :     }
     632         103 : }
     633             : 
     634         103 : void MMRBand::UpdateSimbolizationInfo(const CPLString &osSection)
     635             : {
     636         103 :     m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection, "Color_Const",
     637         103 :                               m_osColor_Const);
     638             : 
     639         103 :     if (EQUAL(m_osColor_Const, "1"))
     640             :     {
     641           1 :         if (CE_None == m_pfRel->UpdateGDALColorEntryFromBand(
     642           1 :                            osSection, m_sConstantColorRGB))
     643           1 :             m_osValidColorConst = true;
     644             :     }
     645             : 
     646         103 :     m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection, "Color_Paleta",
     647         103 :                               m_osColor_Paleta);
     648             : 
     649             :     // Treatment of the color variable
     650         103 :     m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
     651             :                               "Color_TractamentVariable",
     652         103 :                               m_osColor_TractamentVariable);
     653             : 
     654         103 :     m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     655         103 :                               KEY_TractamentVariable, m_osTractamentVariable);
     656             : 
     657             :     // Is categorical?
     658         103 :     if (m_osTractamentVariable.empty())
     659             :     {
     660           8 :         m_bIsCategorical = false;
     661             :     }
     662             :     else
     663             :     {
     664          95 :         if (EQUAL(m_osTractamentVariable, "Categoric"))
     665          89 :             m_bIsCategorical = true;
     666             :         else
     667           6 :             m_bIsCategorical = false;
     668             :     }
     669             : 
     670         103 :     m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
     671         103 :                               "Color_EscalatColor", m_osColor_EscalatColor);
     672             : 
     673         103 :     m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
     674             :                               "Color_N_SimbolsALaTaula",
     675         103 :                               m_osColor_N_SimbolsALaTaula);
     676         103 : }
     677             : 
     678         103 : void MMRBand::UpdateRATInfo(const CPLString &osSection)
     679             : {
     680         103 :     CPLString os_IndexJoin;
     681             : 
     682         103 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     683         181 :                                    "IndexsJoinTaula", os_IndexJoin) ||
     684          78 :         os_IndexJoin.empty())
     685             :     {
     686          25 :         return;
     687             :     }
     688             : 
     689             :     // Let's see if there is any table that can ve converted to RAT
     690          78 :     const CPLStringList aosTokens(CSLTokenizeString2(os_IndexJoin, ",", 0));
     691          78 :     const int nTokens = CSLCount(aosTokens);
     692          78 :     if (nTokens < 1)
     693           0 :         return;
     694             : 
     695          78 :     CPLString os_Join = "JoinTaula";
     696          78 :     os_Join.append("_");
     697          78 :     os_Join.append(aosTokens[0]);
     698             : 
     699          78 :     CPLString osTableNameSection_value;
     700          78 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, os_Join,
     701         156 :                                    osTableNameSection_value) ||
     702          78 :         osTableNameSection_value.empty())
     703           0 :         return;
     704             : 
     705          78 :     CPLString osTableNameSection = "TAULA_";
     706          78 :     osTableNameSection.append(osTableNameSection_value);
     707             : 
     708          78 :     if (!m_pfRel->GetMetadataValue(osTableNameSection, KEY_NomFitxer,
     709         156 :                                    m_osShortRATName) ||
     710          78 :         m_osShortRATName.empty())
     711             :     {
     712           0 :         m_osAssociateREL = "";
     713           0 :         return;
     714             :     }
     715             : 
     716          78 :     m_pfRel->GetMetadataValue(osTableNameSection, "AssociatRel",
     717          78 :                               m_osAssociateREL);
     718             : }
     719             : 
     720             : /************************************************************************/
     721             : /*             Functions that read bytes from IMG file band             */
     722             : /************************************************************************/
     723             : template <typename TYPE>
     724          54 : CPLErr MMRBand::UncompressRow(void *rowBuffer, size_t nCompressedRawSize)
     725             : {
     726          54 :     int nAccumulated = 0L, nIAccumulated = 0L;
     727             :     unsigned char cCounter;
     728          54 :     size_t nCompressedIndex = 0;
     729             : 
     730             :     TYPE RLEValue;
     731             :     TYPE *pDst;
     732          54 :     size_t sizeof_TYPE = sizeof(TYPE);
     733             : 
     734         108 :     std::vector<unsigned char> aCompressedRow;
     735             : 
     736          54 :     if (nCompressedRawSize != SIZE_MAX)
     737             :     {
     738          32 :         if (nCompressedRawSize > 1000 * 1000 &&
     739           0 :             GetFileSize() < nCompressedRawSize)
     740             :         {
     741           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too small file");
     742           0 :             return CE_Failure;
     743             :         }
     744             :         try
     745             :         {
     746          32 :             aCompressedRow.resize(nCompressedRawSize);
     747             :         }
     748           0 :         catch (const std::exception &)
     749             :         {
     750           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
     751             :                      "Out of memory allocating working buffer");
     752           0 :             return CE_Failure;
     753             :         }
     754          32 :         if (VSIFReadL(aCompressedRow.data(), nCompressedRawSize, 1, m_pfIMG) !=
     755             :             1)
     756           0 :             return CE_Failure;
     757             :     }
     758             : 
     759         162 :     while (nAccumulated < m_nWidth)
     760             :     {
     761         108 :         if (nCompressedRawSize == SIZE_MAX)
     762             :         {
     763          44 :             if (VSIFReadL(&cCounter, 1, 1, m_pfIMG) != 1)
     764           0 :                 return CE_Failure;
     765             :         }
     766             :         else
     767             :         {
     768          64 :             if (nCompressedIndex >= aCompressedRow.size())
     769             :             {
     770           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     771             :                          "Invalid nCompressedIndex");
     772           0 :                 return CE_Failure;
     773             :             }
     774          64 :             cCounter = aCompressedRow[nCompressedIndex];
     775          64 :             nCompressedIndex++;
     776             :         }
     777             : 
     778         108 :         if (cCounter == 0) /* Not compressed part */
     779             :         {
     780             :             /* The following counter read does not indicate
     781             :             "how many repeated values follow" but rather
     782             :             "how many are decompressed in standard raster format" */
     783           0 :             if (nCompressedRawSize == SIZE_MAX)
     784             :             {
     785           0 :                 if (VSIFReadL(&cCounter, 1, 1, m_pfIMG) != 1)
     786           0 :                     return CE_Failure;
     787             :             }
     788             :             else
     789             :             {
     790           0 :                 if (nCompressedIndex >= aCompressedRow.size())
     791             :                 {
     792           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     793             :                              "Invalid nCompressedIndex");
     794           0 :                     return CE_Failure;
     795             :                 }
     796           0 :                 cCounter = aCompressedRow[nCompressedIndex];
     797           0 :                 nCompressedIndex++;
     798             :             }
     799             : 
     800           0 :             nAccumulated += cCounter;
     801             : 
     802           0 :             if (nAccumulated > m_nWidth) /* This should not happen if the file
     803             :                                   is RLE and does not share counters across rows */
     804           0 :                 return CE_Failure;
     805             : 
     806           0 :             for (; nIAccumulated < nAccumulated; nIAccumulated++)
     807             :             {
     808           0 :                 if (nCompressedRawSize == SIZE_MAX)
     809             :                 {
     810           0 :                     VSIFReadL(&RLEValue, sizeof_TYPE, 1, m_pfIMG);
     811           0 :                     memcpy((static_cast<TYPE *>(rowBuffer)) + nIAccumulated,
     812             :                            &RLEValue, sizeof_TYPE);
     813             :                 }
     814             :                 else
     815             :                 {
     816           0 :                     if (nCompressedIndex + sizeof_TYPE > aCompressedRow.size())
     817             :                     {
     818           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     819             :                                  "Invalid nCompressedIndex");
     820           0 :                         return CE_Failure;
     821             :                     }
     822           0 :                     memcpy((static_cast<TYPE *>(rowBuffer)) + nIAccumulated,
     823           0 :                            &aCompressedRow[nCompressedIndex], sizeof_TYPE);
     824           0 :                     nCompressedIndex += sizeof_TYPE;
     825             :                 }
     826             :             }
     827             :         }
     828             :         else
     829             :         {
     830         108 :             nAccumulated += cCounter;
     831         108 :             if (nAccumulated > m_nWidth) /* This should not happen if the file
     832             :                                   is RLE and does not share counters across rows */
     833           0 :                 return CE_Failure;
     834             : 
     835         108 :             if (nCompressedRawSize == SIZE_MAX)
     836             :             {
     837          44 :                 if (VSIFReadL(&RLEValue, sizeof_TYPE, 1, m_pfIMG) != 1)
     838           0 :                     return CE_Failure;
     839             :             }
     840             :             else
     841             :             {
     842          64 :                 if (nCompressedIndex + sizeof(TYPE) > aCompressedRow.size())
     843             :                 {
     844           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     845             :                              "Invalid nCompressedIndex");
     846           0 :                     return CE_Failure;
     847             :                 }
     848          64 :                 memcpy(&RLEValue, &aCompressedRow[nCompressedIndex],
     849             :                        sizeof(TYPE));
     850          64 :                 nCompressedIndex += sizeof(TYPE);
     851             :             }
     852             : 
     853         108 :             const int nCount = nAccumulated - nIAccumulated;
     854         108 :             pDst = static_cast<TYPE *>(rowBuffer) + nIAccumulated;
     855             : 
     856         108 :             std::fill(pDst, pDst + nCount, RLEValue);
     857             : 
     858         108 :             nIAccumulated = nAccumulated;
     859             :         }
     860             :     }
     861             : 
     862          54 :     return CE_None;
     863             : }
     864             : 
     865         121 : CPLErr MMRBand::GetBlockData(void *rowBuffer, size_t nCompressedRawSize)
     866             : {
     867         121 :     if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BIT)
     868             :     {
     869          16 :         const int nGDALBlockSize = DIV_ROUND_UP(m_nBlockXSize, 8);
     870             : 
     871          16 :         if (VSIFReadL(rowBuffer, nGDALBlockSize, 1, m_pfIMG) != 1)
     872             :         {
     873           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Error while reading band");
     874           0 :             return CE_Failure;
     875             :         }
     876          16 :         return CE_None;
     877             :     }
     878             : 
     879         105 :     if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BYTE ||
     880          84 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_INTEGER ||
     881          78 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_UINTEGER ||
     882          72 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_LONG ||
     883          66 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_REAL ||
     884          60 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_DOUBLE)
     885             :     {
     886          51 :         if (VSIFReadL(rowBuffer, m_nDataTypeSizeBytes, m_nWidth, m_pfIMG) !=
     887          51 :             static_cast<size_t>(m_nWidth))
     888             :         {
     889           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Error while reading band");
     890           0 :             return CE_Failure;
     891             :         }
     892          51 :         return CE_None;
     893             :     }
     894             : 
     895             :     CPLErr eErr;
     896          54 :     switch (m_eMMDataType)
     897             :     {
     898          24 :         case MMDataType::DATATYPE_AND_COMPR_BYTE_RLE:
     899          24 :             eErr = UncompressRow<GByte>(rowBuffer, nCompressedRawSize);
     900          24 :             break;
     901           6 :         case MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE:
     902           6 :             eErr = UncompressRow<GInt16>(rowBuffer, nCompressedRawSize);
     903           6 :             break;
     904           6 :         case MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE:
     905           6 :             eErr = UncompressRow<GUInt16>(rowBuffer, nCompressedRawSize);
     906           6 :             break;
     907           6 :         case MMDataType::DATATYPE_AND_COMPR_LONG_RLE:
     908           6 :             eErr = UncompressRow<GInt32>(rowBuffer, nCompressedRawSize);
     909           6 :             break;
     910           6 :         case MMDataType::DATATYPE_AND_COMPR_REAL_RLE:
     911           6 :             eErr = UncompressRow<float>(rowBuffer, nCompressedRawSize);
     912           6 :             break;
     913           6 :         case MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE:
     914           6 :             eErr = UncompressRow<double>(rowBuffer, nCompressedRawSize);
     915           6 :             break;
     916             : 
     917           0 :         default:
     918           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Error in datatype");
     919           0 :             eErr = CE_Failure;
     920             :     }
     921             : 
     922          54 :     return eErr;
     923             : }  // End of GetBlockData()
     924             : 
     925          16 : int MMRBand::PositionAtStartOfRowOffsetsInFile()
     926             : {
     927             :     vsi_l_offset nFileSize, nHeaderOffset;
     928             :     char szChain[16];
     929             :     GInt16 nVersion, nSubVersion;
     930             :     int nOffsetSize, nOffsetsSectionType;
     931             : 
     932          16 :     if (VSIFSeekL(m_pfIMG, 0, SEEK_END))
     933           0 :         return 0;
     934             : 
     935          16 :     nFileSize = VSIFTellL(m_pfIMG);
     936             : 
     937          16 :     if (nFileSize < 32)  // Minimum required size
     938           2 :         return 0;
     939             : 
     940          14 :     if (m_nHeight)
     941             :     {
     942          14 :         if (nFileSize < static_cast<vsi_l_offset>(32) + m_nHeight + 32)
     943           0 :             return 0;
     944             :     }
     945             : 
     946          14 :     vsi_l_offset nHeadOffset = nFileSize - 32;
     947             : 
     948          14 :     if (VSIFSeekL(m_pfIMG, nHeadOffset, SEEK_SET))  // Reading final header.
     949           0 :         return 0;
     950          14 :     if (VSIFReadL(szChain, 16, 1, m_pfIMG) != 1)
     951           0 :         return 0;
     952         238 :     for (int nIndex = 0; nIndex < 16; nIndex++)
     953             :     {
     954         224 :         if (szChain[nIndex] != '\0')
     955           0 :             return 0;  // Supposed 0's are not 0.
     956             :     }
     957             : 
     958          14 :     if (VSIFReadL(szChain, 8, 1, m_pfIMG) != 1)
     959           0 :         return 0;
     960             : 
     961          14 :     if (strncmp(szChain, "IMG ", 4) || szChain[5] != '.')
     962           0 :         return 0;
     963             : 
     964             :     // Some version checks
     965          14 :     szChain[7] = 0;
     966          14 :     if (sscanf(szChain + 6, "%hd", &nSubVersion) != 1 || nSubVersion < 0)
     967           0 :         return 0;
     968             : 
     969          14 :     szChain[5] = 0;
     970          14 :     if (sscanf(szChain + 4, "%hd", &nVersion) != 1 || nVersion != 1)
     971           0 :         return 0;
     972             : 
     973             :     // Next header to be read
     974          14 :     if (VSIFReadL(&nHeaderOffset, sizeof(vsi_l_offset), 1, m_pfIMG) != 1)
     975           0 :         return 0;
     976             : 
     977          28 :     std::set<vsi_l_offset> alreadyVisitedOffsets;
     978             :     bool bRepeat;
     979          14 :     do
     980             :     {
     981          14 :         bRepeat = FALSE;
     982             : 
     983          14 :         if (VSIFSeekL(m_pfIMG, nHeaderOffset, SEEK_SET))
     984           0 :             return 0;
     985             : 
     986          14 :         if (VSIFReadL(szChain, 8, 1, m_pfIMG) != 1)
     987           0 :             return 0;
     988             : 
     989          14 :         if (strncmp(szChain, "IMG ", 4) || szChain[5] != '.')
     990           0 :             return 0;
     991             : 
     992          14 :         if (VSIFReadL(&nOffsetsSectionType, 4, 1, m_pfIMG) != 1)
     993           0 :             return 0;
     994             : 
     995          14 :         if (nOffsetsSectionType != 2)  // 2 = row offsets section
     996             :         {
     997             :             // This is not the section I am looking for
     998           0 :             if (VSIFSeekL(m_pfIMG, 8 + 4, SEEK_CUR))
     999           0 :                 return 0;
    1000             : 
    1001           0 :             if (VSIFReadL(&nHeaderOffset, sizeof(vsi_l_offset), 1, m_pfIMG) !=
    1002             :                 1)
    1003           0 :                 return 0;
    1004             : 
    1005           0 :             if (nHeaderOffset == 0)
    1006           0 :                 return 0;
    1007             : 
    1008           0 :             if (cpl::contains(alreadyVisitedOffsets, nHeaderOffset))
    1009             :             {
    1010           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    1011             :                          "Error reading offsets. They will be ignored.");
    1012           0 :                 return 0;
    1013             :             }
    1014             : 
    1015           0 :             alreadyVisitedOffsets.insert(nHeaderOffset);
    1016             : 
    1017           0 :             bRepeat = TRUE;
    1018             :         }
    1019             : 
    1020             :     } while (bRepeat);
    1021             : 
    1022          14 :     szChain[7] = 0;
    1023          14 :     if (sscanf(szChain + 6, "%hd", &nSubVersion) != 1 || nSubVersion < 0)
    1024           0 :         return 0;
    1025          14 :     szChain[5] = 0;
    1026          14 :     if (sscanf(szChain + 4, "%hd", &nVersion) != 1 || nVersion != 1)
    1027           0 :         return 0;
    1028             : 
    1029             :     /*
    1030             :         Now I'm in the correct section
    1031             :         -------------------------------
    1032             :         Info about this section:
    1033             :         RasterRLE: minimum size: nHeight*2
    1034             :         Offsets:   minimum size: 32+nHeight*4
    1035             :         Final:     size: 32
    1036             :     */
    1037             : 
    1038          14 :     if (m_nHeight)
    1039             :     {
    1040          14 :         if (nHeaderOffset < static_cast<vsi_l_offset>(m_nHeight) *
    1041          14 :                                 2 ||  // Minimum size of an RLE
    1042          14 :             nFileSize - nHeaderOffset <
    1043          14 :                 static_cast<vsi_l_offset>(32) + m_nHeight +
    1044             :                     32)  // Minimum size of the section in version 1.0
    1045           0 :             return 0;
    1046             :     }
    1047             : 
    1048          28 :     if (VSIFReadL(&nOffsetSize, 4, 1, m_pfIMG) != 1 ||
    1049          14 :         (nOffsetSize != 8 && nOffsetSize != 4 && nOffsetSize != 2 &&
    1050          14 :          nOffsetSize != 1))
    1051           0 :         return 0;
    1052             : 
    1053          14 :     if (m_nHeight)
    1054             :     {
    1055          14 :         if (nFileSize - nHeaderOffset <
    1056          14 :             32 + static_cast<vsi_l_offset>(nOffsetSize) * m_nHeight +
    1057             :                 32)  // No space for this section in this file
    1058           0 :             return 0;
    1059             : 
    1060             :         // I leave the file prepared to read offsets
    1061          14 :         if (VSIFSeekL(m_pfIMG, 16, SEEK_CUR))
    1062           0 :             return 0;
    1063             :     }
    1064             :     else
    1065             :     {
    1066           0 :         if (VSIFSeekL(m_pfIMG, 4, SEEK_CUR))
    1067           0 :             return 0;
    1068             : 
    1069           0 :         if (VSIFSeekL(m_pfIMG, 4, SEEK_CUR))
    1070           0 :             return 0;
    1071             : 
    1072             :         // I leave the file prepared to read offsets
    1073           0 :         if (VSIFSeekL(m_pfIMG, 8, SEEK_CUR))
    1074           0 :             return 0;
    1075             :     }
    1076             : 
    1077             :     // There are offsets!
    1078          14 :     return nOffsetSize;
    1079             : }  // Fi de PositionAtStartOfRowOffsetsInFile()
    1080             : 
    1081             : /************************************************************************/
    1082             : /*                            GetFileSize()                             */
    1083             : /************************************************************************/
    1084             : 
    1085           0 : vsi_l_offset MMRBand::GetFileSize()
    1086             : {
    1087           0 :     if (m_nFileSize == 0)
    1088             :     {
    1089           0 :         const auto nCurPos = VSIFTellL(m_pfIMG);
    1090           0 :         VSIFSeekL(m_pfIMG, 0, SEEK_END);
    1091           0 :         m_nFileSize = VSIFTellL(m_pfIMG);
    1092           0 :         VSIFSeekL(m_pfIMG, nCurPos, SEEK_SET);
    1093             :     }
    1094           0 :     return m_nFileSize;
    1095             : }
    1096             : 
    1097             : /************************************************************************/
    1098             : /*                           FillRowOffsets()                           */
    1099             : /************************************************************************/
    1100             : 
    1101         115 : bool MMRBand::FillRowOffsets()
    1102             : {
    1103             :     vsi_l_offset nStartOffset;
    1104             :     int nIRow;
    1105             :     vsi_l_offset nBytesPerPixelPerNCol;
    1106             :     int nSizeToRead;  // nSizeToRead is not an offset, but the size of the offsets being read
    1107             :                       // directly from the IMG file (can be 1, 2, 4, or 8).
    1108             :     vsi_l_offset nFileByte;
    1109             :     size_t nMaxBytesPerCompressedRow;
    1110         115 :     const int nGDALBlockSize = DIV_ROUND_UP(m_nBlockXSize, 8);
    1111             : 
    1112             :     // If it's filled, there is no need to fill it again
    1113         115 :     if (!m_aFileOffsets.empty())
    1114          80 :         return true;
    1115             : 
    1116             :     // Sanity check to avoid attempting huge memory allocation
    1117          35 :     if (m_nHeight > 1000 * 1000)
    1118             :     {
    1119           0 :         if (GetFileSize() < static_cast<vsi_l_offset>(m_nHeight))
    1120             :         {
    1121           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too small file");
    1122           0 :             return false;
    1123             :         }
    1124             :     }
    1125             : 
    1126             :     try
    1127             :     {
    1128          35 :         m_aFileOffsets.resize(static_cast<size_t>(m_nHeight) + 1);
    1129             :     }
    1130           0 :     catch (const std::bad_alloc &e)
    1131             :     {
    1132           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1133           0 :         return false;
    1134             :     }
    1135             : 
    1136          35 :     switch (m_eMMDataType)
    1137             :     {
    1138           2 :         case MMDataType::DATATYPE_AND_COMPR_BIT:
    1139             : 
    1140             :             // "<=" it's ok. There is space and it's to make easier the programming
    1141          20 :             for (nIRow = 0; nIRow <= m_nHeight; nIRow++)
    1142          18 :                 m_aFileOffsets[nIRow] =
    1143          18 :                     static_cast<vsi_l_offset>(nIRow) * nGDALBlockSize;
    1144           2 :             break;
    1145             : 
    1146          17 :         case MMDataType::DATATYPE_AND_COMPR_BYTE:
    1147             :         case MMDataType::DATATYPE_AND_COMPR_INTEGER:
    1148             :         case MMDataType::DATATYPE_AND_COMPR_UINTEGER:
    1149             :         case MMDataType::DATATYPE_AND_COMPR_LONG:
    1150             :         case MMDataType::DATATYPE_AND_COMPR_REAL:
    1151             :         case MMDataType::DATATYPE_AND_COMPR_DOUBLE:
    1152          17 :             nBytesPerPixelPerNCol =
    1153          17 :                 m_nDataTypeSizeBytes * static_cast<vsi_l_offset>(m_nWidth);
    1154             :             // "<=" it's ok. There is space and it's to make easier the programming
    1155          85 :             for (nIRow = 0; nIRow <= m_nHeight; nIRow++)
    1156          68 :                 m_aFileOffsets[nIRow] =
    1157          68 :                     static_cast<vsi_l_offset>(nIRow) * nBytesPerPixelPerNCol;
    1158          17 :             break;
    1159             : 
    1160          16 :         case MMDataType::DATATYPE_AND_COMPR_BYTE_RLE:
    1161             :         case MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE:
    1162             :         case MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE:
    1163             :         case MMDataType::DATATYPE_AND_COMPR_LONG_RLE:
    1164             :         case MMDataType::DATATYPE_AND_COMPR_REAL_RLE:
    1165             :         case MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE:
    1166             : 
    1167          16 :             nStartOffset = VSIFTellL(m_pfIMG);
    1168             : 
    1169             :             // Let's determine if are there offsets in the file
    1170          16 :             if (0 < (nSizeToRead = PositionAtStartOfRowOffsetsInFile()))
    1171             :             {
    1172             :                 // I have offsets!!
    1173          14 :                 nFileByte = 0L;  // all bits to 0
    1174          56 :                 for (nIRow = 0; nIRow < m_nHeight; nIRow++)
    1175             :                 {
    1176          42 :                     if (VSIFReadL(&nFileByte, nSizeToRead, 1, m_pfIMG) != 1)
    1177           0 :                         return false;
    1178             : 
    1179          42 :                     m_aFileOffsets[nIRow] = nFileByte;
    1180             : 
    1181             :                     // Let's check that the difference between two offsets is in a int range
    1182          42 :                     if (nIRow > 0)
    1183             :                     {
    1184          56 :                         if (m_aFileOffsets[nIRow] <=
    1185          28 :                             m_aFileOffsets[static_cast<size_t>(nIRow) - 1])
    1186           0 :                             return false;
    1187             : 
    1188          28 :                         if (m_aFileOffsets[nIRow] -
    1189          28 :                                 m_aFileOffsets[static_cast<size_t>(nIRow) -
    1190          28 :                                                1] >=
    1191             :                             static_cast<vsi_l_offset>(SIZE_MAX))
    1192           0 :                             return false;
    1193             :                     }
    1194             :                 }
    1195          14 :                 m_aFileOffsets[nIRow] = 0;  // Not reliable
    1196          14 :                 VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
    1197          14 :                 break;
    1198             :             }
    1199             : 
    1200             :             // Not indexed RLE. We create a dynamic indexation
    1201           4 :             if (m_nWidth >
    1202           2 :                 INT_MAX /
    1203           2 :                     (std::max(1, static_cast<int>(m_eMMBytesPerPixel)) + 1))
    1204             :             {
    1205           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too large row: %d",
    1206             :                          m_nWidth);
    1207           0 :                 VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
    1208           0 :                 return false;
    1209             :             }
    1210             : 
    1211           2 :             nMaxBytesPerCompressedRow =
    1212           2 :                 static_cast<int>(m_eMMBytesPerPixel)
    1213           2 :                     ? (m_nWidth * (static_cast<int>(m_eMMBytesPerPixel) + 1))
    1214           0 :                     : (m_nWidth * (1 + 1));
    1215             :             unsigned char *pBuffer;
    1216             : 
    1217           2 :             if (nullptr == (pBuffer = static_cast<unsigned char *>(
    1218           2 :                                 VSI_MALLOC_VERBOSE(nMaxBytesPerCompressedRow))))
    1219             :             {
    1220           0 :                 VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
    1221           0 :                 return false;
    1222             :             }
    1223             : 
    1224           2 :             VSIFSeekL(m_pfIMG, 0, SEEK_SET);
    1225           2 :             m_aFileOffsets[0] = 0;
    1226           8 :             for (nIRow = 0; nIRow < m_nHeight; nIRow++)
    1227             :             {
    1228           6 :                 GetBlockData(pBuffer, SIZE_MAX);
    1229           6 :                 m_aFileOffsets[static_cast<size_t>(nIRow) + 1] =
    1230           6 :                     VSIFTellL(m_pfIMG);
    1231             :             }
    1232           2 :             VSIFree(pBuffer);
    1233           2 :             VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
    1234           2 :             break;
    1235             : 
    1236           0 :         default:
    1237           0 :             return false;
    1238             :     }  // End of switch (eMMDataType)
    1239          35 :     return true;
    1240             : 
    1241             : }  // End of FillRowOffsets()

Generated by: LCOV version 1.14