LCOV - code coverage report
Current view: top level - frmts/miramon - miramon_band.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 990 1276 77.6 %
Date: 2026-03-05 10:33:42 Functions: 45 46 97.8 %

          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         257 : MMRBand::MMRBand(MMRRel &fRel, const CPLString &osBandSectionIn)
      28             :     : m_pfRel(&fRel), m_nWidth(0), m_nHeight(0),
      29         257 :       m_osBandSection(osBandSectionIn)
      30             : {
      31             :     // Getting band and band file name from metadata.
      32         257 :     CPLString osNomFitxer;
      33         257 :     osNomFitxer = SECTION_ATTRIBUTE_DATA;
      34         257 :     osNomFitxer.append(":");
      35         257 :     osNomFitxer.append(osBandSectionIn);
      36         257 :     if (!m_pfRel->GetMetadataValue(osNomFitxer, KEY_NomFitxer,
      37         397 :                                    m_osRawBandFileName) ||
      38         140 :         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         120 :         if (m_pfRel->GetNBands() >= 1)
      48           0 :             m_osBandFileName = "";
      49             :         else
      50             :         {
      51         360 :             m_osBandFileName = m_pfRel->MMRGetFileNameFromRelName(
      52         240 :                 m_pfRel->GetRELName(), pszExtRaster);
      53             :         }
      54             : 
      55         120 :         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         120 :         m_osBandName = CPLGetBasenameSafe(m_osBandFileName);
      67         120 :         m_osRawBandFileName = m_osBandName;
      68             :     }
      69             :     else
      70             :     {
      71         137 :         m_osBandName = CPLGetBasenameSafe(m_osRawBandFileName);
      72         137 :         CPLString osAux = CPLGetPathSafe(m_pfRel->GetRELNameChar());
      73             :         m_osBandFileName =
      74         137 :             CPLFormFilenameSafe(osAux.c_str(), m_osRawBandFileName.c_str(), "");
      75             : 
      76             :         CPLString osExtension =
      77         137 :             CPLString(CPLGetExtensionSafe(m_osBandFileName).c_str());
      78         137 :         if (!EQUAL(osExtension, pszExtRaster + 1))
      79           0 :             return;
      80             :     }
      81             : 
      82             :     // There is a band file documented?
      83         257 :     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         257 :     if (!UpdateColumnsNumberFromREL(m_osBandSection))
     100             :     {
     101           1 :         m_nWidth = 0;
     102           1 :         m_nHeight = 0;
     103           1 :         return;
     104             :     }
     105             : 
     106         256 :     if (!UpdateRowsNumberFromREL(m_osBandSection))
     107             :     {
     108           1 :         m_nWidth = 0;
     109           1 :         m_nHeight = 0;
     110           1 :         return;
     111             :     }
     112             : 
     113         255 :     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         254 :     if (!UpdateDataTypeFromREL(m_osBandSection))
     125           2 :         return;
     126             : 
     127             :     // Let's see if there is RLE compression
     128         252 :     m_bIsCompressed =
     129         377 :         (((m_eMMDataType >= MMDataType::DATATYPE_AND_COMPR_BYTE_RLE) &&
     130         379 :           (m_eMMDataType <= MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE)) ||
     131         127 :          m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BIT);
     132             : 
     133             :     // Getting min and max values
     134         252 :     UpdateMinMaxValuesFromREL(m_osBandSection);
     135             : 
     136             :     // Getting unit type
     137         252 :     UpdateUnitTypeValueFromREL(m_osBandSection);
     138             : 
     139             :     // Getting min and max values for simbolization
     140         252 :     UpdateMinMaxVisuValuesFromREL(m_osBandSection);
     141         252 :     if (!m_bMinVisuSet)
     142             :     {
     143         240 :         if (m_bMinSet)
     144             :         {
     145         237 :             m_dfVisuMin = m_dfMin;
     146         237 :             m_bMinVisuSet = true;
     147             :         }
     148             :     }
     149         252 :     if (!m_bMaxVisuSet)
     150             :     {
     151         240 :         if (m_bMaxSet)
     152             :         {
     153         237 :             m_dfVisuMax = m_dfMax;
     154         237 :             m_bMaxVisuSet = true;
     155             :         }
     156             :     }
     157             : 
     158             :     // Getting the friendly description of the band
     159         252 :     UpdateFriendlyDescriptionFromREL(m_osBandSection);
     160             : 
     161             :     // Getting NoData value and definition
     162         252 :     UpdateNoDataValue(m_osBandSection);
     163             : 
     164             :     // Getting reference system and coordinates of the geographic bounding box
     165         252 :     UpdateReferenceSystemFromREL();
     166             : 
     167             :     // Getting the bounding box: coordinates in the terrain
     168         252 :     UpdateBoundingBoxFromREL(m_osBandSection);
     169             : 
     170             :     // Getting all information about simbolization
     171         252 :     UpdateSimbolizationInfo(m_osBandSection);
     172             : 
     173             :     // Getting all information about RAT
     174         252 :     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         252 :     m_nBlockXSize = m_nWidth;
     179         252 :     m_nBlockYSize = 1;
     180         252 :     m_nNRowsPerBlock = 1;
     181             : 
     182             :     // Can the binary file that contains all data for this band be opened?
     183         252 :     m_pfIMG = VSIFOpenL(m_osBandFileName, "rb");
     184         252 :     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         251 :     m_bIsValid = true;
     196             : }
     197             : 
     198         167 : MMRBand::MMRBand(GDALProgressFunc pfnProgress, void *pProgressData,
     199             :                  GDALDataset &oSrcDS, int nIBand, const CPLString &osDestPath,
     200             :                  GDALRasterBand &papoBand, bool bCompress, bool bCategorical,
     201             :                  const CPLString &osPattern, const CPLString &osBandSection,
     202         167 :                  bool bNeedOfNomFitxer)
     203             :     : m_pfnProgress(pfnProgress), m_pProgressData(pProgressData),
     204             :       m_nIBand(nIBand), m_osBandSection(osBandSection),
     205         167 :       m_osFriendlyDescription(papoBand.GetDescription()),
     206         167 :       m_bIsCompressed(bCompress), m_bIsCategorical(bCategorical)
     207             : {
     208             :     // Getting the binary filename
     209         167 :     if (bNeedOfNomFitxer)
     210         101 :         m_osBandName = osPattern + "_" + osBandSection;
     211             :     else
     212          66 :         m_osBandName = osPattern;
     213             : 
     214         167 :     m_osRawBandFileName = m_osBandName + pszExtRaster;
     215             :     m_osBandFileName =
     216         167 :         CPLFormFilenameSafe(osDestPath, m_osBandName, pszExtRaster);
     217             : 
     218             :     // Getting essential metadata documented at
     219             :     // https://www.miramon.cat/new_note/eng/notes/MiraMon_raster_file_format.pdf
     220             : 
     221             :     // Getting number of columns and rows
     222         167 :     m_nWidth = papoBand.GetXSize();
     223         167 :     m_nHeight = papoBand.GetYSize();
     224             : 
     225         167 :     if (m_nWidth <= 0 || m_nHeight <= 0)
     226             :     {
     227           0 :         m_nWidth = 0;
     228           0 :         m_nHeight = 0;
     229           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     230             :                  "MMRBand::MMRBand : (nWidth <= 0 || nHeight <= 0)");
     231           0 :         return;
     232             :     }
     233             : 
     234             :     // Getting units
     235         167 :     m_osBandUnitType = papoBand.GetUnitType();
     236             : 
     237             :     // Getting data type and compression from papoBand.
     238             :     // If error, message given inside.
     239         167 :     if (!UpdateDataTypeAndBytesPerPixelFromRasterBand(papoBand))
     240             :     {
     241           5 :         CPLError(CE_Failure, CPLE_AppDefined,
     242             :                  "MMRBand::MMRBand : DataType not supported");
     243           5 :         return;
     244             :     }
     245         162 :     m_nDataTypeSizeBytes = std::max(1, static_cast<int>(m_eMMBytesPerPixel));
     246         162 :     m_bIsCompressed = bCompress;
     247             : 
     248             :     // Getting NoData value and definition
     249         162 :     UpdateNoDataValueFromRasterBand(papoBand);
     250             : 
     251         162 :     if (WriteColorTable(oSrcDS))
     252             :     {
     253           0 :         m_osCTName.clear();
     254           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     255             :                  "MMRBand::MMRBand : Existent color table but not imported"
     256             :                  "due to some existent errors");
     257             :     }
     258             : 
     259         162 :     if (WriteAttributeTable(oSrcDS))
     260             :     {
     261           0 :         m_osRATDBFName.clear();
     262           0 :         m_osRATRELName.clear();
     263           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     264             :                  "MMRBand::MMRBand : Existent attribute table but not imported "
     265             :                  "due to some existent errors");
     266             :     }
     267             : 
     268             :     // We have a valid MMRBand.
     269         162 :     m_bIsValid = true;
     270             : }
     271             : 
     272             : /************************************************************************/
     273             : /*                              ~MMRBand()                              */
     274             : /************************************************************************/
     275         690 : MMRBand::~MMRBand()
     276             : {
     277         424 :     if (m_pfIMG == nullptr)
     278          14 :         return;
     279             : 
     280         410 :     CPL_IGNORE_RET_VAL(VSIFCloseL(m_pfIMG));
     281         410 :     m_pfIMG = nullptr;
     282         424 : }
     283             : 
     284          40 : const CPLString &MMRBand::GetRELFileName() const
     285             : {
     286          40 :     static const CPLString osEmpty;
     287          40 :     if (!m_pfRel)
     288           0 :         return osEmpty;
     289          40 :     return m_pfRel->GetRELName();
     290             : }
     291             : 
     292             : /************************************************************************/
     293             : /*                           GetRasterBlock()                           */
     294             : /************************************************************************/
     295         385 : CPLErr MMRBand::GetRasterBlock(int /*nXBlock*/, int nYBlock, void *pData,
     296             :                                int nDataSize)
     297             : 
     298             : {
     299         385 :     if (nYBlock > INT_MAX / (std::max(1, m_nNRowsPerBlock)))
     300             :     {
     301           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "Error in GetRasterBlock");
     302           0 :         return CE_Failure;
     303             :     }
     304         385 :     const int iBlock = nYBlock * m_nNRowsPerBlock;
     305             : 
     306         385 :     if (m_nBlockXSize > INT_MAX / (std::max(1, m_nDataTypeSizeBytes)))
     307             :     {
     308           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "Error in GetRasterBlock");
     309           0 :         return CE_Failure;
     310             :     }
     311             : 
     312         770 :     if (m_nBlockYSize >
     313         385 :         INT_MAX / (std::max(1, m_nDataTypeSizeBytes * m_nBlockXSize)))
     314             :     {
     315           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "Error in GetRasterBlock");
     316           0 :         return CE_Failure;
     317             :     }
     318             : 
     319         385 :     const int nGDALBlockSize =
     320         385 :         m_nDataTypeSizeBytes * m_nBlockXSize * m_nBlockYSize;
     321             : 
     322             :     // Calculate block offset in case we have spill file. Use predefined
     323             :     // block map otherwise.
     324             : 
     325         385 :     if (nDataSize != -1 && nGDALBlockSize > nDataSize)
     326             :     {
     327           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid block size: %d",
     328             :                  nGDALBlockSize);
     329           0 :         return CE_Failure;
     330             :     }
     331             : 
     332             :     // Getting the row offsets to optimize access.
     333         385 :     if (FillRowOffsets() == false || m_aFileOffsets.empty())
     334             :     {
     335           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     336             :                  "Some error in offsets calculation");
     337           0 :         return CE_Failure;
     338             :     }
     339             : 
     340             :     // Read the block in the documented or deduced offset
     341         385 :     if (VSIFSeekL(m_pfIMG, m_aFileOffsets[iBlock], SEEK_SET))
     342             :     {
     343           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     344             :                  "Read from invalid offset for grid block.");
     345           0 :         return CE_Failure;
     346             :     }
     347             : 
     348             :     size_t nCompressedRawSize;
     349         385 :     if (iBlock == m_nHeight - 1)
     350         171 :         nCompressedRawSize = SIZE_MAX;  // We don't know it
     351             :     else
     352         428 :         nCompressedRawSize = static_cast<size_t>(m_aFileOffsets[iBlock + 1] -
     353         214 :                                                  m_aFileOffsets[iBlock]);
     354             : 
     355         385 :     return GetBlockData(pData, nCompressedRawSize);
     356             : }
     357             : 
     358         210 : void MMRBand::UpdateGeoTransform()
     359             : {
     360         210 :     m_gt.xorig = GetBoundingBoxMinX();
     361         210 :     m_gt.xscale = (GetBoundingBoxMaxX() - m_gt.xorig) / GetWidth();
     362         210 :     m_gt.xrot = 0.0;  // No rotation in MiraMon rasters
     363         210 :     m_gt.yorig = GetBoundingBoxMaxY();
     364         210 :     m_gt.yrot = 0.0;
     365         210 :     m_gt.yscale = (GetBoundingBoxMinY() - m_gt.yorig) / GetHeight();
     366         210 : }
     367             : 
     368             : /************************************************************************/
     369             : /*                           Other functions                            */
     370             : /************************************************************************/
     371             : 
     372             : // [ATTRIBUTE_DATA:xxxx] or [OVERVIEW:ASPECTES_TECNICS]
     373         513 : bool MMRBand::Get_ATTRIBUTE_DATA_or_OVERVIEW_ASPECTES_TECNICS_int(
     374             :     const CPLString &osSection, const char *pszKey, int *nValue,
     375             :     const char *pszErrorMessage)
     376             : {
     377         513 :     if (osSection.empty() || !pszKey || !nValue)
     378           0 :         return false;
     379             : 
     380        1026 :     CPLString osValue;
     381         513 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, pszKey,
     382         515 :                                    osValue) ||
     383           2 :         osValue.empty())
     384             :     {
     385         511 :         if (m_pfRel->GetMetadataValue(SECTION_OVERVIEW,
     386             :                                       SECTION_ASPECTES_TECNICS, pszKey,
     387        1020 :                                       osValue) == false ||
     388         509 :             osValue.empty())
     389             :         {
     390           2 :             if (pszErrorMessage)
     391           2 :                 CPLError(CE_Failure, CPLE_AppDefined, "%s", pszErrorMessage);
     392           2 :             return false;
     393             :         }
     394             :     }
     395             : 
     396         511 :     if (1 != sscanf(osValue, "%d", nValue))
     397             :     {
     398           0 :         if (pszErrorMessage)
     399           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", pszErrorMessage);
     400           0 :         return false;
     401             :     }
     402         511 :     return true;
     403             : }
     404             : 
     405         253 : bool MMRBand::GetDataTypeAndBytesPerPixel(const char *pszCompType,
     406             :                                           MMDataType *nCompressionType,
     407             :                                           MMBytesPerPixel *nBytesPerPixel)
     408             : {
     409         253 :     if (!nCompressionType || !nBytesPerPixel || !pszCompType)
     410           0 :         return false;
     411             : 
     412         253 :     if (EQUAL(pszCompType, "bit"))
     413             :     {
     414           2 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_BIT;
     415           2 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_BYTE_I_RLE;
     416           2 :         return true;
     417             :     }
     418         251 :     if (EQUAL(pszCompType, "byte"))
     419             :     {
     420          49 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_BYTE;
     421          49 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_BYTE_I_RLE;
     422          49 :         return true;
     423             :     }
     424         202 :     if (EQUAL(pszCompType, "byte-RLE"))
     425             :     {
     426          41 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_BYTE_RLE;
     427          41 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_BYTE_I_RLE;
     428          41 :         return true;
     429             :     }
     430         161 :     if (EQUAL(pszCompType, "integer"))
     431             :     {
     432          15 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_INTEGER;
     433          15 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
     434          15 :         return true;
     435             :     }
     436         146 :     if (EQUAL(pszCompType, "integer-RLE"))
     437             :     {
     438          29 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE;
     439          29 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
     440          29 :         return true;
     441             :     }
     442         117 :     if (EQUAL(pszCompType, "uinteger"))
     443             :     {
     444          18 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_UINTEGER;
     445          18 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
     446          18 :         return true;
     447             :     }
     448          99 :     if (EQUAL(pszCompType, "uinteger-RLE"))
     449             :     {
     450          14 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE;
     451          14 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
     452          14 :         return true;
     453             :     }
     454          85 :     if (EQUAL(pszCompType, "long"))
     455             :     {
     456          14 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_LONG;
     457          14 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
     458          14 :         return true;
     459             :     }
     460          71 :     if (EQUAL(pszCompType, "long-RLE"))
     461             :     {
     462          13 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_LONG_RLE;
     463          13 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
     464          13 :         return true;
     465             :     }
     466          58 :     if (EQUAL(pszCompType, "real"))
     467             :     {
     468          15 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_REAL;
     469          15 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
     470          15 :         return true;
     471             :     }
     472          43 :     if (EQUAL(pszCompType, "real-RLE"))
     473             :     {
     474          13 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_REAL_RLE;
     475          13 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
     476          13 :         return true;
     477             :     }
     478          30 :     if (EQUAL(pszCompType, "double"))
     479             :     {
     480          14 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_DOUBLE;
     481          14 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_DOUBLE_I_RLE;
     482          14 :         return true;
     483             :     }
     484          16 :     if (EQUAL(pszCompType, "double-RLE"))
     485             :     {
     486          15 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE;
     487          15 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_DOUBLE_I_RLE;
     488          15 :         return true;
     489             :     }
     490             : 
     491           1 :     return false;
     492             : }
     493             : 
     494             : // Getting data type from metadata
     495         254 : bool MMRBand::UpdateDataTypeFromREL(const CPLString &osSection)
     496             : {
     497         254 :     m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_UNDEFINED;
     498         254 :     m_eMMBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_UNDEFINED;
     499             : 
     500         508 :     CPLString osValue;
     501         254 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     502         508 :                                    "TipusCompressio", osValue) ||
     503         254 :         osValue.empty())
     504             :     {
     505           1 :         m_nWidth = 0;
     506           1 :         m_nHeight = 0;
     507           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     508             :                  "MiraMonRaster: no nDataType documented");
     509           1 :         return false;
     510             :     }
     511             : 
     512         253 :     if (!GetDataTypeAndBytesPerPixel(osValue.c_str(), &m_eMMDataType,
     513             :                                      &m_eMMBytesPerPixel))
     514             :     {
     515           1 :         m_nWidth = 0;
     516           1 :         m_nHeight = 0;
     517           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     518             :                  "MiraMonRaster: data type unhandled");
     519           1 :         return false;
     520             :     }
     521             : 
     522         252 :     m_nDataTypeSizeBytes = std::max(1, static_cast<int>(m_eMMBytesPerPixel));
     523         252 :     return true;
     524             : }
     525             : 
     526             : // Getting number of columns from metadata
     527         257 : bool MMRBand::UpdateColumnsNumberFromREL(const CPLString &osSection)
     528             : {
     529         257 :     return Get_ATTRIBUTE_DATA_or_OVERVIEW_ASPECTES_TECNICS_int(
     530             :         osSection, "columns", &m_nWidth,
     531         257 :         "MMRBand::MMRBand : No number of columns documented");
     532             : }
     533             : 
     534         256 : bool MMRBand::UpdateRowsNumberFromREL(const CPLString &osSection)
     535             : {
     536         256 :     return Get_ATTRIBUTE_DATA_or_OVERVIEW_ASPECTES_TECNICS_int(
     537             :         osSection, "rows", &m_nHeight,
     538         256 :         "MMRBand::MMRBand : No number of rows documented");
     539             : }
     540             : 
     541             : // Getting nodata value from metadata
     542         252 : void MMRBand::UpdateNoDataValue(const CPLString &osSection)
     543             : {
     544         504 :     CPLString osValue;
     545         252 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, "NODATA",
     546         433 :                                    osValue) ||
     547         181 :         osValue.empty())
     548             :     {
     549          85 :         m_dfNoData = 0;  // No a valid value.
     550          85 :         m_bNoDataSet = false;
     551             :     }
     552             :     else
     553             :     {
     554         167 :         m_dfNoData = CPLAtof(osValue);
     555         167 :         m_bNoDataSet = true;
     556             :     }
     557         252 : }
     558             : 
     559         252 : void MMRBand::UpdateMinMaxValuesFromREL(const CPLString &osSection)
     560             : {
     561         252 :     m_bMinSet = false;
     562             : 
     563         504 :     CPLString osValue;
     564             : 
     565         252 :     if (m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, "min",
     566         504 :                                   osValue) &&
     567         252 :         !osValue.empty())
     568             :     {
     569         249 :         if (1 == CPLsscanf(osValue, "%lf", &m_dfMin))
     570         249 :             m_bMinSet = true;
     571             :     }
     572             : 
     573         252 :     m_bMaxSet = false;
     574         252 :     if (m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, "max",
     575         504 :                                   osValue) &&
     576         252 :         !osValue.empty())
     577             :     {
     578         249 :         if (1 == CPLsscanf(osValue, "%lf", &m_dfMax))
     579         249 :             m_bMaxSet = true;
     580             :     }
     581             : 
     582             :     // Special case: dfMin > dfMax
     583         252 :     if (m_bMinSet && m_bMaxSet && m_dfMin > m_dfMax)
     584             :     {
     585           0 :         m_bMinSet = false;
     586           0 :         m_bMaxSet = false;
     587             :     }
     588         252 : }
     589             : 
     590         252 : void MMRBand::UpdateUnitTypeValueFromREL(const CPLString &osSection)
     591             : {
     592         504 :     CPLString osValue;
     593             : 
     594         252 :     if (m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, "unitats",
     595         298 :                                   osValue) &&
     596          46 :         !osValue.empty())
     597             :     {
     598          41 :         m_osBandUnitType = std::move(osValue);
     599             :     }
     600         252 : }
     601             : 
     602         252 : void MMRBand::UpdateMinMaxVisuValuesFromREL(const CPLString &osSection)
     603             : {
     604         252 :     m_bMinVisuSet = false;
     605         252 :     m_dfVisuMin = 1;
     606             : 
     607         504 :     CPLString osValue;
     608         252 :     if (m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
     609         264 :                                   "Color_ValorColor_0", osValue) &&
     610          12 :         !osValue.empty())
     611             :     {
     612          12 :         if (1 == CPLsscanf(osValue, "%lf", &m_dfVisuMin))
     613          12 :             m_bMinVisuSet = true;
     614             :     }
     615             : 
     616         252 :     m_bMaxVisuSet = false;
     617         252 :     m_dfVisuMax = 1;
     618             : 
     619         252 :     if (m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
     620         264 :                                   "Color_ValorColor_n_1", osValue) &&
     621          12 :         !osValue.empty())
     622             :     {
     623          12 :         if (1 == CPLsscanf(osValue, "%lf", &m_dfVisuMax))
     624          12 :             m_bMaxVisuSet = true;
     625             :     }
     626         252 : }
     627             : 
     628         252 : void MMRBand::UpdateFriendlyDescriptionFromREL(const CPLString &osSection)
     629             : {
     630             :     // This "if" is due to CID 1620830 in Coverity Scan
     631         252 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     632         252 :                                    KEY_descriptor, m_osFriendlyDescription))
     633         147 :         m_osFriendlyDescription = "";
     634         252 : }
     635             : 
     636         252 : void MMRBand::UpdateReferenceSystemFromREL()
     637             : {
     638             :     // This "if" is due to CID 1620842 in Coverity Scan
     639         252 :     if (!m_pfRel->GetMetadataValue("SPATIAL_REFERENCE_SYSTEM:HORIZONTAL",
     640         252 :                                    "HorizontalSystemIdentifier", m_osRefSystem))
     641           0 :         m_osRefSystem = "";
     642         252 : }
     643             : 
     644         252 : void MMRBand::UpdateBoundingBoxFromREL(const CPLString &osSection)
     645             : {
     646             :     // Bounding box of the band
     647             :     // [ATTRIBUTE_DATA:xxxx:EXTENT] or [EXTENT]
     648         504 :     CPLString osValue;
     649         252 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     650         480 :                                    SECTION_EXTENT, "MinX", osValue) ||
     651         228 :         osValue.empty())
     652             :     {
     653          24 :         m_dfBBMinX = 0;
     654             :     }
     655             :     else
     656             :     {
     657         228 :         if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMinX))
     658           0 :             m_dfBBMinX = 0;
     659             :     }
     660             : 
     661         252 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     662         480 :                                    SECTION_EXTENT, "MaxX", osValue) ||
     663         228 :         osValue.empty())
     664             :     {
     665          24 :         m_dfBBMaxX = m_nWidth;
     666             :     }
     667             :     else
     668             :     {
     669         228 :         if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMaxX))
     670             :         {
     671             :             // If the value is something that cannot be scanned,
     672             :             // we silently continue as it was undefined.
     673           0 :             m_dfBBMaxX = m_nWidth;
     674             :         }
     675             :     }
     676             : 
     677         252 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     678         480 :                                    SECTION_EXTENT, "MinY", osValue) ||
     679         228 :         osValue.empty())
     680             :     {
     681          24 :         m_dfBBMinY = 0;
     682             :     }
     683             :     else
     684             :     {
     685         228 :         if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMinY))
     686           0 :             m_dfBBMinY = 0;
     687             :     }
     688             : 
     689         252 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     690         480 :                                    SECTION_EXTENT, "MaxY", osValue) ||
     691         228 :         osValue.empty())
     692             :     {
     693          24 :         m_dfBBMaxY = m_nHeight;
     694             :     }
     695             :     else
     696             :     {
     697         228 :         if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMaxY))
     698             :         {
     699             :             // If the value is something that cannot be scanned,
     700             :             // we silently continue as it was undefined.
     701           0 :             m_dfBBMaxY = m_nHeight;
     702             :         }
     703             :     }
     704         252 : }
     705             : 
     706         252 : void MMRBand::UpdateSimbolizationInfo(const CPLString &osSection)
     707             : {
     708         252 :     m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection, "Color_Const",
     709         252 :                               m_osColor_Const);
     710             : 
     711         252 :     if (EQUAL(m_osColor_Const, "1"))
     712             :     {
     713           3 :         if (CE_None == m_pfRel->UpdateGDALColorEntryFromBand(
     714           3 :                            osSection, m_sConstantColorRGB))
     715           3 :             m_osValidColorConst = true;
     716             :     }
     717             : 
     718         252 :     m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection, "Color_Paleta",
     719         252 :                               m_osColor_Paleta);
     720             : 
     721             :     // Treatment of the color variable
     722         252 :     m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
     723             :                               "Color_TractamentVariable",
     724         252 :                               m_osColor_TractamentVariable);
     725             : 
     726         252 :     m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     727         252 :                               KEY_TractamentVariable, m_osTractamentVariable);
     728             : 
     729             :     // Is categorical?
     730         252 :     if (m_osTractamentVariable.empty())
     731             :     {
     732          16 :         m_bIsCategorical = false;
     733             :     }
     734             :     else
     735             :     {
     736         236 :         if (EQUAL(m_osTractamentVariable, "Categoric"))
     737         119 :             m_bIsCategorical = true;
     738             :         else
     739         117 :             m_bIsCategorical = false;
     740             :     }
     741             : 
     742         252 :     m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
     743         252 :                               "Color_EscalatColor", m_osColor_EscalatColor);
     744             : 
     745         252 :     m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
     746             :                               "Color_N_SimbolsALaTaula",
     747         252 :                               m_osColor_N_SimbolsALaTaula);
     748         252 : }
     749             : 
     750         252 : void MMRBand::UpdateRATInfo(const CPLString &osSection)
     751             : {
     752         252 :     CPLString os_IndexJoin;
     753             : 
     754         252 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     755         359 :                                    "IndexsJoinTaula", os_IndexJoin) ||
     756         107 :         os_IndexJoin.empty())
     757             :     {
     758         145 :         return;
     759             :     }
     760             : 
     761             :     // Let's see if there is any table that can ve converted to RAT
     762         107 :     const CPLStringList aosTokens(CSLTokenizeString2(os_IndexJoin, ",", 0));
     763         107 :     const int nTokens = CSLCount(aosTokens);
     764         107 :     if (nTokens < 1)
     765           0 :         return;
     766             : 
     767         107 :     CPLString os_Join = "JoinTaula";
     768         107 :     os_Join.append("_");
     769         107 :     os_Join.append(aosTokens[0]);
     770             : 
     771         107 :     CPLString osTableNameSection_value;
     772         107 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, os_Join,
     773         214 :                                    osTableNameSection_value) ||
     774         107 :         osTableNameSection_value.empty())
     775           0 :         return;
     776             : 
     777         107 :     CPLString osTableNameSection = "TAULA_";
     778         107 :     osTableNameSection.append(osTableNameSection_value);
     779             : 
     780         107 :     if (!m_pfRel->GetMetadataValue(osTableNameSection, KEY_NomFitxer,
     781         214 :                                    m_osShortRATName) ||
     782         107 :         m_osShortRATName.empty())
     783             :     {
     784           0 :         m_osAssociateREL = "";
     785           0 :         return;
     786             :     }
     787             : 
     788         107 :     CPL_IGNORE_RET_VAL(m_pfRel->GetMetadataValue(
     789         107 :         osTableNameSection, "AssociatRel", m_osAssociateREL));
     790             : }
     791             : 
     792             : /************************************************************************/
     793             : /*             Functions that read bytes from IMG file band             */
     794             : /************************************************************************/
     795             : template <typename TYPE>
     796         192 : CPLErr MMRBand::UncompressRow(void *rowBuffer, size_t nCompressedRawSize)
     797             : {
     798         192 :     int nAccumulated = 0L, nIAccumulated = 0L;
     799             :     unsigned char cCounter;
     800         192 :     size_t nCompressedIndex = 0;
     801             : 
     802             :     TYPE RLEValue;
     803             :     TYPE *pDst;
     804         192 :     size_t sizeof_TYPE = sizeof(TYPE);
     805             : 
     806         384 :     std::vector<unsigned char> aCompressedRow;
     807             : 
     808         192 :     if (nCompressedRawSize != SIZE_MAX)
     809             :     {
     810         100 :         if (nCompressedRawSize > 1000 * 1000 &&
     811           0 :             GetFileSize() < nCompressedRawSize)
     812             :         {
     813           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too small file");
     814           0 :             return CE_Failure;
     815             :         }
     816             :         try
     817             :         {
     818         100 :             aCompressedRow.resize(nCompressedRawSize);
     819             :         }
     820           0 :         catch (const std::exception &)
     821             :         {
     822           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
     823             :                      "Out of memory allocating working buffer");
     824           0 :             return CE_Failure;
     825             :         }
     826         100 :         if (VSIFReadL(aCompressedRow.data(), nCompressedRawSize, 1, m_pfIMG) !=
     827             :             1)
     828           0 :             return CE_Failure;
     829             :     }
     830             : 
     831         685 :     while (nAccumulated < m_nWidth)
     832             :     {
     833         493 :         if (nCompressedRawSize == SIZE_MAX)
     834             :         {
     835         239 :             if (VSIFReadL(&cCounter, 1, 1, m_pfIMG) != 1)
     836           0 :                 return CE_Failure;
     837             :         }
     838             :         else
     839             :         {
     840         254 :             if (nCompressedIndex >= aCompressedRow.size())
     841             :             {
     842           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     843             :                          "Invalid nCompressedIndex");
     844           0 :                 return CE_Failure;
     845             :             }
     846         254 :             cCounter = aCompressedRow[nCompressedIndex];
     847         254 :             nCompressedIndex++;
     848             :         }
     849             : 
     850         493 :         if (cCounter == 0) /* Not compressed part */
     851             :         {
     852             :             /* The following counter read does not indicate
     853             :             "how many repeated values follow" but rather
     854             :             "how many are decompressed in standard raster format" */
     855           6 :             if (nCompressedRawSize == SIZE_MAX)
     856             :             {
     857           0 :                 if (VSIFReadL(&cCounter, 1, 1, m_pfIMG) != 1)
     858           0 :                     return CE_Failure;
     859             :             }
     860             :             else
     861             :             {
     862           6 :                 if (nCompressedIndex >= aCompressedRow.size())
     863             :                 {
     864           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     865             :                              "Invalid nCompressedIndex");
     866           0 :                     return CE_Failure;
     867             :                 }
     868           6 :                 cCounter = aCompressedRow[nCompressedIndex];
     869           6 :                 nCompressedIndex++;
     870             :             }
     871             : 
     872           6 :             nAccumulated += cCounter;
     873             : 
     874           6 :             if (nAccumulated > m_nWidth) /* This should not happen if the file
     875             :                                   is RLE and does not share counters across rows */
     876           0 :                 return CE_Failure;
     877             : 
     878          36 :             for (; nIAccumulated < nAccumulated; nIAccumulated++)
     879             :             {
     880          30 :                 if (nCompressedRawSize == SIZE_MAX)
     881             :                 {
     882           0 :                     VSIFReadL(&RLEValue, sizeof_TYPE, 1, m_pfIMG);
     883           0 :                     memcpy((static_cast<TYPE *>(rowBuffer)) + nIAccumulated,
     884             :                            &RLEValue, sizeof_TYPE);
     885             :                 }
     886             :                 else
     887             :                 {
     888          30 :                     if (nCompressedIndex + sizeof_TYPE > aCompressedRow.size())
     889             :                     {
     890           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     891             :                                  "Invalid nCompressedIndex");
     892           0 :                         return CE_Failure;
     893             :                     }
     894          30 :                     memcpy((static_cast<TYPE *>(rowBuffer)) + nIAccumulated,
     895          30 :                            &aCompressedRow[nCompressedIndex], sizeof_TYPE);
     896          30 :                     nCompressedIndex += sizeof_TYPE;
     897             :                 }
     898             :             }
     899             :         }
     900             :         else
     901             :         {
     902         487 :             nAccumulated += cCounter;
     903         487 :             if (nAccumulated > m_nWidth) /* This should not happen if the file
     904             :                                   is RLE and does not share counters across rows */
     905           0 :                 return CE_Failure;
     906             : 
     907         487 :             if (nCompressedRawSize == SIZE_MAX)
     908             :             {
     909         239 :                 if (VSIFReadL(&RLEValue, sizeof_TYPE, 1, m_pfIMG) != 1)
     910           0 :                     return CE_Failure;
     911             :             }
     912             :             else
     913             :             {
     914         248 :                 if (nCompressedIndex + sizeof(TYPE) > aCompressedRow.size())
     915             :                 {
     916           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     917             :                              "Invalid nCompressedIndex");
     918           0 :                     return CE_Failure;
     919             :                 }
     920         248 :                 memcpy(&RLEValue, &aCompressedRow[nCompressedIndex],
     921             :                        sizeof(TYPE));
     922         248 :                 nCompressedIndex += sizeof(TYPE);
     923             :             }
     924             : 
     925         487 :             const int nCount = nAccumulated - nIAccumulated;
     926         487 :             pDst = static_cast<TYPE *>(rowBuffer) + nIAccumulated;
     927             : 
     928         487 :             std::fill(pDst, pDst + nCount, RLEValue);
     929             : 
     930         487 :             nIAccumulated = nAccumulated;
     931             :         }
     932             :     }
     933             : 
     934         192 :     return CE_None;
     935             : }
     936             : 
     937         391 : CPLErr MMRBand::GetBlockData(void *rowBuffer, size_t nCompressedRawSize)
     938             : {
     939         391 :     if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BIT)
     940             :     {
     941          16 :         const int nGDALBlockSize = DIV_ROUND_UP(m_nBlockXSize, 8);
     942             : 
     943          16 :         if (VSIFReadL(rowBuffer, nGDALBlockSize, 1, m_pfIMG) != 1)
     944             :         {
     945           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Error while reading band");
     946           0 :             return CE_Failure;
     947             :         }
     948          16 :         return CE_None;
     949             :     }
     950             : 
     951         375 :     if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BYTE ||
     952         332 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_INTEGER ||
     953         304 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_UINTEGER ||
     954         276 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_LONG ||
     955         248 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_REAL ||
     956         220 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_DOUBLE)
     957             :     {
     958         183 :         if (VSIFReadL(rowBuffer, m_nDataTypeSizeBytes, m_nWidth, m_pfIMG) !=
     959         183 :             static_cast<size_t>(m_nWidth))
     960             :         {
     961           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Error while reading band");
     962           0 :             return CE_Failure;
     963             :         }
     964         183 :         return CE_None;
     965             :     }
     966             : 
     967             :     CPLErr eErr;
     968         192 :     switch (m_eMMDataType)
     969             :     {
     970          49 :         case MMDataType::DATATYPE_AND_COMPR_BYTE_RLE:
     971          49 :             eErr = UncompressRow<GByte>(rowBuffer, nCompressedRawSize);
     972          49 :             break;
     973          31 :         case MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE:
     974          31 :             eErr = UncompressRow<GInt16>(rowBuffer, nCompressedRawSize);
     975          31 :             break;
     976          28 :         case MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE:
     977          28 :             eErr = UncompressRow<GUInt16>(rowBuffer, nCompressedRawSize);
     978          28 :             break;
     979          28 :         case MMDataType::DATATYPE_AND_COMPR_LONG_RLE:
     980          28 :             eErr = UncompressRow<GInt32>(rowBuffer, nCompressedRawSize);
     981          28 :             break;
     982          28 :         case MMDataType::DATATYPE_AND_COMPR_REAL_RLE:
     983          28 :             eErr = UncompressRow<float>(rowBuffer, nCompressedRawSize);
     984          28 :             break;
     985          28 :         case MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE:
     986          28 :             eErr = UncompressRow<double>(rowBuffer, nCompressedRawSize);
     987          28 :             break;
     988             : 
     989           0 :         default:
     990           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Error in datatype");
     991           0 :             eErr = CE_Failure;
     992             :     }
     993             : 
     994         192 :     return eErr;
     995             : }  // End of GetBlockData()
     996             : 
     997          86 : int MMRBand::PositionAtStartOfRowOffsetsInFile()
     998             : {
     999             :     vsi_l_offset nFileSize, nHeaderOffset;
    1000             :     char szChain[16];
    1001             :     GInt16 nVersion, nSubVersion;
    1002             :     int nOffsetSize, nOffsetsSectionType;
    1003             : 
    1004          86 :     if (VSIFSeekL(m_pfIMG, 0, SEEK_END))
    1005           0 :         return 0;
    1006             : 
    1007          86 :     nFileSize = VSIFTellL(m_pfIMG);
    1008             : 
    1009          86 :     if (nFileSize < 32)  // Minimum required size
    1010           2 :         return 0;
    1011             : 
    1012          84 :     if (m_nHeight)
    1013             :     {
    1014          84 :         if (nFileSize < static_cast<vsi_l_offset>(32) + m_nHeight + 32)
    1015           0 :             return 0;
    1016             :     }
    1017             : 
    1018          84 :     vsi_l_offset nHeadOffset = nFileSize - 32;
    1019             : 
    1020          84 :     if (VSIFSeekL(m_pfIMG, nHeadOffset, SEEK_SET))  // Reading final header.
    1021           0 :         return 0;
    1022          84 :     if (VSIFReadL(szChain, 16, 1, m_pfIMG) != 1)
    1023           0 :         return 0;
    1024        1428 :     for (int nIndex = 0; nIndex < 16; nIndex++)
    1025             :     {
    1026        1344 :         if (szChain[nIndex] != '\0')
    1027           0 :             return 0;  // Supposed 0's are not 0.
    1028             :     }
    1029             : 
    1030          84 :     if (VSIFReadL(szChain, 8, 1, m_pfIMG) != 1)
    1031           0 :         return 0;
    1032             : 
    1033          84 :     if (strncmp(szChain, "IMG ", 4) || szChain[5] != '.')
    1034           0 :         return 0;
    1035             : 
    1036             :     // Some version checks
    1037          84 :     szChain[7] = 0;
    1038          84 :     if (sscanf(szChain + 6, "%hd", &nSubVersion) != 1 || nSubVersion < 0)
    1039           0 :         return 0;
    1040             : 
    1041          84 :     szChain[5] = 0;
    1042          84 :     if (sscanf(szChain + 4, "%hd", &nVersion) != 1 || nVersion != 1)
    1043           0 :         return 0;
    1044             : 
    1045             :     // Next header to be read
    1046          84 :     if (VSIFReadL(&nHeaderOffset, sizeof(vsi_l_offset), 1, m_pfIMG) != 1)
    1047           0 :         return 0;
    1048             : 
    1049         168 :     std::set<vsi_l_offset> alreadyVisitedOffsets;
    1050             :     bool bRepeat;
    1051          84 :     do
    1052             :     {
    1053          84 :         bRepeat = FALSE;
    1054             : 
    1055          84 :         if (VSIFSeekL(m_pfIMG, nHeaderOffset, SEEK_SET))
    1056           0 :             return 0;
    1057             : 
    1058          84 :         if (VSIFReadL(szChain, 8, 1, m_pfIMG) != 1)
    1059           0 :             return 0;
    1060             : 
    1061          84 :         if (strncmp(szChain, "IMG ", 4) || szChain[5] != '.')
    1062           0 :             return 0;
    1063             : 
    1064          84 :         if (VSIFReadL(&nOffsetsSectionType, 4, 1, m_pfIMG) != 1)
    1065           0 :             return 0;
    1066             : 
    1067          84 :         if (nOffsetsSectionType != 2)  // 2 = row offsets section
    1068             :         {
    1069             :             // This is not the section I am looking for
    1070           0 :             if (VSIFSeekL(m_pfIMG, 8 + 4, SEEK_CUR))
    1071           0 :                 return 0;
    1072             : 
    1073           0 :             if (VSIFReadL(&nHeaderOffset, sizeof(vsi_l_offset), 1, m_pfIMG) !=
    1074             :                 1)
    1075           0 :                 return 0;
    1076             : 
    1077           0 :             if (nHeaderOffset == 0)
    1078           0 :                 return 0;
    1079             : 
    1080           0 :             if (cpl::contains(alreadyVisitedOffsets, nHeaderOffset))
    1081             :             {
    1082           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
    1083             :                          "Error reading offsets. They will be ignored.");
    1084           0 :                 return 0;
    1085             :             }
    1086             : 
    1087           0 :             alreadyVisitedOffsets.insert(nHeaderOffset);
    1088             : 
    1089           0 :             bRepeat = TRUE;
    1090             :         }
    1091             : 
    1092             :     } while (bRepeat);
    1093             : 
    1094          84 :     szChain[7] = 0;
    1095          84 :     if (sscanf(szChain + 6, "%hd", &nSubVersion) != 1 || nSubVersion < 0)
    1096           0 :         return 0;
    1097          84 :     szChain[5] = 0;
    1098          84 :     if (sscanf(szChain + 4, "%hd", &nVersion) != 1 || nVersion != 1)
    1099           0 :         return 0;
    1100             : 
    1101             :     /*
    1102             :         Now I'm in the correct section
    1103             :         -------------------------------
    1104             :         Info about this section:
    1105             :         RasterRLE: minimum size: nHeight*2
    1106             :         Offsets:   minimum size: 32+nHeight*4
    1107             :         Final:     size: 32
    1108             :     */
    1109             : 
    1110          84 :     if (m_nHeight)
    1111             :     {
    1112          84 :         if (nHeaderOffset < static_cast<vsi_l_offset>(m_nHeight) *
    1113          84 :                                 2 ||  // Minimum size of an RLE
    1114          84 :             nFileSize - nHeaderOffset <
    1115          84 :                 static_cast<vsi_l_offset>(32) + m_nHeight +
    1116             :                     32)  // Minimum size of the section in version 1.0
    1117           0 :             return 0;
    1118             :     }
    1119             : 
    1120         168 :     if (VSIFReadL(&nOffsetSize, 4, 1, m_pfIMG) != 1 ||
    1121          84 :         (nOffsetSize != 8 && nOffsetSize != 4 && nOffsetSize != 2 &&
    1122          84 :          nOffsetSize != 1))
    1123           0 :         return 0;
    1124             : 
    1125          84 :     if (m_nHeight)
    1126             :     {
    1127          84 :         if (nFileSize - nHeaderOffset <
    1128          84 :             32 + static_cast<vsi_l_offset>(nOffsetSize) * m_nHeight +
    1129             :                 32)  // No space for this section in this file
    1130           0 :             return 0;
    1131             : 
    1132             :         // I leave the file prepared to read offsets
    1133          84 :         if (VSIFSeekL(m_pfIMG, 16, SEEK_CUR))
    1134           0 :             return 0;
    1135             :     }
    1136             :     else
    1137             :     {
    1138           0 :         if (VSIFSeekL(m_pfIMG, 4, SEEK_CUR))
    1139           0 :             return 0;
    1140             : 
    1141           0 :         if (VSIFSeekL(m_pfIMG, 4, SEEK_CUR))
    1142           0 :             return 0;
    1143             : 
    1144             :         // I leave the file prepared to read offsets
    1145           0 :         if (VSIFSeekL(m_pfIMG, 8, SEEK_CUR))
    1146           0 :             return 0;
    1147             :     }
    1148             : 
    1149             :     // There are offsets!
    1150          84 :     return nOffsetSize;
    1151             : }  // Fi de PositionAtStartOfRowOffsetsInFile()
    1152             : 
    1153             : /************************************************************************/
    1154             : /*                            GetFileSize()                             */
    1155             : /************************************************************************/
    1156             : 
    1157           0 : vsi_l_offset MMRBand::GetFileSize()
    1158             : {
    1159           0 :     if (m_nFileSize == 0)
    1160             :     {
    1161           0 :         const auto nCurPos = VSIFTellL(m_pfIMG);
    1162           0 :         VSIFSeekL(m_pfIMG, 0, SEEK_END);
    1163           0 :         m_nFileSize = VSIFTellL(m_pfIMG);
    1164           0 :         VSIFSeekL(m_pfIMG, nCurPos, SEEK_SET);
    1165             :     }
    1166           0 :     return m_nFileSize;
    1167             : }
    1168             : 
    1169             : /************************************************************************/
    1170             : /*                           FillRowOffsets()                           */
    1171             : /************************************************************************/
    1172             : 
    1173         385 : bool MMRBand::FillRowOffsets()
    1174             : {
    1175             :     vsi_l_offset nStartOffset;
    1176             :     int nIRow;
    1177             :     vsi_l_offset nBytesPerPixelPerNCol;
    1178             :     int nSizeToRead;  // nSizeToRead is not an offset, but the size of the offsets being read
    1179             :                       // directly from the IMG file (can be 1, 2, 4, or 8).
    1180             :     vsi_l_offset nFileByte;
    1181             :     size_t nMaxBytesPerCompressedRow;
    1182         385 :     const int nGDALBlockSize = DIV_ROUND_UP(m_nBlockXSize, 8);
    1183             : 
    1184             :     // If it's filled, there is no need to fill it again
    1185         385 :     if (!m_aFileOffsets.empty())
    1186         214 :         return true;
    1187             : 
    1188             :     // Sanity check to avoid attempting huge memory allocation
    1189         171 :     if (m_nHeight > 1000 * 1000)
    1190             :     {
    1191           0 :         if (GetFileSize() < static_cast<vsi_l_offset>(m_nHeight))
    1192             :         {
    1193           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too small file");
    1194           0 :             return false;
    1195             :         }
    1196             :     }
    1197             : 
    1198             :     try
    1199             :     {
    1200         171 :         m_aFileOffsets.resize(static_cast<size_t>(m_nHeight) + 1);
    1201             :     }
    1202           0 :     catch (const std::bad_alloc &e)
    1203             :     {
    1204           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1205           0 :         return false;
    1206             :     }
    1207             : 
    1208         171 :     switch (m_eMMDataType)
    1209             :     {
    1210           2 :         case MMDataType::DATATYPE_AND_COMPR_BIT:
    1211             : 
    1212             :             // "<=" it's ok. There is space and it's to make easier the programming
    1213          20 :             for (nIRow = 0; nIRow <= m_nHeight; nIRow++)
    1214          18 :                 m_aFileOffsets[nIRow] =
    1215          18 :                     static_cast<vsi_l_offset>(nIRow) * nGDALBlockSize;
    1216           2 :             break;
    1217             : 
    1218          83 :         case MMDataType::DATATYPE_AND_COMPR_BYTE:
    1219             :         case MMDataType::DATATYPE_AND_COMPR_INTEGER:
    1220             :         case MMDataType::DATATYPE_AND_COMPR_UINTEGER:
    1221             :         case MMDataType::DATATYPE_AND_COMPR_LONG:
    1222             :         case MMDataType::DATATYPE_AND_COMPR_REAL:
    1223             :         case MMDataType::DATATYPE_AND_COMPR_DOUBLE:
    1224          83 :             nBytesPerPixelPerNCol =
    1225          83 :                 m_nDataTypeSizeBytes * static_cast<vsi_l_offset>(m_nWidth);
    1226             :             // "<=" it's ok. There is space and it's to make easier the programming
    1227         349 :             for (nIRow = 0; nIRow <= m_nHeight; nIRow++)
    1228         266 :                 m_aFileOffsets[nIRow] =
    1229         266 :                     static_cast<vsi_l_offset>(nIRow) * nBytesPerPixelPerNCol;
    1230          83 :             break;
    1231             : 
    1232          86 :         case MMDataType::DATATYPE_AND_COMPR_BYTE_RLE:
    1233             :         case MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE:
    1234             :         case MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE:
    1235             :         case MMDataType::DATATYPE_AND_COMPR_LONG_RLE:
    1236             :         case MMDataType::DATATYPE_AND_COMPR_REAL_RLE:
    1237             :         case MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE:
    1238             : 
    1239          86 :             nStartOffset = VSIFTellL(m_pfIMG);
    1240             : 
    1241             :             // Let's determine if are there offsets in the file
    1242          86 :             if (0 < (nSizeToRead = PositionAtStartOfRowOffsetsInFile()))
    1243             :             {
    1244             :                 // I have offsets!!
    1245          84 :                 nFileByte = 0L;  // all bits to 0
    1246         264 :                 for (nIRow = 0; nIRow < m_nHeight; nIRow++)
    1247             :                 {
    1248         180 :                     if (VSIFReadL(&nFileByte, nSizeToRead, 1, m_pfIMG) != 1)
    1249           0 :                         return false;
    1250             : 
    1251         180 :                     m_aFileOffsets[nIRow] = nFileByte;
    1252             : 
    1253             :                     // Let's check that the difference between two offsets is in a int range
    1254         180 :                     if (nIRow > 0)
    1255             :                     {
    1256         192 :                         if (m_aFileOffsets[nIRow] <=
    1257          96 :                             m_aFileOffsets[static_cast<size_t>(nIRow) - 1])
    1258           0 :                             return false;
    1259             : 
    1260          96 :                         if (m_aFileOffsets[nIRow] -
    1261          96 :                                 m_aFileOffsets[static_cast<size_t>(nIRow) -
    1262          96 :                                                1] >=
    1263             :                             static_cast<vsi_l_offset>(SIZE_MAX))
    1264           0 :                             return false;
    1265             :                     }
    1266             :                 }
    1267          84 :                 m_aFileOffsets[nIRow] = 0;  // Not reliable
    1268          84 :                 VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
    1269          84 :                 break;
    1270             :             }
    1271             : 
    1272             :             // Not indexed RLE. We create a dynamic indexation
    1273           4 :             if (m_nWidth >
    1274           2 :                 INT_MAX /
    1275           2 :                     (std::max(1, static_cast<int>(m_eMMBytesPerPixel)) + 1))
    1276             :             {
    1277           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too large row: %d",
    1278             :                          m_nWidth);
    1279           0 :                 VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
    1280           0 :                 return false;
    1281             :             }
    1282             : 
    1283           2 :             nMaxBytesPerCompressedRow =
    1284           2 :                 static_cast<int>(m_eMMBytesPerPixel)
    1285           2 :                     ? (m_nWidth * (static_cast<int>(m_eMMBytesPerPixel) + 1))
    1286           0 :                     : (m_nWidth * (1 + 1));
    1287             :             unsigned char *pBuffer;
    1288             : 
    1289           2 :             if (nullptr == (pBuffer = static_cast<unsigned char *>(
    1290           2 :                                 VSI_MALLOC_VERBOSE(nMaxBytesPerCompressedRow))))
    1291             :             {
    1292           0 :                 VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
    1293           0 :                 return false;
    1294             :             }
    1295             : 
    1296           2 :             VSIFSeekL(m_pfIMG, 0, SEEK_SET);
    1297           2 :             m_aFileOffsets[0] = 0;
    1298           8 :             for (nIRow = 0; nIRow < m_nHeight; nIRow++)
    1299             :             {
    1300           6 :                 GetBlockData(pBuffer, SIZE_MAX);
    1301           6 :                 m_aFileOffsets[static_cast<size_t>(nIRow) + 1] =
    1302           6 :                     VSIFTellL(m_pfIMG);
    1303             :             }
    1304           2 :             VSIFree(pBuffer);
    1305           2 :             VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
    1306           2 :             break;
    1307             : 
    1308           0 :         default:
    1309           0 :             return false;
    1310             :     }  // End of switch (eMMDataType)
    1311         171 :     return true;
    1312             : 
    1313             : }  // End of FillRowOffsets()
    1314             : 
    1315             : /************************************************************************/
    1316             : /*                              Writing part()                          */
    1317             : /* Indexing a compressed file increments the efficiency when reading it */
    1318             : /************************************************************************/
    1319          93 : bool MMRBand::WriteRowOffsets()
    1320             : {
    1321          93 :     if (m_aFileOffsets.empty() || m_nHeight == 0)
    1322           0 :         return true;
    1323             : 
    1324          93 :     VSIFSeekL(m_pfIMG, 0, SEEK_END);
    1325             : 
    1326          93 :     vsi_l_offset nStartOffset = VSIFTellL(m_pfIMG);
    1327          93 :     if (nStartOffset == 0)
    1328           0 :         return false;
    1329             : 
    1330          93 :     if (VSIFWriteL("IMG 1.0\0", sizeof("IMG 1.0"), 1, m_pfIMG) != 1)
    1331           0 :         return false;
    1332             : 
    1333             :     // Type of section
    1334          93 :     int nAux = 2;
    1335          93 :     if (VSIFWriteL(&nAux, 4, 1, m_pfIMG) != 1)
    1336           0 :         return false;
    1337             : 
    1338          93 :     size_t nIndexOffset = static_cast<size_t>(m_nHeight);
    1339             :     int nOffsetSize;
    1340             : 
    1341          93 :     if (m_aFileOffsets[nIndexOffset - 1] < static_cast<vsi_l_offset>(UCHAR_MAX))
    1342          93 :         nOffsetSize = 1;
    1343           0 :     else if (m_aFileOffsets[nIndexOffset - 1] <
    1344             :              static_cast<vsi_l_offset>(USHRT_MAX))
    1345           0 :         nOffsetSize = 2;
    1346           0 :     else if (m_aFileOffsets[nIndexOffset - 1] <
    1347             :              static_cast<vsi_l_offset>(UINT32_MAX))
    1348           0 :         nOffsetSize = 4;
    1349             :     else
    1350           0 :         nOffsetSize = 8;
    1351             : 
    1352          93 :     if (VSIFWriteL(&nOffsetSize, 4, 1, m_pfIMG) != 1)
    1353           0 :         return false;
    1354             : 
    1355          93 :     nAux = 0;
    1356          93 :     if (VSIFWriteL(&nAux, 4, 1, m_pfIMG) != 1)
    1357           0 :         return false;
    1358          93 :     if (VSIFWriteL(&nAux, 4, 1, m_pfIMG) != 1)
    1359           0 :         return false;
    1360             : 
    1361          93 :     vsi_l_offset nUselessOffset = 0;
    1362          93 :     if (VSIFWriteL(&nUselessOffset, sizeof(vsi_l_offset), 1, m_pfIMG) != 1)
    1363           0 :         return false;
    1364             : 
    1365             :     // The main part
    1366          93 :     size_t nSizeTOffsetSize = nOffsetSize;
    1367          93 :     if (nSizeTOffsetSize == sizeof(vsi_l_offset))
    1368             :     {
    1369           0 :         if (VSIFWriteL(&(m_aFileOffsets[0]), sizeof(vsi_l_offset), nIndexOffset,
    1370           0 :                        m_pfIMG) != nIndexOffset)
    1371           0 :             return false;
    1372             :     }
    1373             :     else
    1374             :     {
    1375         437 :         for (nIndexOffset = 0; nIndexOffset < m_aFileOffsets.size() - 1;
    1376             :              nIndexOffset++)
    1377             :         {
    1378         344 :             if (VSIFWriteL(&(m_aFileOffsets[nIndexOffset]), nSizeTOffsetSize, 1,
    1379         344 :                            m_pfIMG) != 1)
    1380           0 :                 return false;
    1381             :         }
    1382             :     }
    1383             : 
    1384             :     // End part of file
    1385          93 :     nAux = 0;
    1386         465 :     for (int nIndex = 0; nIndex < 4; nIndex++)
    1387             :     {
    1388         372 :         if (VSIFWriteL(&nAux, 4, 1, m_pfIMG) != 1)
    1389           0 :             return false;
    1390             :     }
    1391          93 :     if (VSIFWriteL("IMG 1.0\0", sizeof("IMG 1.0"), 1, m_pfIMG) != 1)
    1392           0 :         return false;
    1393             : 
    1394          93 :     if (VSIFWriteL(&nStartOffset, sizeof(vsi_l_offset), 1, m_pfIMG) != 1)
    1395           0 :         return false;
    1396             : 
    1397          93 :     return true;
    1398             : 
    1399             : }  // End of WriteRowOffsets()
    1400             : 
    1401             : // Getting data type from dataset
    1402         167 : bool MMRBand::UpdateDataTypeAndBytesPerPixelFromRasterBand(
    1403             :     GDALRasterBand &papoBand)
    1404             : {
    1405         167 :     switch (papoBand.GetRasterDataType())
    1406             :     {
    1407          46 :         case GDT_Byte:
    1408          46 :             if (m_bIsCompressed)
    1409          35 :                 m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_BYTE_RLE;
    1410             :             else
    1411          11 :                 m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_BYTE;
    1412             : 
    1413          46 :             m_eMMBytesPerPixel =
    1414             :                 MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_BYTE_I_RLE;
    1415          46 :             break;
    1416             : 
    1417          23 :         case GDT_UInt16:
    1418          23 :             if (m_bIsCompressed)
    1419          12 :                 m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE;
    1420             :             else
    1421          11 :                 m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_UINTEGER;
    1422             : 
    1423          23 :             m_eMMBytesPerPixel =
    1424             :                 MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
    1425          23 :             break;
    1426             : 
    1427          24 :         case GDT_Int16:
    1428          24 :             if (m_bIsCompressed)
    1429          13 :                 m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE;
    1430             :             else
    1431          11 :                 m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_INTEGER;
    1432             : 
    1433          24 :             m_eMMBytesPerPixel =
    1434             :                 MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
    1435          24 :             break;
    1436             : 
    1437          23 :         case GDT_Int32:
    1438          23 :             if (m_bIsCompressed)
    1439          12 :                 m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_LONG_RLE;
    1440             :             else
    1441          11 :                 m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_LONG;
    1442             : 
    1443          23 :             m_eMMBytesPerPixel =
    1444             :                 MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
    1445          23 :             break;
    1446             : 
    1447          23 :         case GDT_Float32:
    1448          23 :             if (m_bIsCompressed)
    1449          12 :                 m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_REAL_RLE;
    1450             :             else
    1451          11 :                 m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_REAL;
    1452             : 
    1453          23 :             m_eMMBytesPerPixel =
    1454             :                 MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
    1455          23 :             break;
    1456             : 
    1457          23 :         case GDT_Float64:
    1458          23 :             if (m_bIsCompressed)
    1459          12 :                 m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE;
    1460             :             else
    1461          11 :                 m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_DOUBLE;
    1462             : 
    1463          23 :             m_eMMBytesPerPixel =
    1464             :                 MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_DOUBLE_I_RLE;
    1465          23 :             break;
    1466             : 
    1467           5 :         default:
    1468           5 :             m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_UNDEFINED;
    1469           5 :             m_eMMBytesPerPixel =
    1470             :                 MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_UNDEFINED;
    1471           5 :             m_nDataTypeSizeBytes = 0;
    1472           5 :             m_nWidth = 0;
    1473           5 :             m_nHeight = 0;
    1474           5 :             CPLError(CE_Failure, CPLE_AppDefined,
    1475             :                      "MiraMonRaster: data type unhandled");
    1476           5 :             return false;
    1477             :     }
    1478         162 :     return true;
    1479             : }
    1480             : 
    1481             : // Getting nodata value from metadata
    1482         162 : void MMRBand::UpdateNoDataValueFromRasterBand(GDALRasterBand &papoBand)
    1483             : {
    1484             :     int pbSuccess;
    1485         162 :     m_dfNoData = papoBand.GetNoDataValue(&pbSuccess);
    1486         162 :     m_bNoDataSet = pbSuccess == 1 ? true : false;
    1487         162 : }
    1488             : 
    1489         282 : CPLString MMRBand::GetRELDataType() const
    1490             : {
    1491         282 :     if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BIT)
    1492           0 :         return "bit";
    1493         282 :     if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BYTE ||
    1494         262 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BYTE_RLE)
    1495             :     {
    1496          70 :         if (m_bIsCompressed)
    1497          50 :             return "byte-RLE";
    1498          20 :         return "byte";
    1499             :     }
    1500         212 :     if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_INTEGER ||
    1501         192 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE)
    1502             :     {
    1503          44 :         if (m_bIsCompressed)
    1504          24 :             return "integer-RLE";
    1505          20 :         return "integer";
    1506             :     }
    1507             : 
    1508         168 :     if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_UINTEGER ||
    1509         148 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE)
    1510             :     {
    1511          42 :         if (m_bIsCompressed)
    1512          22 :             return "uinteger-RLE";
    1513          20 :         return "uinteger";
    1514             :     }
    1515             : 
    1516         126 :     if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_LONG ||
    1517         106 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_LONG_RLE)
    1518             :     {
    1519          42 :         if (m_bIsCompressed)
    1520          22 :             return "long-RLE";
    1521          20 :         return "long";
    1522             :     }
    1523             : 
    1524          84 :     if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_REAL ||
    1525          64 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_REAL_RLE)
    1526             :     {
    1527          42 :         if (m_bIsCompressed)
    1528          22 :             return "real-RLE";
    1529          20 :         return "real";
    1530             :     }
    1531             : 
    1532          42 :     if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_DOUBLE ||
    1533          22 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE)
    1534             :     {
    1535          42 :         if (m_bIsCompressed)
    1536          22 :             return "double-RLE";
    1537          20 :         return "double";
    1538             :     }
    1539             : 
    1540           0 :     return "";
    1541             : }
    1542             : 
    1543         476 : void MMRBand::UpdateRowMinMax(const void *pBuffer)
    1544             : {
    1545         476 :     switch (m_eMMDataType)
    1546             :     {
    1547         203 :         case MMDataType::DATATYPE_AND_COMPR_BIT:
    1548             :         case MMDataType::DATATYPE_AND_COMPR_BYTE:
    1549             :         case MMDataType::DATATYPE_AND_COMPR_BYTE_RLE:
    1550         203 :             UpdateRowMinMax<unsigned char>(pBuffer);
    1551         203 :             break;
    1552             : 
    1553          54 :         case MMDataType::DATATYPE_AND_COMPR_UINTEGER:
    1554             :         case MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE:
    1555          54 :             UpdateRowMinMax<GUInt16>(pBuffer);
    1556          54 :             break;
    1557             : 
    1558          57 :         case MMDataType::DATATYPE_AND_COMPR_INTEGER:
    1559             :         case MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE:
    1560             :         case MMDataType::DATATYPE_AND_COMPR_INTEGER_ASCII:
    1561          57 :             UpdateRowMinMax<GInt16>(pBuffer);
    1562          57 :             break;
    1563             : 
    1564          54 :         case MMDataType::DATATYPE_AND_COMPR_LONG:
    1565             :         case MMDataType::DATATYPE_AND_COMPR_LONG_RLE:
    1566          54 :             UpdateRowMinMax<int>(pBuffer);
    1567          54 :             break;
    1568             : 
    1569          54 :         case MMDataType::DATATYPE_AND_COMPR_REAL:
    1570             :         case MMDataType::DATATYPE_AND_COMPR_REAL_RLE:
    1571             :         case MMDataType::DATATYPE_AND_COMPR_REAL_ASCII:
    1572          54 :             UpdateRowMinMax<float>(pBuffer);
    1573          54 :             break;
    1574             : 
    1575          54 :         case MMDataType::DATATYPE_AND_COMPR_DOUBLE:
    1576             :         case MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE:
    1577          54 :             UpdateRowMinMax<double>(pBuffer);
    1578          54 :             break;
    1579             : 
    1580           0 :         default:
    1581           0 :             break;
    1582             :     }
    1583         476 : }
    1584             : 
    1585         159 : bool MMRBand::WriteBandFile(GDALDataset &oSrcDS, int nNBands, int nIBand)
    1586             : {
    1587             : 
    1588         159 :     GDALRasterBand *pRasterBand = oSrcDS.GetRasterBand(nIBand + 1);
    1589         159 :     if (!pRasterBand)
    1590           0 :         return false;
    1591             : 
    1592             :     // Updating variable to suitable values
    1593         159 :     m_nBlockXSize = m_nWidth;
    1594         159 :     m_nBlockYSize = 1;
    1595         159 :     m_nNRowsPerBlock = 1;
    1596             : 
    1597             :     // Opening the RAW file
    1598         159 :     m_pfIMG = VSIFOpenL(m_osBandFileName, "wb");
    1599         159 :     if (!m_pfIMG)
    1600             :     {
    1601           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1602             :                  "Failed to create MiraMon band file `%s' with access 'wb'.",
    1603             :                  m_osBandFileName.c_str());
    1604           0 :         return false;
    1605             :     }
    1606             : 
    1607             :     // Creating index information
    1608         159 :     if (m_bIsCompressed)
    1609             :     {
    1610             :         try
    1611             :         {
    1612          93 :             m_aFileOffsets.resize(static_cast<size_t>(m_nHeight) + 1);
    1613             :         }
    1614           0 :         catch (const std::bad_alloc &e)
    1615             :         {
    1616           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1617           0 :             return false;
    1618             :         }
    1619             :     }
    1620             : 
    1621         159 :     GDALDataType eDT = pRasterBand->GetRasterDataType();
    1622             : 
    1623             :     // Temporary buffer for one scanline
    1624             :     void *pBuffer =
    1625         159 :         VSI_MALLOC2_VERBOSE(m_nWidth, GDALGetDataTypeSizeBytes(eDT));
    1626         159 :     if (pBuffer == nullptr)
    1627           0 :         return false;
    1628             : 
    1629             :     void *pRow =
    1630         159 :         VSI_MALLOC2_VERBOSE(m_nWidth, (GDALGetDataTypeSizeBytes(eDT) + 1));
    1631         159 :     if (pRow == nullptr)
    1632             :     {
    1633           0 :         VSIFree(pBuffer);
    1634           0 :         return false;
    1635             :     }
    1636             : 
    1637             :     // Loop over each line
    1638         159 :     double dfComplete = nIBand * 1.0 / nNBands;
    1639         159 :     double dfIncr = 1.0 / (nNBands * m_nHeight);
    1640         159 :     if (!m_pfnProgress(dfComplete, nullptr, m_pProgressData))
    1641             :     {
    1642           0 :         VSIFree(pBuffer);
    1643           0 :         VSIFree(pRow);
    1644           0 :         return false;
    1645             :     }
    1646             : 
    1647         159 :     m_bMinSet = false;
    1648         159 :     m_dfMin = std::numeric_limits<double>::max();
    1649         159 :     m_bMaxSet = false;
    1650         159 :     m_dfMax = -std::numeric_limits<double>::max();
    1651             : 
    1652         635 :     for (int iLine = 0; iLine < m_nHeight; ++iLine)
    1653             :     {
    1654             :         // Read one line from the raster band
    1655             :         CPLErr err =
    1656         476 :             pRasterBand->RasterIO(GF_Read, 0, iLine, m_nWidth, 1, pBuffer,
    1657             :                                   m_nWidth, 1, eDT, 0, 0, nullptr);
    1658             : 
    1659         476 :         if (err != CE_None)
    1660             :         {
    1661           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1662             :                      "Error reading line %d from raster band", iLine);
    1663           0 :             VSIFree(pBuffer);
    1664           0 :             VSIFree(pRow);
    1665           0 :             return false;
    1666             :         }
    1667             : 
    1668             :         // MinMax calculation
    1669         476 :         UpdateRowMinMax(pBuffer);
    1670             : 
    1671         476 :         if (m_bIsCompressed)
    1672         344 :             m_aFileOffsets[iLine] = VSIFTellL(m_pfIMG);
    1673             : 
    1674             :         // Write the line to the MiraMon band file
    1675             :         size_t nWritten, nCompressed;
    1676         476 :         if (m_bIsCompressed)
    1677             :         {
    1678             :             nCompressed =
    1679         344 :                 CompressRowType(m_eMMDataType, pBuffer, m_nWidth, pRow);
    1680         344 :             nWritten = VSIFWriteL(pRow, 1, nCompressed, m_pfIMG);
    1681         344 :             if (nWritten != nCompressed)
    1682             :             {
    1683           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1684             :                          "Failed to write line %d to MiraMon band file", iLine);
    1685           0 :                 VSIFree(pBuffer);
    1686           0 :                 VSIFree(pRow);
    1687           0 :                 return false;
    1688             :             }
    1689             :         }
    1690             :         else
    1691             :         {
    1692         132 :             nWritten = VSIFWriteL(pBuffer, GDALGetDataTypeSizeBytes(eDT),
    1693         132 :                                   m_nWidth, m_pfIMG);
    1694         132 :             if (nWritten != static_cast<size_t>(m_nWidth))
    1695             :             {
    1696           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1697             :                          "Failed to write line %d to MiraMon band file", iLine);
    1698           0 :                 VSIFree(pBuffer);
    1699           0 :                 VSIFree(pRow);
    1700           0 :                 return false;
    1701             :             }
    1702             :         }
    1703             : 
    1704         476 :         dfComplete += dfIncr;
    1705         476 :         if (!m_pfnProgress(dfComplete, nullptr, m_pProgressData))
    1706             :         {
    1707           0 :             VSIFree(pBuffer);
    1708           0 :             VSIFree(pRow);
    1709           0 :             return false;
    1710             :         }
    1711             :     }
    1712         159 :     VSIFree(pBuffer);
    1713         159 :     VSIFree(pRow);
    1714             : 
    1715             :     // Updating min and max values for simbolization
    1716         159 :     m_dfVisuMin = m_dfMin;
    1717         159 :     m_bMinVisuSet = m_bMinSet;
    1718         159 :     m_dfVisuMax = m_dfMax;
    1719         159 :     m_bMaxVisuSet = m_bMaxSet;
    1720             : 
    1721             :     // There is a final part that contain the indexes to every row
    1722         159 :     if (m_bIsCompressed)
    1723             :     {
    1724          93 :         if (WriteRowOffsets() == false)
    1725           0 :             return false;
    1726             :     }
    1727             : 
    1728         159 :     dfComplete = (nIBand + 1.0) / nNBands;
    1729         159 :     if (!m_pfnProgress(dfComplete, nullptr, m_pProgressData))
    1730           0 :         return false;
    1731             : 
    1732         159 :     return true;
    1733             : }
    1734             : 
    1735             : constexpr uint8_t LIMIT = 255;
    1736             : 
    1737             : template <typename T>
    1738         344 : size_t MMRBand::CompressRowTypeTpl(const T *pRow, int nNCol, void *pBufferVoid)
    1739             : {
    1740         344 :     uint8_t *pBuffer = static_cast<uint8_t *>(pBufferVoid);
    1741             : 
    1742         344 :     T tPreviousValue = pRow[0];
    1743         344 :     uint8_t nByteCounter = 0;
    1744         344 :     size_t nRowBytes = 0;
    1745             : 
    1746        2773 :     for (int i = 0; i < nNCol; i++)
    1747             :     {
    1748        2435 :         if (tPreviousValue == pRow[i] && nByteCounter < LIMIT)
    1749        2170 :             nByteCounter++;
    1750             :         else  // I have found a different value or I have reached the limit of the counter
    1751             :         {
    1752         265 :             if (nByteCounter == 1)
    1753             :             {
    1754             :                 //In cas of three consecutive different values, it's more efficient
    1755             :                 // to write them as uncompressed values than as three RLE
    1756         264 :                 if (i + 2 < nNCol && pRow[i] != pRow[i + 1] &&
    1757           6 :                     pRow[i + 1] != pRow[i + 2])
    1758             :                 {
    1759             :                     // Indicates that the following values are
    1760             :                     // uncompressed, and how many of them are there
    1761           6 :                     *pBuffer++ = 0;
    1762           6 :                     uint8_t *pHowManyUncompressed = pBuffer++;
    1763           6 :                     nRowBytes += 2;
    1764             : 
    1765             :                     // Writing first three
    1766           6 :                     memcpy(pBuffer, &tPreviousValue, sizeof(T));
    1767           6 :                     pBuffer += sizeof(T);
    1768             : 
    1769           6 :                     tPreviousValue = pRow[i];
    1770           6 :                     memcpy(pBuffer, &tPreviousValue, sizeof(T));
    1771           6 :                     pBuffer += sizeof(T);
    1772           6 :                     i++;
    1773             : 
    1774           6 :                     tPreviousValue = pRow[i];
    1775           6 :                     memcpy(pBuffer, &tPreviousValue, sizeof(T));
    1776           6 :                     pBuffer += sizeof(T);
    1777             : 
    1778           6 :                     nRowBytes += 3 * sizeof(T);
    1779             : 
    1780             :                     // nByteCounter is now the number of bytes of the three uncompressed values
    1781           6 :                     nByteCounter = 3;
    1782             : 
    1783          12 :                     for (i++; i + 1 < nNCol && nByteCounter < LIMIT;
    1784             :                          i++, nByteCounter++)
    1785             :                     {
    1786           6 :                         if (pRow[i] == pRow[i + 1])
    1787           0 :                             break;
    1788             : 
    1789           6 :                         memcpy(pBuffer, pRow + i, sizeof(T));
    1790           6 :                         pBuffer += sizeof(T);
    1791           6 :                         nRowBytes += sizeof(T);
    1792             :                     }
    1793             : 
    1794           6 :                     if (i + 1 == nNCol && nByteCounter < LIMIT)
    1795             :                     {
    1796           6 :                         *pHowManyUncompressed = ++nByteCounter;
    1797           6 :                         memcpy(pBuffer, pRow + i, sizeof(T));
    1798           6 :                         nRowBytes += sizeof(T);
    1799           6 :                         return nRowBytes;
    1800             :                     }
    1801             : 
    1802           0 :                     *pHowManyUncompressed = nByteCounter;
    1803           0 :                     tPreviousValue = pRow[i];
    1804           0 :                     nByteCounter = 1;
    1805           0 :                     continue;
    1806             :                 }
    1807             :             }
    1808             : 
    1809             :             // Normal RLE
    1810         259 :             *pBuffer++ = nByteCounter;
    1811         259 :             memcpy(pBuffer, &tPreviousValue, sizeof(T));
    1812         259 :             pBuffer += sizeof(T);
    1813         259 :             nRowBytes += 1 + sizeof(T);
    1814             : 
    1815         259 :             tPreviousValue = pRow[i];
    1816         259 :             nByteCounter = 1;
    1817             :         }
    1818             :     }
    1819             : 
    1820             :     // Last element
    1821         338 :     *pBuffer++ = nByteCounter;
    1822             : 
    1823         338 :     memcpy(pBuffer, &tPreviousValue, sizeof(T));
    1824         338 :     nRowBytes += 1 + sizeof(T);
    1825             : 
    1826         338 :     return nRowBytes;
    1827             : }
    1828             : 
    1829         344 : size_t MMRBand::CompressRowType(MMDataType nDataType, const void *pRow,
    1830             :                                 int nNCol, void *pBuffer)
    1831             : {
    1832         344 :     switch (nDataType)
    1833             :     {
    1834         181 :         case MMDataType::DATATYPE_AND_COMPR_BYTE_RLE:
    1835             :         case MMDataType::DATATYPE_AND_COMPR_BYTE:
    1836         181 :             return CompressRowTypeTpl<uint8_t>(
    1837         181 :                 reinterpret_cast<const uint8_t *>(pRow), nNCol, pBuffer);
    1838             : 
    1839          35 :         case MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE:
    1840             :         case MMDataType::DATATYPE_AND_COMPR_INTEGER:
    1841          35 :             return CompressRowTypeTpl<GInt16>(
    1842          35 :                 reinterpret_cast<const GInt16 *>(pRow), nNCol, pBuffer);
    1843             : 
    1844          32 :         case MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE:
    1845             :         case MMDataType::DATATYPE_AND_COMPR_UINTEGER:
    1846          32 :             return CompressRowTypeTpl<GUInt16>(
    1847          32 :                 reinterpret_cast<const GUInt16 *>(pRow), nNCol, pBuffer);
    1848             : 
    1849          32 :         case MMDataType::DATATYPE_AND_COMPR_LONG_RLE:
    1850             :         case MMDataType::DATATYPE_AND_COMPR_LONG:
    1851          32 :             return CompressRowTypeTpl<GInt32>(
    1852          32 :                 reinterpret_cast<const GInt32 *>(pRow), nNCol, pBuffer);
    1853             : 
    1854          32 :         case MMDataType::DATATYPE_AND_COMPR_REAL_RLE:
    1855             :         case MMDataType::DATATYPE_AND_COMPR_REAL:
    1856          32 :             return CompressRowTypeTpl<float>(
    1857          32 :                 reinterpret_cast<const float *>(pRow), nNCol, pBuffer);
    1858             : 
    1859          32 :         case MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE:
    1860             :         case MMDataType::DATATYPE_AND_COMPR_DOUBLE:
    1861          32 :             return CompressRowTypeTpl<double>(
    1862          32 :                 reinterpret_cast<const double *>(pRow), nNCol, pBuffer);
    1863             : 
    1864           0 :         default:
    1865             :             // same treatment than the original
    1866           0 :             return 0;
    1867             :     }
    1868             : }
    1869             : 
    1870         162 : int MMRBand::WriteColorTable(GDALDataset &oSrcDS)
    1871             : {
    1872         162 :     GDALRasterBand *pRasterBand = oSrcDS.GetRasterBand(m_nIBand + 1);
    1873         162 :     if (!pRasterBand)
    1874           0 :         return 0;
    1875             : 
    1876         162 :     m_poCT = pRasterBand->GetColorTable();
    1877         162 :     if (!m_poCT)
    1878             :     {
    1879             :         // Perhaps the RAT contains colors and MiraMon can use them as a palette
    1880         149 :         return WriteColorTableFromRAT(oSrcDS);
    1881             :     }
    1882             : 
    1883          13 :     if (!m_poCT->GetColorEntryCount())
    1884           0 :         return 0;
    1885             : 
    1886             :     // Creating DBF table name
    1887          13 :     if (!cpl::ends_with(m_osBandFileName, pszExtRaster))
    1888           0 :         return 1;
    1889             : 
    1890             :     // Extract .img
    1891          13 :     m_osCTName = m_osBandFileName;
    1892          13 :     m_osCTName.resize(m_osCTName.size() - strlen(".img"));
    1893          13 :     m_osCTName.append("_CT.dbf");
    1894             : 
    1895             :     // Creating DBF
    1896             :     struct MM_DATA_BASE_XP *pBD_XP =
    1897          13 :         MM_CreateDBFHeader(4, MM_JOC_CARAC_UTF8_DBF);
    1898          13 :     if (!pBD_XP)
    1899           0 :         return 1;
    1900             : 
    1901             :     // Assigning DBF table name
    1902          13 :     CPLStrlcpy(pBD_XP->szFileName, m_osCTName, sizeof(pBD_XP->szFileName));
    1903             : 
    1904             :     // Initializing the table
    1905             :     MM_EXT_DBF_N_RECORDS nPaletteColors =
    1906          13 :         static_cast<MM_EXT_DBF_N_RECORDS>(m_poCT->GetColorEntryCount());
    1907          13 :     pBD_XP->nFields = 4;
    1908          13 :     pBD_XP->nRecords = nPaletteColors;
    1909          13 :     pBD_XP->FirstRecordOffset =
    1910          13 :         static_cast<MM_FIRST_RECORD_OFFSET_TYPE>(33 + (pBD_XP->nFields * 32));
    1911          13 :     pBD_XP->CharSet = MM_JOC_CARAC_ANSI_DBASE;
    1912          13 :     pBD_XP->dbf_version = MM_MARCA_DBASE4;
    1913          13 :     pBD_XP->BytesPerRecord = 1;
    1914             : 
    1915          13 :     MM_ACCUMULATED_BYTES_TYPE_DBF nClauSimbolNBytes = 0;
    1916           2 :     do
    1917             :     {
    1918          15 :         nClauSimbolNBytes++;
    1919          15 :         nPaletteColors /= 10;
    1920          15 :     } while (nPaletteColors > 0);
    1921          13 :     nClauSimbolNBytes++;
    1922             : 
    1923             :     // Fields of the DBF table
    1924             :     struct MM_FIELD MMField;
    1925          13 :     MM_InitializeField(&MMField);
    1926          13 :     MMField.FieldType = 'N';
    1927          13 :     CPLStrlcpy(MMField.FieldName, "CLAUSIMBOL", MM_MAX_LON_FIELD_NAME_DBF);
    1928          13 :     MMField.BytesPerField = nClauSimbolNBytes;
    1929          13 :     MM_DuplicateFieldDBXP(pBD_XP->pField, &MMField);
    1930             : 
    1931          13 :     CPLStrlcpy(MMField.FieldName, "R_COLOR", MM_MAX_LON_FIELD_NAME_DBF);
    1932          13 :     MMField.BytesPerField = 3;
    1933          13 :     MM_DuplicateFieldDBXP(pBD_XP->pField + 1, &MMField);
    1934             : 
    1935          13 :     CPLStrlcpy(MMField.FieldName, "G_COLOR", MM_MAX_LON_FIELD_NAME_DBF);
    1936          13 :     MMField.BytesPerField = 3;
    1937          13 :     MM_DuplicateFieldDBXP(pBD_XP->pField + 2, &MMField);
    1938             : 
    1939          13 :     CPLStrlcpy(MMField.FieldName, "B_COLOR", MM_MAX_LON_FIELD_NAME_DBF);
    1940          13 :     MMField.BytesPerField = 3;
    1941          13 :     MM_DuplicateFieldDBXP(pBD_XP->pField + 3, &MMField);
    1942             : 
    1943             :     // Opening the table
    1944          13 :     if (!MM_CreateAndOpenDBFFile(pBD_XP, pBD_XP->szFileName))
    1945             :     {
    1946           0 :         MM_ReleaseDBFHeader(&pBD_XP);
    1947           0 :         return 1;
    1948             :     }
    1949             : 
    1950             :     // Writing records to the table
    1951          13 :     if (0 != VSIFSeekL(pBD_XP->pfDataBase,
    1952          13 :                        static_cast<vsi_l_offset>(pBD_XP->FirstRecordOffset),
    1953             :                        SEEK_SET))
    1954             :     {
    1955           0 :         MM_ReleaseDBFHeader(&pBD_XP);
    1956           0 :         return 1;
    1957             :     }
    1958             : 
    1959             :     GDALColorEntry colorEntry;
    1960             :     int nIColor;
    1961         341 :     for (nIColor = 0; nIColor < static_cast<int>(pBD_XP->nRecords); nIColor++)
    1962             :     {
    1963         328 :         m_poCT->GetColorEntryAsRGB(nIColor, &colorEntry);
    1964             : 
    1965             :         // Deletion flag
    1966         328 :         if (!VSIFPrintfL(pBD_XP->pfDataBase, " "))
    1967             :         {
    1968           0 :             MM_ReleaseDBFHeader(&pBD_XP);
    1969           0 :             return 1;
    1970             :         }
    1971             : 
    1972         328 :         if (!VSIFPrintfL(pBD_XP->pfDataBase, "%*d",
    1973             :                          static_cast<int>(nClauSimbolNBytes), nIColor))
    1974             :         {
    1975           0 :             MM_ReleaseDBFHeader(&pBD_XP);
    1976           0 :             return 1;
    1977             :         }
    1978         328 :         if (colorEntry.c4 == 0)
    1979             :         {
    1980          12 :             if (!VSIFPrintfL(pBD_XP->pfDataBase, "%3d%3d%3d", -1, -1, -1))
    1981             :             {
    1982           0 :                 MM_ReleaseDBFHeader(&pBD_XP);
    1983           0 :                 return 1;
    1984             :             }
    1985             :         }
    1986             :         else
    1987             :         {
    1988         316 :             if (!VSIFPrintfL(pBD_XP->pfDataBase, "%3d%3d%3d",
    1989         316 :                              static_cast<int>(colorEntry.c1),
    1990         316 :                              static_cast<int>(colorEntry.c2),
    1991         316 :                              static_cast<int>(colorEntry.c3)))
    1992             :             {
    1993           0 :                 MM_ReleaseDBFHeader(&pBD_XP);
    1994           0 :                 return 1;
    1995             :             }
    1996             :         }
    1997             :     }
    1998             : 
    1999          13 :     fclose_and_nullify(&pBD_XP->pfDataBase);
    2000          13 :     MM_ReleaseDBFHeader(&pBD_XP);
    2001          13 :     return 0;
    2002             : }
    2003             : 
    2004             : // Writes DBF and REL attribute table in MiraMon format
    2005         149 : int MMRBand::WriteColorTableFromRAT(GDALDataset &oSrcDS)
    2006             : {
    2007         149 :     GDALRasterBand *pRasterBand = oSrcDS.GetRasterBand(m_nIBand + 1);
    2008         149 :     if (!pRasterBand)
    2009           0 :         return 0;
    2010             : 
    2011         149 :     m_poRAT = pRasterBand->GetDefaultRAT();
    2012         149 :     if (!m_poRAT)
    2013         135 :         return 0;
    2014             : 
    2015             :     // At least a value and RGB columns are required
    2016          14 :     if (m_poRAT->GetColumnCount() < 4)
    2017          12 :         return 0;
    2018             : 
    2019           2 :     if (!m_poRAT->GetRowCount())
    2020           0 :         return 0;
    2021             : 
    2022             :     // Getting the columns that can be converted to a MiraMon palette
    2023           2 :     int nIValueMinMax = m_poRAT->GetColOfUsage(GFU_MinMax);
    2024           2 :     int nIValueMin = m_poRAT->GetColOfUsage(GFU_Min);
    2025           2 :     int nIValueMax = m_poRAT->GetColOfUsage(GFU_Max);
    2026           2 :     int nIRed = m_poRAT->GetColOfUsage(GFU_Red);
    2027           2 :     int nIGreen = m_poRAT->GetColOfUsage(GFU_Green);
    2028           2 :     int nIBlue = m_poRAT->GetColOfUsage(GFU_Blue);
    2029           2 :     int nIAlpha = m_poRAT->GetColOfUsage(GFU_Alpha);
    2030             : 
    2031             :     // If the RAT has no value type nor RGB colors, MiraMon can't handle that
    2032             :     // as a color table. MinMax or Min and Max are accepted as values.
    2033           2 :     if (!(nIValueMinMax != -1 || (nIValueMin != -1 && nIValueMax != -1) ||
    2034           0 :           nIRed == -1 || nIGreen == -1 || nIBlue == -1))
    2035           0 :         return 0;
    2036             : 
    2037             :     // Creating DBF table name
    2038           2 :     if (!cpl::ends_with(m_osBandFileName, pszExtRaster))
    2039           0 :         return 1;
    2040             : 
    2041             :     // Extract .img
    2042           2 :     m_osCTName = m_osBandFileName;
    2043           2 :     m_osCTName.resize(m_osCTName.size() - strlen(".img"));
    2044           2 :     m_osCTName.append("_CT.dbf");
    2045             : 
    2046             :     // Creating DBF
    2047             :     struct MM_DATA_BASE_XP *pBD_XP =
    2048           2 :         MM_CreateDBFHeader(4, MM_JOC_CARAC_UTF8_DBF);
    2049           2 :     if (!pBD_XP)
    2050           0 :         return 1;
    2051             : 
    2052             :     // Assigning DBF table name
    2053           2 :     CPLStrlcpy(pBD_XP->szFileName, m_osCTName, sizeof(pBD_XP->szFileName));
    2054             : 
    2055             :     // Initializing the table
    2056             :     MM_EXT_DBF_N_RECORDS nPaletteColors =
    2057           2 :         static_cast<MM_EXT_DBF_N_RECORDS>(m_poRAT->GetRowCount());
    2058           2 :     pBD_XP->nFields = 4;
    2059           2 :     pBD_XP->nRecords = nPaletteColors;
    2060           2 :     pBD_XP->FirstRecordOffset =
    2061           2 :         static_cast<MM_FIRST_RECORD_OFFSET_TYPE>(33 + (pBD_XP->nFields * 32));
    2062           2 :     pBD_XP->CharSet = MM_JOC_CARAC_ANSI_DBASE;
    2063           2 :     pBD_XP->dbf_version = MM_MARCA_DBASE4;
    2064           2 :     pBD_XP->BytesPerRecord = 1;
    2065             : 
    2066           2 :     MM_ACCUMULATED_BYTES_TYPE_DBF nClauSimbolNBytes = 0;
    2067           0 :     do
    2068             :     {
    2069           2 :         nClauSimbolNBytes++;
    2070           2 :         nPaletteColors /= 10;
    2071           2 :     } while (nPaletteColors > 0);
    2072           2 :     nClauSimbolNBytes++;
    2073             : 
    2074             :     // Fields of the DBF table
    2075             :     struct MM_FIELD MMField;
    2076           2 :     MM_InitializeField(&MMField);
    2077           2 :     MMField.FieldType = 'N';
    2078           2 :     CPLStrlcpy(MMField.FieldName, "CLAUSIMBOL", MM_MAX_LON_FIELD_NAME_DBF);
    2079           2 :     MMField.BytesPerField = nClauSimbolNBytes;
    2080           2 :     MM_DuplicateFieldDBXP(pBD_XP->pField, &MMField);
    2081             : 
    2082           2 :     CPLStrlcpy(MMField.FieldName, "R_COLOR", MM_MAX_LON_FIELD_NAME_DBF);
    2083           2 :     MMField.BytesPerField = 3;
    2084           2 :     MM_DuplicateFieldDBXP(pBD_XP->pField + 1, &MMField);
    2085             : 
    2086           2 :     CPLStrlcpy(MMField.FieldName, "G_COLOR", MM_MAX_LON_FIELD_NAME_DBF);
    2087           2 :     MMField.BytesPerField = 3;
    2088           2 :     MM_DuplicateFieldDBXP(pBD_XP->pField + 2, &MMField);
    2089             : 
    2090           2 :     CPLStrlcpy(MMField.FieldName, "B_COLOR", MM_MAX_LON_FIELD_NAME_DBF);
    2091           2 :     MMField.BytesPerField = 3;
    2092           2 :     MM_DuplicateFieldDBXP(pBD_XP->pField + 3, &MMField);
    2093             : 
    2094             :     // Opening the table
    2095           2 :     if (!MM_CreateAndOpenDBFFile(pBD_XP, pBD_XP->szFileName))
    2096             :     {
    2097           0 :         MM_ReleaseDBFHeader(&pBD_XP);
    2098           0 :         return 1;
    2099             :     }
    2100             : 
    2101             :     // Writing records to the table
    2102           2 :     if (0 != VSIFSeekL(pBD_XP->pfDataBase,
    2103           2 :                        static_cast<vsi_l_offset>(pBD_XP->FirstRecordOffset),
    2104             :                        SEEK_SET))
    2105             :     {
    2106           0 :         MM_ReleaseDBFHeader(&pBD_XP);
    2107           0 :         return 1;
    2108             :     }
    2109             : 
    2110             :     int nIRow;
    2111          14 :     for (nIRow = 0; nIRow < static_cast<int>(pBD_XP->nRecords); nIRow++)
    2112             :     {
    2113          12 :         if (nIRow > 0 && nIValueMin != -1 && nIValueMax != -1)
    2114             :         {
    2115           5 :             if (m_poRAT->GetValueAsInt(nIRow, nIValueMin) ==
    2116           5 :                 m_poRAT->GetValueAsInt(nIRow, nIValueMax))
    2117           0 :                 break;
    2118             :         }
    2119             :         // Deletion flag
    2120          12 :         if (!VSIFPrintfL(pBD_XP->pfDataBase, " "))
    2121             :         {
    2122           0 :             MM_ReleaseDBFHeader(&pBD_XP);
    2123           0 :             return 1;
    2124             :         }
    2125             : 
    2126          12 :         if (!VSIFPrintfL(pBD_XP->pfDataBase, "%*d",
    2127             :                          static_cast<int>(nClauSimbolNBytes), nIRow))
    2128             :         {
    2129           0 :             MM_ReleaseDBFHeader(&pBD_XP);
    2130           0 :             return 1;
    2131             :         }
    2132          12 :         if (nIAlpha != -1 && m_poRAT->GetValueAsInt(nIRow, nIAlpha) == 0)
    2133             :         {
    2134           2 :             if (!VSIFPrintfL(pBD_XP->pfDataBase, "%3d%3d%3d", -1, -1, -1))
    2135             :             {
    2136           0 :                 MM_ReleaseDBFHeader(&pBD_XP);
    2137           0 :                 return 1;
    2138             :             }
    2139             :         }
    2140             :         else
    2141             :         {
    2142          10 :             if (!VSIFPrintfL(pBD_XP->pfDataBase, "%3d%3d%3d",
    2143          10 :                              m_poRAT->GetValueAsInt(nIRow, nIRed),
    2144          10 :                              m_poRAT->GetValueAsInt(nIRow, nIGreen),
    2145          10 :                              m_poRAT->GetValueAsInt(nIRow, nIBlue)))
    2146             :             {
    2147           0 :                 MM_ReleaseDBFHeader(&pBD_XP);
    2148           0 :                 return 1;
    2149             :             }
    2150             :         }
    2151             :     }
    2152             : 
    2153             :     // Nodata color
    2154           2 :     if (!VSIFPrintfL(pBD_XP->pfDataBase, " "))
    2155             :     {
    2156           0 :         MM_ReleaseDBFHeader(&pBD_XP);
    2157           0 :         return 1;
    2158             :     }
    2159           2 :     if (!VSIFPrintfL(pBD_XP->pfDataBase, "%*s",
    2160             :                      static_cast<int>(nClauSimbolNBytes), ""))
    2161             :     {
    2162           0 :         MM_ReleaseDBFHeader(&pBD_XP);
    2163           0 :         return 1;
    2164             :     }
    2165           2 :     if (!VSIFPrintfL(pBD_XP->pfDataBase, "%3d%3d%3d", -1, -1, -1))
    2166             :     {
    2167           0 :         MM_ReleaseDBFHeader(&pBD_XP);
    2168           0 :         return 1;
    2169             :     }
    2170           2 :     nIRow++;
    2171             : 
    2172           2 :     if (nIRow != static_cast<int>(pBD_XP->nRecords))
    2173             :     {
    2174           2 :         pBD_XP->nRecords = nIRow;
    2175           2 :         MM_WriteNRecordsMMBD_XPFile(pBD_XP);
    2176             :     }
    2177             : 
    2178           2 :     fclose_and_nullify(&pBD_XP->pfDataBase);
    2179           2 :     MM_ReleaseDBFHeader(&pBD_XP);
    2180           2 :     return 0;
    2181             : }
    2182             : 
    2183             : // Writes DBF and REL attribute table in MiraMon format
    2184         162 : int MMRBand::WriteAttributeTable(GDALDataset &oSrcDS)
    2185             : {
    2186         162 :     GDALRasterBand *pRasterBand = oSrcDS.GetRasterBand(m_nIBand + 1);
    2187         162 :     if (!pRasterBand)
    2188           0 :         return 0;
    2189             : 
    2190         162 :     m_poRAT = pRasterBand->GetDefaultRAT();
    2191         162 :     if (!m_poRAT)
    2192         136 :         return 0;
    2193             : 
    2194          26 :     if (!m_poRAT->GetColumnCount())
    2195           0 :         return 0;
    2196             : 
    2197          26 :     if (!m_poRAT->GetRowCount())
    2198           0 :         return 0;
    2199             : 
    2200             :     // Getting the Value column
    2201             :     int nIField;
    2202          26 :     m_osValue = "";
    2203          26 :     int nIValue = m_poRAT->GetColOfUsage(GFU_MinMax);
    2204             :     // If the RAT has no value type, MiraMon can't handle that
    2205             :     // as a RAT
    2206          26 :     if (nIValue == -1)
    2207             :     {
    2208          17 :         nIValue = m_poRAT->GetColOfUsage(GFU_Min);
    2209          17 :         if (nIValue == -1)
    2210             :         {
    2211           8 :             GDALRATFieldType nType = m_poRAT->GetTypeOfCol(0);
    2212           8 :             if (nType == GFT_Integer)
    2213           8 :                 nIValue = 0;
    2214             :             else
    2215           0 :                 return 1;
    2216             :         }
    2217             :     }
    2218             : 
    2219          26 :     m_osValue = m_poRAT->GetNameOfCol(nIValue);
    2220             : 
    2221             :     // Creating DBF table name
    2222          26 :     if (!cpl::ends_with(m_osBandFileName, pszExtRaster))
    2223           0 :         return 1;
    2224             : 
    2225             :     // Extract .img and create DBF file name
    2226          26 :     m_osRATDBFName = m_osBandFileName;
    2227          26 :     m_osRATDBFName.resize(m_osRATDBFName.size() - strlen(".img"));
    2228          26 :     m_osRATDBFName.append("_RAT.dbf");
    2229             : 
    2230             :     // Extract .img and create REL file name
    2231          26 :     m_osRATRELName = m_osBandFileName;
    2232          26 :     m_osRATRELName.resize(m_osRATRELName.size() - strlen(".img"));
    2233          26 :     m_osRATRELName.append("_RAT.rel");
    2234             : 
    2235             :     // Creating DBF
    2236          26 :     int nFields = m_poRAT->GetColumnCount();
    2237             :     struct MM_DATA_BASE_XP *pBD_XP =
    2238          26 :         MM_CreateDBFHeader(nFields, MM_JOC_CARAC_UTF8_DBF);
    2239          26 :     if (!pBD_XP)
    2240           0 :         return 1;
    2241             : 
    2242             :     // Creating a simple REL that allows to MiraMon user to
    2243             :     // document this RAT in case of need.
    2244          52 :     auto pRATRel = std::make_unique<MMRRel>(m_osRATRELName);
    2245          26 :     if (!pRATRel->OpenRELFile("wb"))
    2246             :     {
    2247           0 :         MM_ReleaseDBFHeader(&pBD_XP);
    2248           0 :         return 1;
    2249             :     }
    2250             : 
    2251          26 :     pRATRel->AddSectionStart(SECTION_VERSIO);
    2252          26 :     pRATRel->AddKeyValue(KEY_Vers, "4");
    2253          26 :     pRATRel->AddKeyValue(KEY_SubVers, "3");
    2254          26 :     pRATRel->AddSectionEnd();
    2255             : 
    2256          26 :     pRATRel->AddSectionStart(SECTION_TAULA_PRINCIPAL);
    2257             :     // Get path relative to REL file
    2258          26 :     pRATRel->AddKeyValue(KEY_NomFitxer, CPLGetFilename(m_osRATDBFName));
    2259          26 :     pRATRel->AddKeyValue(KEY_TipusRelacio, "RELACIO_N_1");
    2260          26 :     pRATRel->AddKeyValue("AssociatRel", m_osValue);
    2261          26 :     pRATRel->AddSectionEnd();
    2262             : 
    2263             :     // Assigning DBF table name
    2264          26 :     CPLStrlcpy(pBD_XP->szFileName, m_osRATDBFName, sizeof(pBD_XP->szFileName));
    2265             : 
    2266             :     // Initializing the table
    2267             :     MM_EXT_DBF_N_RECORDS nRecords =
    2268          26 :         static_cast<MM_EXT_DBF_N_RECORDS>(m_poRAT->GetRowCount());
    2269          26 :     pBD_XP->nFields = nFields;
    2270          26 :     pBD_XP->nRecords = nRecords;
    2271          26 :     pBD_XP->FirstRecordOffset =
    2272          26 :         static_cast<MM_FIRST_RECORD_OFFSET_TYPE>(33 + (pBD_XP->nFields * 32));
    2273          26 :     pBD_XP->CharSet = MM_JOC_CARAC_ANSI_DBASE;
    2274          26 :     pBD_XP->dbf_version = MM_MARCA_DBASE4;
    2275          26 :     pBD_XP->BytesPerRecord = 1;
    2276             : 
    2277             :     // Fields of the DBF table
    2278             :     struct MM_FIELD MMField;
    2279         109 :     for (nIField = 0; nIField < m_poRAT->GetColumnCount(); nIField++)
    2280             :     {
    2281             :         // DBF part
    2282          83 :         MM_InitializeField(&MMField);
    2283          83 :         CPLStrlcpy(MMField.FieldName, m_poRAT->GetNameOfCol(nIField),
    2284             :                    sizeof(MMField.FieldName));
    2285             : 
    2286          83 :         MMField.BytesPerField = 0;
    2287         581 :         for (int nIRow = 0; nIRow < m_poRAT->GetRowCount(); nIRow++)
    2288             :         {
    2289         498 :             if (m_poRAT->GetTypeOfCol(nIField) == GFT_DateTime)
    2290             :             {
    2291           0 :                 MMField.FieldType = 'D';
    2292           0 :                 MMField.DecimalsIfFloat = 0;
    2293           0 :                 MMField.BytesPerField = MM_MAX_AMPLADA_CAMP_D_DBF;
    2294           0 :                 break;
    2295             :             }
    2296         498 :             else if (m_poRAT->GetTypeOfCol(nIField) == GFT_Boolean)
    2297             :             {
    2298           0 :                 MMField.FieldType = 'L';
    2299           0 :                 MMField.DecimalsIfFloat = 0;
    2300           0 :                 MMField.BytesPerField = 1;
    2301           0 :                 break;
    2302             :             }
    2303             :             else
    2304             :             {
    2305         852 :                 if (m_poRAT->GetTypeOfCol(nIField) == GFT_String ||
    2306         354 :                     m_poRAT->GetTypeOfCol(nIField) == GFT_WKBGeometry)
    2307             :                 {
    2308         144 :                     MMField.FieldType = 'C';
    2309         144 :                     MMField.DecimalsIfFloat = 0;
    2310             :                 }
    2311             :                 else
    2312             :                 {
    2313         354 :                     MMField.FieldType = 'N';
    2314         354 :                     if (m_poRAT->GetTypeOfCol(nIField) == GFT_Real)
    2315             :                     {
    2316         144 :                         MMField.BytesPerField = 20;
    2317         144 :                         MMField.DecimalsIfFloat = MAX_RELIABLE_SF_DOUBLE;
    2318             :                     }
    2319             :                     else
    2320         210 :                         MMField.DecimalsIfFloat = 0;
    2321             :                 }
    2322             :                 char *pszString =
    2323         498 :                     CPLRecode(m_poRAT->GetValueAsString(nIRow, nIField),
    2324             :                               CPL_ENC_UTF8, "CP1252");
    2325             : 
    2326         498 :                 if (strlen(pszString) > MMField.BytesPerField)
    2327          67 :                     MMField.BytesPerField =
    2328             :                         static_cast<MM_BYTES_PER_FIELD_TYPE_DBF>(
    2329          67 :                             strlen(pszString));
    2330             : 
    2331         498 :                 CPLFree(pszString);
    2332             :             }
    2333             :         }
    2334          83 :         MM_DuplicateFieldDBXP(pBD_XP->pField + nIField, &MMField);
    2335             : 
    2336             :         // REL part
    2337         166 :         CPLString osSection = SECTION_TAULA_PRINCIPAL;
    2338          83 :         osSection.append(":");
    2339          83 :         osSection.append(MMField.FieldName);
    2340          83 :         pRATRel->AddSectionStart(osSection);
    2341             : 
    2342          83 :         if (EQUAL(MMField.FieldName, m_osValue))
    2343             :         {
    2344          26 :             pRATRel->AddKeyValue("visible", "0");
    2345          26 :             pRATRel->AddKeyValue(KEY_TractamentVariable, "Categoric");
    2346             :         }
    2347          83 :         pRATRel->AddKeyValue(KEY_descriptor, "");
    2348          83 :         pRATRel->AddSectionEnd();
    2349             :     }
    2350             : 
    2351             :     // Closing the REL
    2352          26 :     pRATRel->CloseRELFile();
    2353             : 
    2354             :     // Opening the table
    2355          26 :     if (!MM_CreateAndOpenDBFFile(pBD_XP, pBD_XP->szFileName))
    2356             :     {
    2357           0 :         MM_ReleaseDBFHeader(&pBD_XP);
    2358           0 :         return 1;
    2359             :     }
    2360             : 
    2361             :     // Writing records to the table
    2362          26 :     if (0 != VSIFSeekL(pBD_XP->pfDataBase,
    2363          26 :                        static_cast<vsi_l_offset>(pBD_XP->FirstRecordOffset),
    2364             :                        SEEK_SET))
    2365             :     {
    2366           0 :         MM_ReleaseDBFHeader(&pBD_XP);
    2367           0 :         return 1;
    2368             :     }
    2369             : 
    2370         182 :     for (int nIRow = 0; nIRow < static_cast<int>(pBD_XP->nRecords); nIRow++)
    2371             :     {
    2372             :         // Deletion flag
    2373         156 :         if (!VSIFPrintfL(pBD_XP->pfDataBase, " "))
    2374             :         {
    2375           0 :             MM_ReleaseDBFHeader(&pBD_XP);
    2376           0 :             return 1;
    2377             :         }
    2378         654 :         for (nIField = 0; nIField < static_cast<int>(pBD_XP->nFields);
    2379             :              nIField++)
    2380             :         {
    2381         498 :             if (m_poRAT->GetTypeOfCol(nIRow) == GFT_DateTime)
    2382             :             {
    2383             :                 char szDate[15];
    2384             :                 const GDALRATDateTime osDT =
    2385           0 :                     m_poRAT->GetValueAsDateTime(nIRow, nIField);
    2386           0 :                 if (osDT.nYear >= 0)
    2387           0 :                     snprintf(szDate, sizeof(szDate), "%04d%02d%02d", osDT.nYear,
    2388           0 :                              osDT.nMonth, osDT.nDay);
    2389             :                 else
    2390           0 :                     snprintf(szDate, sizeof(szDate), "%04d%02d%02d", 0, 0, 0);
    2391             : 
    2392           0 :                 if (pBD_XP->pField[nIField].BytesPerField !=
    2393           0 :                     VSIFWriteL(szDate, 1, pBD_XP->pField[nIField].BytesPerField,
    2394             :                                pBD_XP->pfDataBase))
    2395             :                 {
    2396           0 :                     MM_ReleaseDBFHeader(&pBD_XP);
    2397           0 :                     return 1;
    2398             :                 }
    2399             :             }
    2400         498 :             else if (m_poRAT->GetTypeOfCol(nIRow) == GFT_Boolean)
    2401             :             {
    2402           0 :                 if (pBD_XP->pField[nIField].BytesPerField !=
    2403           0 :                     VSIFWriteL(m_poRAT->GetValueAsBoolean(nIRow, nIField) ? "T"
    2404             :                                                                           : "F",
    2405           0 :                                1, pBD_XP->pField[nIField].BytesPerField,
    2406             :                                pBD_XP->pfDataBase))
    2407             :                 {
    2408           0 :                     MM_ReleaseDBFHeader(&pBD_XP);
    2409           0 :                     return 1;
    2410             :                 }
    2411             :             }
    2412         498 :             else if (m_poRAT->GetTypeOfCol(nIRow) == GFT_WKBGeometry)
    2413             :             {
    2414           0 :                 if (pBD_XP->pField[nIField].BytesPerField !=
    2415           0 :                     VSIFWriteL(m_poRAT->GetValueAsString(nIRow, nIField), 1,
    2416           0 :                                pBD_XP->pField[nIField].BytesPerField,
    2417             :                                pBD_XP->pfDataBase))
    2418             :                 {
    2419           0 :                     MM_ReleaseDBFHeader(&pBD_XP);
    2420           0 :                     return 1;
    2421             :                 }
    2422             :             }
    2423             :             else
    2424             :             {
    2425             :                 char *pszString =
    2426         498 :                     CPLRecode(m_poRAT->GetValueAsString(nIRow, nIField),
    2427             :                               CPL_ENC_UTF8, "CP1252");
    2428             : 
    2429         996 :                 if (pBD_XP->pField[nIField].BytesPerField !=
    2430         498 :                     VSIFWriteL(pszString, 1,
    2431         498 :                                pBD_XP->pField[nIField].BytesPerField,
    2432             :                                pBD_XP->pfDataBase))
    2433             :                 {
    2434           0 :                     MM_ReleaseDBFHeader(&pBD_XP);
    2435           0 :                     return 1;
    2436             :                 }
    2437         498 :                 CPLFree(pszString);
    2438             :             }
    2439             :         }
    2440             :     }
    2441             : 
    2442          26 :     fclose_and_nullify(&pBD_XP->pfDataBase);
    2443          26 :     MM_ReleaseDBFHeader(&pBD_XP);
    2444          26 :     return 0;
    2445             : }

Generated by: LCOV version 1.14