LCOV - code coverage report
Current view: top level - frmts/rmf - rmfdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1355 1626 83.3 %
Date: 2025-05-31 00:00:17 Functions: 52 52 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Raster Matrix Format
       4             :  * Purpose:  Read/write raster files used in GIS "Integratsia"
       5             :  *           (also known as "Panorama" GIS).
       6             :  * Author:   Andrey Kiselev, dron@ak4719.spb.edu
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2005, Andrey Kiselev <dron@ak4719.spb.edu>
      10             :  * Copyright (c) 2007-2012, Even Rouault <even dot rouault at spatialys.com>
      11             :  * Copyright (c) 2023, NextGIS <info@nextgis.com>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : #include <algorithm>
      16             : #include <array>
      17             : #include <limits>
      18             : 
      19             : #include "cpl_string.h"
      20             : #include "gdal_frmts.h"
      21             : #include "ogr_spatialref.h"
      22             : 
      23             : #include "rmfdataset.h"
      24             : 
      25             : #include "cpl_safemaths.hpp"
      26             : 
      27             : constexpr int RMF_DEFAULT_BLOCKXSIZE = 256;
      28             : constexpr int RMF_DEFAULT_BLOCKYSIZE = 256;
      29             : 
      30             : static const char RMF_SigRSW[] = {'R', 'S', 'W', '\0'};
      31             : static const char RMF_SigRSW_BE[] = {'\0', 'W', 'S', 'R'};
      32             : static const char RMF_SigMTW[] = {'M', 'T', 'W', '\0'};
      33             : 
      34             : static const char RMF_UnitsEmpty[] = "";
      35             : static const char RMF_UnitsM[] = "m";
      36             : static const char RMF_UnitsCM[] = "cm";
      37             : static const char RMF_UnitsDM[] = "dm";
      38             : static const char RMF_UnitsMM[] = "mm";
      39             : 
      40             : constexpr double RMF_DEFAULT_SCALE = 10000.0;
      41             : constexpr double RMF_DEFAULT_RESOLUTION = 100.0;
      42             : 
      43             : constexpr const char *MD_VERSION_KEY = "VERSION";
      44             : constexpr const char *MD_NAME_KEY = "NAME";
      45             : constexpr const char *MD_SCALE_KEY = "SCALE";
      46             : constexpr const char *MD_FRAME_KEY = "FRAME";
      47             : 
      48             : constexpr const char *MD_MATH_BASE_MAP_TYPE_KEY = "MATH_BASE.Map type";
      49             : constexpr const char *MD_MATH_BASE_PROJECTION_KEY = "MATH_BASE.Projection";
      50             : 
      51             : constexpr int nMaxFramePointCount = 2048;
      52             : constexpr GInt32 nPolygonType =
      53             :     2147385342;  // 2147385342 magic number for polygon
      54             : 
      55             : /* -------------------------------------------------------------------- */
      56             : /*  Note: Due to the fact that in the early versions of RMF             */
      57             : /*  format the field of the iEPSGCode was marked as a 'reserved',       */
      58             : /*  in the header on its place in many cases garbage values were written.*/
      59             : /*  Most of them can be weeded out by the minimum EPSG code value.      */
      60             : /*                                                                      */
      61             : /*  see: Surveying and Positioning Guidance Note Number 7, part 1       */
      62             : /*       Using the EPSG Geodetic Parameter Dataset p. 22                */
      63             : /*       http://www.epsg.org/Portals/0/373-07-1.pdf                     */
      64             : /* -------------------------------------------------------------------- */
      65             : constexpr GInt32 RMF_EPSG_MIN_CODE = 1024;
      66             : 
      67          74 : static char *RMFUnitTypeToStr(GUInt32 iElevationUnit)
      68             : {
      69          74 :     switch (iElevationUnit)
      70             :     {
      71          68 :         case 0:
      72          68 :             return CPLStrdup(RMF_UnitsM);
      73           0 :         case 1:
      74           0 :             return CPLStrdup(RMF_UnitsDM);
      75           1 :         case 2:
      76           1 :             return CPLStrdup(RMF_UnitsCM);
      77           5 :         case 3:
      78           5 :             return CPLStrdup(RMF_UnitsMM);
      79           0 :         default:
      80           0 :             return CPLStrdup(RMF_UnitsEmpty);
      81             :     }
      82             : }
      83             : 
      84          80 : static GUInt32 RMFStrToUnitType(const char *pszUnit, int *pbSuccess = nullptr)
      85             : {
      86          80 :     if (pbSuccess != nullptr)
      87             :     {
      88           3 :         *pbSuccess = TRUE;
      89             :     }
      90          80 :     if (EQUAL(pszUnit, RMF_UnitsM))
      91           0 :         return 0;
      92          80 :     else if (EQUAL(pszUnit, RMF_UnitsDM))
      93           0 :         return 1;
      94          80 :     else if (EQUAL(pszUnit, RMF_UnitsCM))
      95           1 :         return 2;
      96          79 :     else if (EQUAL(pszUnit, RMF_UnitsMM))
      97           1 :         return 3;
      98             :     else
      99             :     {
     100             :         // There is no 'invalid unit' in RMF format. So meter is default...
     101          78 :         if (pbSuccess != nullptr)
     102             :         {
     103           1 :             *pbSuccess = FALSE;
     104             :         }
     105          78 :         return 0;
     106             :     }
     107             : }
     108             : 
     109             : /************************************************************************/
     110             : /* ==================================================================== */
     111             : /*                            RMFRasterBand                             */
     112             : /* ==================================================================== */
     113             : /************************************************************************/
     114             : 
     115             : /************************************************************************/
     116             : /*                           RMFRasterBand()                            */
     117             : /************************************************************************/
     118             : 
     119         419 : RMFRasterBand::RMFRasterBand(RMFDataset *poDSIn, int nBandIn,
     120         419 :                              GDALDataType eType)
     121         838 :     : nLastTileWidth(poDSIn->GetRasterXSize() % poDSIn->sHeader.nTileWidth),
     122         838 :       nLastTileHeight(poDSIn->GetRasterYSize() % poDSIn->sHeader.nTileHeight),
     123         419 :       nDataSize(GDALGetDataTypeSizeBytes(eType))
     124             : {
     125         419 :     poDS = poDSIn;
     126         419 :     nBand = nBandIn;
     127             : 
     128         419 :     eDataType = eType;
     129         419 :     nBlockXSize = poDSIn->sHeader.nTileWidth;
     130         419 :     nBlockYSize = poDSIn->sHeader.nTileHeight;
     131         419 :     nBlockSize = nBlockXSize * nBlockYSize;
     132         419 :     nBlockBytes = nBlockSize * nDataSize;
     133             : 
     134             : #ifdef DEBUG
     135         419 :     CPLDebug("RMF",
     136             :              "Band %d: tile width is %d, tile height is %d, "
     137             :              " last tile width %u, last tile height %u, "
     138             :              "bytes per pixel is %d, data type size is %d",
     139             :              nBand, nBlockXSize, nBlockYSize, nLastTileWidth, nLastTileHeight,
     140         419 :              poDSIn->sHeader.nBitDepth / 8, nDataSize);
     141             : #endif
     142         419 : }
     143             : 
     144             : /************************************************************************/
     145             : /*                           ~RMFRasterBand()                           */
     146             : /************************************************************************/
     147             : 
     148         838 : RMFRasterBand::~RMFRasterBand()
     149             : {
     150         838 : }
     151             : 
     152             : /************************************************************************/
     153             : /*                             IReadBlock()                             */
     154             : /************************************************************************/
     155             : 
     156         454 : CPLErr RMFRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     157             : {
     158         454 :     RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
     159             : 
     160         454 :     CPLAssert(poGDS != nullptr && nBlockXOff >= 0 && nBlockYOff >= 0 &&
     161             :               pImage != nullptr);
     162             : 
     163         454 :     memset(pImage, 0, nBlockBytes);
     164             : 
     165         454 :     GUInt32 nRawXSize = nBlockXSize;
     166         454 :     GUInt32 nRawYSize = nBlockYSize;
     167             : 
     168         454 :     if (nLastTileWidth &&
     169         381 :         static_cast<GUInt32>(nBlockXOff) == poGDS->nXTiles - 1)
     170         176 :         nRawXSize = nLastTileWidth;
     171             : 
     172         454 :     if (nLastTileHeight &&
     173         365 :         static_cast<GUInt32>(nBlockYOff) == poGDS->nYTiles - 1)
     174         198 :         nRawYSize = nLastTileHeight;
     175             : 
     176         454 :     GUInt32 nRawBytes = nRawXSize * nRawYSize * poGDS->sHeader.nBitDepth / 8;
     177             : 
     178             :     // Direct read optimization
     179         454 :     if (poGDS->nBands == 1 && poGDS->sHeader.nBitDepth >= 8 &&
     180         183 :         nRawXSize == static_cast<GUInt32>(nBlockXSize) &&
     181          71 :         nRawYSize == static_cast<GUInt32>(nBlockYSize))
     182             :     {
     183          67 :         bool bNullTile = false;
     184          67 :         if (CE_None != poGDS->ReadTile(nBlockXOff, nBlockYOff,
     185             :                                        reinterpret_cast<GByte *>(pImage),
     186             :                                        nRawBytes, nRawXSize, nRawYSize,
     187             :                                        bNullTile))
     188             :         {
     189           0 :             CPLError(CE_Failure, CPLE_FileIO,
     190             :                      "Failed to read tile xOff %d yOff %d", nBlockXOff,
     191             :                      nBlockYOff);
     192           0 :             return CE_Failure;
     193             :         }
     194          67 :         if (bNullTile)
     195             :         {
     196             :             const int nChunkSize =
     197           1 :                 std::max(1, GDALGetDataTypeSizeBytes(eDataType));
     198           1 :             const GPtrDiff_t nWords =
     199           1 :                 static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize;
     200           1 :             GDALCopyWords64(&poGDS->sHeader.dfNoData, GDT_Float64, 0, pImage,
     201             :                             eDataType, nChunkSize, nWords);
     202             :         }
     203          67 :         return CE_None;
     204             :     }
     205             : #ifdef DEBUG
     206         387 :     CPLDebug("RMF", "IReadBlock nBand %d, RawSize [%d, %d], Bits %u", nBand,
     207             :              nRawXSize, nRawYSize, poGDS->sHeader.nBitDepth);
     208             : #endif  // DEBUG
     209         387 :     if (poGDS->pabyCurrentTile == nullptr ||
     210         290 :         poGDS->nCurrentTileXOff != nBlockXOff ||
     211         102 :         poGDS->nCurrentTileYOff != nBlockYOff ||
     212         102 :         poGDS->nCurrentTileBytes != nRawBytes)
     213             :     {
     214         285 :         if (poGDS->pabyCurrentTile == nullptr)
     215             :         {
     216          97 :             GUInt32 nMaxTileBytes = poGDS->sHeader.nTileWidth *
     217          97 :                                     poGDS->sHeader.nTileHeight *
     218          97 :                                     poGDS->sHeader.nBitDepth / 8;
     219          97 :             poGDS->pabyCurrentTile = reinterpret_cast<GByte *>(
     220          97 :                 VSIMalloc(std::max(1U, nMaxTileBytes)));
     221          97 :             if (!poGDS->pabyCurrentTile)
     222             :             {
     223           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
     224             :                          "Can't allocate tile block of size %lu.\n%s",
     225             :                          static_cast<unsigned long>(nMaxTileBytes),
     226           0 :                          VSIStrerror(errno));
     227           0 :                 poGDS->nCurrentTileBytes = 0;
     228           0 :                 return CE_Failure;
     229             :             }
     230             :         }
     231             : 
     232         285 :         poGDS->nCurrentTileXOff = nBlockXOff;
     233         285 :         poGDS->nCurrentTileYOff = nBlockYOff;
     234         285 :         poGDS->nCurrentTileBytes = nRawBytes;
     235             : 
     236         285 :         if (CE_None != poGDS->ReadTile(nBlockXOff, nBlockYOff,
     237             :                                        poGDS->pabyCurrentTile, nRawBytes,
     238             :                                        nRawXSize, nRawYSize,
     239         285 :                                        poGDS->bCurrentTileIsNull))
     240             :         {
     241           0 :             CPLError(CE_Failure, CPLE_FileIO,
     242             :                      "Failed to read tile xOff %d yOff %d", nBlockXOff,
     243             :                      nBlockYOff);
     244           0 :             poGDS->nCurrentTileBytes = 0;
     245           0 :             return CE_Failure;
     246             :         }
     247             :     }
     248             : 
     249             :     /* -------------------------------------------------------------------- */
     250             :     /*  Deinterleave pixels from input buffer.                              */
     251             :     /* -------------------------------------------------------------------- */
     252             : 
     253         387 :     if (poGDS->bCurrentTileIsNull)
     254             :     {
     255           0 :         const int nChunkSize = std::max(1, GDALGetDataTypeSizeBytes(eDataType));
     256           0 :         const GPtrDiff_t nWords =
     257           0 :             static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize;
     258           0 :         GDALCopyWords64(&poGDS->sHeader.dfNoData, GDT_Float64, 0, pImage,
     259             :                         eDataType, nChunkSize, nWords);
     260           0 :         return CE_None;
     261             :     }
     262         387 :     else if ((poGDS->eRMFType == RMFT_RSW &&
     263         283 :               (poGDS->sHeader.nBitDepth == 8 ||
     264         271 :                poGDS->sHeader.nBitDepth == 24 ||
     265           9 :                poGDS->sHeader.nBitDepth == 32)) ||
     266         107 :              (poGDS->eRMFType == RMFT_MTW))
     267             :     {
     268         384 :         const size_t nTilePixelSize = poGDS->sHeader.nBitDepth / 8;
     269         384 :         const size_t nTileLineSize = nTilePixelSize * nRawXSize;
     270         384 :         const size_t nBlockLineSize =
     271         384 :             static_cast<size_t>(nDataSize) * nBlockXSize;
     272         384 :         int iDstBand = (poGDS->nBands - nBand);
     273       50423 :         for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
     274             :         {
     275             :             GByte *pabySrc;
     276             :             GByte *pabyDst;
     277       50039 :             pabySrc = poGDS->pabyCurrentTile + iLine * nTileLineSize +
     278       50039 :                       iDstBand * nDataSize;
     279       50039 :             pabyDst =
     280       50039 :                 reinterpret_cast<GByte *>(pImage) + iLine * nBlockLineSize;
     281       50039 :             GDALCopyWords(pabySrc, eDataType, static_cast<int>(nTilePixelSize),
     282       50039 :                           pabyDst, eDataType, static_cast<int>(nDataSize),
     283             :                           nRawXSize);
     284             :         }
     285         384 :         return CE_None;
     286             :     }
     287           3 :     else if (poGDS->eRMFType == RMFT_RSW && poGDS->sHeader.nBitDepth == 16 &&
     288           0 :              poGDS->nBands == 3)
     289             :     {
     290           0 :         const size_t nTilePixelBits = poGDS->sHeader.nBitDepth;
     291           0 :         const size_t nTileLineSize = nTilePixelBits * nRawXSize / 8;
     292           0 :         const size_t nBlockLineSize =
     293           0 :             static_cast<size_t>(nDataSize) * nBlockXSize;
     294             : 
     295           0 :         for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
     296             :         {
     297             :             GUInt16 *pabySrc;
     298             :             GByte *pabyDst;
     299           0 :             pabySrc = reinterpret_cast<GUInt16 *>(poGDS->pabyCurrentTile +
     300           0 :                                                   iLine * nTileLineSize);
     301           0 :             pabyDst =
     302           0 :                 reinterpret_cast<GByte *>(pImage) + iLine * nBlockLineSize;
     303             : 
     304           0 :             for (GUInt32 i = 0; i < nRawXSize; i++)
     305             :             {
     306           0 :                 switch (nBand)
     307             :                 {
     308           0 :                     case 1:
     309           0 :                         pabyDst[i] =
     310           0 :                             static_cast<GByte>((pabySrc[i] & 0x7c00) >> 7);
     311           0 :                         break;
     312           0 :                     case 2:
     313           0 :                         pabyDst[i] =
     314           0 :                             static_cast<GByte>((pabySrc[i] & 0x03e0) >> 2);
     315           0 :                         break;
     316           0 :                     case 3:
     317           0 :                         pabyDst[i] =
     318           0 :                             static_cast<GByte>((pabySrc[i] & 0x1F) << 3);
     319           0 :                         break;
     320           0 :                     default:
     321           0 :                         break;
     322             :                 }
     323             :             }
     324             :         }
     325           0 :         return CE_None;
     326             :     }
     327           3 :     else if (poGDS->eRMFType == RMFT_RSW && poGDS->nBands == 1 &&
     328           3 :              poGDS->sHeader.nBitDepth == 4)
     329             :     {
     330           2 :         if (poGDS->nCurrentTileBytes != (nBlockSize + 1) / 2)
     331             :         {
     332           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     333             :                      "Tile has %d bytes, %d were expected",
     334           0 :                      poGDS->nCurrentTileBytes, (nBlockSize + 1) / 2);
     335           0 :             return CE_Failure;
     336             :         }
     337             : 
     338           2 :         const size_t nTilePixelBits = poGDS->sHeader.nBitDepth;
     339           2 :         const size_t nTileLineSize = nTilePixelBits * nRawXSize / 8;
     340           2 :         const size_t nBlockLineSize =
     341           2 :             static_cast<size_t>(nDataSize) * nBlockXSize;
     342             : 
     343         464 :         for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
     344             :         {
     345             :             GByte *pabySrc;
     346             :             GByte *pabyDst;
     347         462 :             pabySrc = poGDS->pabyCurrentTile + iLine * nTileLineSize;
     348         462 :             pabyDst =
     349         462 :                 reinterpret_cast<GByte *>(pImage) + iLine * nBlockLineSize;
     350      112266 :             for (GUInt32 i = 0; i < nRawXSize; ++i)
     351             :             {
     352      111804 :                 if (i & 0x01)
     353       55902 :                     pabyDst[i] = (*pabySrc++ & 0xF0) >> 4;
     354             :                 else
     355       55902 :                     pabyDst[i] = *pabySrc & 0x0F;
     356             :             }
     357             :         }
     358           2 :         return CE_None;
     359             :     }
     360           1 :     else if (poGDS->eRMFType == RMFT_RSW && poGDS->nBands == 1 &&
     361           1 :              poGDS->sHeader.nBitDepth == 1)
     362             :     {
     363           1 :         if (poGDS->nCurrentTileBytes != (nBlockSize + 7) / 8)
     364             :         {
     365           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     366             :                      "Tile has %d bytes, %d were expected",
     367           0 :                      poGDS->nCurrentTileBytes, (nBlockSize + 7) / 8);
     368           0 :             return CE_Failure;
     369             :         }
     370             : 
     371           1 :         const size_t nTilePixelBits = poGDS->sHeader.nBitDepth;
     372           1 :         const size_t nTileLineSize = nTilePixelBits * nRawXSize / 8;
     373           1 :         const size_t nBlockLineSize =
     374           1 :             static_cast<size_t>(nDataSize) * nBlockXSize;
     375             : 
     376         232 :         for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
     377             :         {
     378             :             GByte *pabySrc;
     379             :             GByte *pabyDst;
     380         231 :             pabySrc = poGDS->pabyCurrentTile + iLine * nTileLineSize;
     381         231 :             pabyDst =
     382         231 :                 reinterpret_cast<GByte *>(pImage) + iLine * nBlockLineSize;
     383             : 
     384       57519 :             for (GUInt32 i = 0; i < nRawXSize; ++i)
     385             :             {
     386       57288 :                 switch (i & 0x7)
     387             :                 {
     388        7161 :                     case 0:
     389        7161 :                         pabyDst[i] = (*pabySrc & 0x80) >> 7;
     390        7161 :                         break;
     391        7161 :                     case 1:
     392        7161 :                         pabyDst[i] = (*pabySrc & 0x40) >> 6;
     393        7161 :                         break;
     394        7161 :                     case 2:
     395        7161 :                         pabyDst[i] = (*pabySrc & 0x20) >> 5;
     396        7161 :                         break;
     397        7161 :                     case 3:
     398        7161 :                         pabyDst[i] = (*pabySrc & 0x10) >> 4;
     399        7161 :                         break;
     400        7161 :                     case 4:
     401        7161 :                         pabyDst[i] = (*pabySrc & 0x08) >> 3;
     402        7161 :                         break;
     403        7161 :                     case 5:
     404        7161 :                         pabyDst[i] = (*pabySrc & 0x04) >> 2;
     405        7161 :                         break;
     406        7161 :                     case 6:
     407        7161 :                         pabyDst[i] = (*pabySrc & 0x02) >> 1;
     408        7161 :                         break;
     409        7161 :                     case 7:
     410        7161 :                         pabyDst[i] = *pabySrc++ & 0x01;
     411        7161 :                         break;
     412           0 :                     default:
     413           0 :                         break;
     414             :                 }
     415             :             }
     416             :         }
     417           1 :         return CE_None;
     418             :     }
     419             : 
     420           0 :     CPLError(CE_Failure, CPLE_AppDefined,
     421             :              "Invalid block data type. BitDepth %d, nBands %d",
     422           0 :              static_cast<int>(poGDS->sHeader.nBitDepth), poGDS->nBands);
     423             : 
     424           0 :     return CE_Failure;
     425             : }
     426             : 
     427             : /************************************************************************/
     428             : /*                            IWriteBlock()                             */
     429             : /************************************************************************/
     430             : 
     431         454 : CPLErr RMFRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     432             : {
     433         454 :     CPLAssert(poDS != nullptr && nBlockXOff >= 0 && nBlockYOff >= 0 &&
     434             :               pImage != nullptr);
     435             : 
     436         454 :     RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
     437             : 
     438             :     // First drop current tile read by IReadBlock
     439         454 :     poGDS->nCurrentTileBytes = 0;
     440             : 
     441         454 :     GUInt32 nRawXSize = nBlockXSize;
     442         454 :     GUInt32 nRawYSize = nBlockYSize;
     443             : 
     444         454 :     if (nLastTileWidth &&
     445         426 :         static_cast<GUInt32>(nBlockXOff) == poGDS->nXTiles - 1)
     446         103 :         nRawXSize = nLastTileWidth;
     447             : 
     448         454 :     if (nLastTileHeight &&
     449         402 :         static_cast<GUInt32>(nBlockYOff) == poGDS->nYTiles - 1)
     450         136 :         nRawYSize = nLastTileHeight;
     451             : 
     452         454 :     const size_t nTilePixelSize =
     453         454 :         static_cast<size_t>(nDataSize) * poGDS->nBands;
     454         454 :     const size_t nTileLineSize = nTilePixelSize * nRawXSize;
     455         454 :     const size_t nTileSize = nTileLineSize * nRawYSize;
     456         454 :     const size_t nBlockLineSize = static_cast<size_t>(nDataSize) * nBlockXSize;
     457             : 
     458             : #ifdef DEBUG
     459         454 :     CPLDebug(
     460             :         "RMF",
     461             :         "IWriteBlock BlockSize [%d, %d], RawSize [%d, %d], size %d, nBand %d",
     462             :         nBlockXSize, nBlockYSize, nRawXSize, nRawYSize,
     463             :         static_cast<int>(nTileSize), nBand);
     464             : #endif  // DEBUG
     465             : 
     466         454 :     if (poGDS->nBands == 1 && nRawXSize == static_cast<GUInt32>(nBlockXSize) &&
     467          24 :         nRawYSize == static_cast<GUInt32>(nBlockYSize))
     468             :     {  // Immediate write
     469          46 :         return poGDS->WriteTile(
     470             :             nBlockXOff, nBlockYOff, reinterpret_cast<GByte *>(pImage),
     471          23 :             static_cast<size_t>(nRawXSize) * nRawYSize * nDataSize, nRawXSize,
     472          23 :             nRawYSize);
     473             :     }
     474             :     else
     475             :     {  // Try to construct full tile in memory and write later
     476         431 :         const GUInt32 nTile = nBlockYOff * poGDS->nXTiles + nBlockXOff;
     477             : 
     478             :         // Find tile
     479         431 :         auto poTile(poGDS->oUnfinishedTiles.find(nTile));
     480         431 :         if (poTile == poGDS->oUnfinishedTiles.end())
     481             :         {
     482         167 :             RMFTileData oTile;
     483         167 :             oTile.oData.resize(nTileSize);
     484             :             // If not found, but exist on disk than read it
     485         167 :             if (poGDS->paiTiles[2 * nTile + 1])
     486             :             {
     487             :                 CPLErr eRes;
     488           0 :                 bool bNullTile = false;
     489             :                 eRes =
     490           0 :                     poGDS->ReadTile(nBlockXOff, nBlockYOff, oTile.oData.data(),
     491             :                                     nTileSize, nRawXSize, nRawYSize, bNullTile);
     492           0 :                 if (eRes != CE_None)
     493             :                 {
     494           0 :                     CPLError(CE_Failure, CPLE_FileIO,
     495             :                              "Can't read block with offset [%d, %d]",
     496             :                              nBlockXOff, nBlockYOff);
     497           0 :                     return eRes;
     498             :                 }
     499             :             }
     500             :             poTile = poGDS->oUnfinishedTiles.insert(
     501         167 :                 poGDS->oUnfinishedTiles.end(), std::make_pair(nTile, oTile));
     502             :         }
     503             : 
     504         431 :         GByte *pabyTileData = poTile->second.oData.data();
     505             : 
     506             :         // Copy new data to a tile
     507         431 :         int iDstBand = (poGDS->nBands - nBand);
     508       79381 :         for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
     509             :         {
     510             :             const GByte *pabySrc;
     511             :             GByte *pabyDst;
     512       78950 :             pabySrc = reinterpret_cast<const GByte *>(pImage) +
     513       78950 :                       iLine * nBlockLineSize;
     514       78950 :             pabyDst =
     515       78950 :                 pabyTileData + iLine * nTileLineSize + iDstBand * nDataSize;
     516       78950 :             GDALCopyWords(pabySrc, eDataType, static_cast<int>(nDataSize),
     517             :                           pabyDst, eDataType, static_cast<int>(nTilePixelSize),
     518             :                           nRawXSize);
     519             :         }
     520         431 :         ++poTile->second.nBandsWritten;
     521             : 
     522             :         // Write to disk if tile is finished
     523         431 :         if (poTile->second.nBandsWritten == poGDS->nBands)
     524             :         {
     525         167 :             poGDS->WriteTile(nBlockXOff, nBlockYOff, pabyTileData, nTileSize,
     526             :                              nRawXSize, nRawYSize);
     527         167 :             poGDS->oUnfinishedTiles.erase(poTile);
     528             :         }
     529             : #ifdef DEBUG
     530         431 :         CPLDebug("RMF", "poGDS->oUnfinishedTiles.size() %d",
     531         431 :                  static_cast<int>(poGDS->oUnfinishedTiles.size()));
     532             : #endif  // DEBUG
     533             :     }
     534             : 
     535         431 :     return CE_None;
     536             : }
     537             : 
     538             : /************************************************************************/
     539             : /*                          GetNoDataValue()                            */
     540             : /************************************************************************/
     541             : 
     542         191 : double RMFRasterBand::GetNoDataValue(int *pbSuccess)
     543             : 
     544             : {
     545         191 :     RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
     546             : 
     547         191 :     if (pbSuccess)
     548         164 :         *pbSuccess = TRUE;
     549             : 
     550         191 :     return poGDS->sHeader.dfNoData;
     551             : }
     552             : 
     553          27 : CPLErr RMFRasterBand::SetNoDataValue(double dfNoData)
     554             : {
     555          27 :     RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
     556             : 
     557          27 :     poGDS->sHeader.dfNoData = dfNoData;
     558          27 :     poGDS->bHeaderDirty = true;
     559             : 
     560          27 :     return CE_None;
     561             : }
     562             : 
     563             : /************************************************************************/
     564             : /*                            GetUnitType()                             */
     565             : /************************************************************************/
     566             : 
     567          10 : const char *RMFRasterBand::GetUnitType()
     568             : 
     569             : {
     570          10 :     RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
     571             : 
     572          10 :     return poGDS->pszUnitType;
     573             : }
     574             : 
     575             : /************************************************************************/
     576             : /*                            SetUnitType()                             */
     577             : /************************************************************************/
     578             : 
     579           3 : CPLErr RMFRasterBand::SetUnitType(const char *pszNewValue)
     580             : 
     581             : {
     582           3 :     RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
     583           3 :     int bSuccess = FALSE;
     584           3 :     int iNewUnit = RMFStrToUnitType(pszNewValue, &bSuccess);
     585             : 
     586           3 :     if (bSuccess)
     587             :     {
     588           2 :         CPLFree(poGDS->pszUnitType);
     589           2 :         poGDS->pszUnitType = CPLStrdup(pszNewValue);
     590           2 :         poGDS->sHeader.iElevationUnit = iNewUnit;
     591           2 :         poGDS->bHeaderDirty = true;
     592           2 :         return CE_None;
     593             :     }
     594             :     else
     595             :     {
     596           1 :         CPLError(CE_Warning, CPLE_NotSupported,
     597             :                  "RMF driver does not support '%s' elevation units. "
     598             :                  "Possible values are: m, dm, cm, mm.",
     599             :                  pszNewValue);
     600           1 :         return CE_Failure;
     601             :     }
     602             : }
     603             : 
     604             : /************************************************************************/
     605             : /*                           GetColorTable()                            */
     606             : /************************************************************************/
     607             : 
     608          28 : GDALColorTable *RMFRasterBand::GetColorTable()
     609             : {
     610          28 :     RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
     611             : 
     612          28 :     return poGDS->poColorTable;
     613             : }
     614             : 
     615             : /************************************************************************/
     616             : /*                           SetColorTable()                            */
     617             : /************************************************************************/
     618             : 
     619           8 : CPLErr RMFRasterBand::SetColorTable(GDALColorTable *poColorTable)
     620             : {
     621           8 :     RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
     622             : 
     623           8 :     if (poColorTable)
     624             :     {
     625           8 :         if (poGDS->eRMFType == RMFT_RSW && poGDS->nBands == 1)
     626             :         {
     627           8 :             if (!poGDS->pabyColorTable)
     628           0 :                 return CE_Failure;
     629             : 
     630             :             GDALColorEntry oEntry;
     631        2056 :             for (GUInt32 i = 0; i < poGDS->nColorTableSize; i++)
     632             :             {
     633        2048 :                 poColorTable->GetColorEntryAsRGB(i, &oEntry);
     634             :                 // Red
     635        2048 :                 poGDS->pabyColorTable[i * 4 + 0] =
     636        2048 :                     static_cast<GByte>(oEntry.c1);
     637             :                 // Green
     638        2048 :                 poGDS->pabyColorTable[i * 4 + 1] =
     639        2048 :                     static_cast<GByte>(oEntry.c2);
     640             :                 // Blue
     641        2048 :                 poGDS->pabyColorTable[i * 4 + 2] =
     642        2048 :                     static_cast<GByte>(oEntry.c3);
     643        2048 :                 poGDS->pabyColorTable[i * 4 + 3] = 0;
     644             :             }
     645             : 
     646           8 :             poGDS->bHeaderDirty = true;
     647             :         }
     648           8 :         return CE_None;
     649             :     }
     650             : 
     651           0 :     return CE_Failure;
     652             : }
     653             : 
     654          59 : int RMFRasterBand::GetOverviewCount()
     655             : {
     656          59 :     RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
     657          59 :     if (poGDS->poOvrDatasets.empty())
     658           0 :         return GDALRasterBand::GetOverviewCount();
     659             :     else
     660          59 :         return static_cast<int>(poGDS->poOvrDatasets.size());
     661             : }
     662             : 
     663          79 : GDALRasterBand *RMFRasterBand::GetOverview(int i)
     664             : {
     665          79 :     RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
     666          79 :     size_t n = static_cast<size_t>(i);
     667          79 :     if (poGDS->poOvrDatasets.empty())
     668           0 :         return GDALRasterBand::GetOverview(i);
     669             :     else
     670          79 :         return poGDS->poOvrDatasets[n]->GetRasterBand(nBand);
     671             : }
     672             : 
     673         694 : CPLErr RMFRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
     674             :                                 int nXSize, int nYSize, void *pData,
     675             :                                 int nBufXSize, int nBufYSize,
     676             :                                 GDALDataType eType, GSpacing nPixelSpace,
     677             :                                 GSpacing nLineSpace,
     678             :                                 GDALRasterIOExtraArg *psExtraArg)
     679             : {
     680         694 :     RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
     681             : 
     682         743 :     if (eRWFlag == GF_Read && poGDS->poCompressData != nullptr &&
     683          49 :         poGDS->poCompressData->oThreadPool.GetThreadCount() > 0)
     684             :     {
     685           9 :         poGDS->poCompressData->oThreadPool.WaitCompletion();
     686             :     }
     687             : 
     688         694 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
     689             :                                      pData, nBufXSize, nBufYSize, eType,
     690         694 :                                      nPixelSpace, nLineSpace, psExtraArg);
     691             : }
     692             : 
     693             : /************************************************************************/
     694             : /*                       GetColorInterpretation()                       */
     695             : /************************************************************************/
     696             : 
     697         104 : GDALColorInterp RMFRasterBand::GetColorInterpretation()
     698             : {
     699         104 :     RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
     700             : 
     701         104 :     if (poGDS->nBands == 3)
     702             :     {
     703          53 :         if (nBand == 1)
     704          18 :             return GCI_RedBand;
     705          35 :         else if (nBand == 2)
     706          17 :             return GCI_GreenBand;
     707          18 :         else if (nBand == 3)
     708          18 :             return GCI_BlueBand;
     709             : 
     710           0 :         return GCI_Undefined;
     711             :     }
     712             : 
     713          51 :     if (poGDS->eRMFType == RMFT_RSW)
     714          28 :         return GCI_PaletteIndex;
     715             : 
     716          23 :     return GCI_Undefined;
     717             : }
     718             : 
     719             : /************************************************************************/
     720             : /* ==================================================================== */
     721             : /*                              RMFDataset                              */
     722             : /* ==================================================================== */
     723             : /************************************************************************/
     724             : 
     725             : /************************************************************************/
     726             : /*                           RMFDataset()                               */
     727             : /************************************************************************/
     728             : 
     729         297 : RMFDataset::RMFDataset() : pszUnitType(CPLStrdup(RMF_UnitsEmpty))
     730             : {
     731         297 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     732         297 :     nBands = 0;
     733         297 :     memset(&sHeader, 0, sizeof(sHeader));
     734         297 :     memset(&sExtHeader, 0, sizeof(sExtHeader));
     735         297 : }
     736             : 
     737             : /************************************************************************/
     738             : /*                            ~RMFDataset()                             */
     739             : /************************************************************************/
     740             : 
     741         594 : RMFDataset::~RMFDataset()
     742             : {
     743         297 :     RMFDataset::FlushCache(true);
     744         404 :     for (size_t n = 0; n != poOvrDatasets.size(); ++n)
     745             :     {
     746         107 :         poOvrDatasets[n]->RMFDataset::FlushCache(true);
     747             :     }
     748             : 
     749         297 :     VSIFree(paiTiles);
     750         297 :     VSIFree(pabyDecompressBuffer);
     751         297 :     VSIFree(pabyCurrentTile);
     752         297 :     CPLFree(pszUnitType);
     753         297 :     CPLFree(pabyColorTable);
     754         297 :     if (poColorTable != nullptr)
     755          68 :         delete poColorTable;
     756             : 
     757         404 :     for (size_t n = 0; n != poOvrDatasets.size(); ++n)
     758             :     {
     759         107 :         GDALClose(poOvrDatasets[n]);
     760             :     }
     761             : 
     762         297 :     if (fp != nullptr && poParentDS == nullptr)
     763             :     {
     764         177 :         VSIFCloseL(fp);
     765             :     }
     766         594 : }
     767             : 
     768             : /************************************************************************/
     769             : /*                          GetGeoTransform()                           */
     770             : /************************************************************************/
     771             : 
     772          47 : CPLErr RMFDataset::GetGeoTransform(double *padfTransform)
     773             : {
     774          47 :     memcpy(padfTransform, adfGeoTransform.data(), sizeof(adfGeoTransform));
     775             : 
     776          47 :     if (sHeader.iGeorefFlag)
     777          41 :         return CE_None;
     778             : 
     779           6 :     return CE_Failure;
     780             : }
     781             : 
     782             : /************************************************************************/
     783             : /*                          SetGeoTransform()                           */
     784             : /************************************************************************/
     785             : 
     786          41 : CPLErr RMFDataset::SetGeoTransform(double *padfTransform)
     787             : {
     788          41 :     memcpy(adfGeoTransform.data(), padfTransform, sizeof(adfGeoTransform));
     789          41 :     sHeader.dfPixelSize = adfGeoTransform[1];
     790          41 :     if (sHeader.dfPixelSize != 0.0)
     791          41 :         sHeader.dfResolution = sHeader.dfScale / sHeader.dfPixelSize;
     792          41 :     sHeader.dfLLX = adfGeoTransform[0];
     793          41 :     sHeader.dfLLY = adfGeoTransform[3] - nRasterYSize * sHeader.dfPixelSize;
     794          41 :     sHeader.iGeorefFlag = 1;
     795             : 
     796          41 :     bHeaderDirty = true;
     797             : 
     798          41 :     return CE_None;
     799             : }
     800             : 
     801             : /************************************************************************/
     802             : /*                          GetSpatialRef()                             */
     803             : /************************************************************************/
     804             : 
     805          34 : const OGRSpatialReference *RMFDataset::GetSpatialRef() const
     806             : 
     807             : {
     808          34 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     809             : }
     810             : 
     811             : /************************************************************************/
     812             : /*                           SetSpatialRef()                            */
     813             : /************************************************************************/
     814             : 
     815          41 : CPLErr RMFDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
     816             : 
     817             : {
     818          41 :     m_oSRS.Clear();
     819          41 :     if (poSRS)
     820          41 :         m_oSRS = *poSRS;
     821             : 
     822          41 :     bHeaderDirty = true;
     823             : 
     824          41 :     return CE_None;
     825             : }
     826             : 
     827             : /************************************************************************/
     828             : /*                           WriteHeader()                              */
     829             : /************************************************************************/
     830             : 
     831         200 : CPLErr RMFDataset::WriteHeader()
     832             : {
     833             :     /* -------------------------------------------------------------------- */
     834             :     /*  Setup projection.                                                   */
     835             :     /* -------------------------------------------------------------------- */
     836         200 :     if (!m_oSRS.IsEmpty())
     837             :     {
     838          52 :         long iProjection = 0;
     839          52 :         long iDatum = 0;
     840          52 :         long iEllips = 0;
     841          52 :         long iZone = 0;
     842          52 :         int iVertCS = 0;
     843          52 :         double adfPrjParams[7] = {};
     844             : 
     845          52 :         m_oSRS.exportToPanorama(&iProjection, &iDatum, &iEllips, &iZone,
     846             :                                 adfPrjParams);
     847          52 :         m_oSRS.exportVertCSToPanorama(&iVertCS);
     848          52 :         sHeader.iProjection = static_cast<GInt32>(iProjection);
     849          52 :         sHeader.dfStdP1 = adfPrjParams[0];
     850          52 :         sHeader.dfStdP2 = adfPrjParams[1];
     851          52 :         sHeader.dfCenterLat = adfPrjParams[2];
     852          52 :         sHeader.dfCenterLong = adfPrjParams[3];
     853          52 :         if (m_oSRS.GetAuthorityName(nullptr) != nullptr &&
     854          62 :             m_oSRS.GetAuthorityCode(nullptr) != nullptr &&
     855          10 :             EQUAL(m_oSRS.GetAuthorityName(nullptr), "EPSG"))
     856             :         {
     857          10 :             sHeader.iEPSGCode = atoi(m_oSRS.GetAuthorityCode(nullptr));
     858             :         }
     859             : 
     860          52 :         sExtHeader.nEllipsoid = static_cast<GInt32>(iEllips);
     861          52 :         sExtHeader.nDatum = static_cast<GInt32>(iDatum);
     862          52 :         sExtHeader.nZone = static_cast<GInt32>(iZone);
     863          52 :         sExtHeader.nVertDatum = static_cast<GInt32>(iVertCS);
     864             : 
     865             :         // Set map type
     866          52 :         auto pszMapType = GetMetadataItem(MD_MATH_BASE_MAP_TYPE_KEY);
     867          52 :         if (pszMapType != nullptr)
     868             :         {
     869          31 :             sHeader.iMapType = static_cast<GInt32>(atoi(pszMapType));
     870             :         }
     871             :     }
     872             : 
     873             : #define RMF_WRITE_LONG(ptr, value, offset)                                     \
     874             :     do                                                                         \
     875             :     {                                                                          \
     876             :         GInt32 iLong = CPL_LSBWORD32(value);                                   \
     877             :         memcpy((ptr) + (offset), &iLong, 4);                                   \
     878             :     } while (false);
     879             : 
     880             : #define RMF_WRITE_ULONG(ptr, value, offset)                                    \
     881             :     do                                                                         \
     882             :     {                                                                          \
     883             :         GUInt32 iULong = CPL_LSBWORD32(value);                                 \
     884             :         memcpy((ptr) + (offset), &iULong, 4);                                  \
     885             :     } while (false);
     886             : 
     887             : #define RMF_WRITE_DOUBLE(ptr, value, offset)                                   \
     888             :     do                                                                         \
     889             :     {                                                                          \
     890             :         double dfDouble = (value);                                             \
     891             :         CPL_LSBPTR64(&dfDouble);                                               \
     892             :         memcpy((ptr) + (offset), &dfDouble, 8);                                \
     893             :     } while (false);
     894             : 
     895             :     // Frame if present
     896         400 :     std::vector<RSWFrameCoord> astFrameCoords;
     897         200 :     auto pszFrameWKT = GetMetadataItem(MD_FRAME_KEY);
     898         200 :     if (pszFrameWKT != nullptr)
     899             :     {
     900           2 :         CPLDebug("RMF", "Write to header frame: %s", pszFrameWKT);
     901           2 :         OGRGeometry *poFrameGeom = nullptr;
     902           2 :         if (OGRGeometryFactory::createFromWkt(pszFrameWKT, nullptr,
     903           2 :                                               &poFrameGeom) == OGRERR_NONE)
     904             :         {
     905           2 :             if (poFrameGeom->getGeometryType() == wkbPolygon)
     906             :             {
     907           2 :                 std::array<double, 6> adfReverseGeoTransform = {0};
     908           2 :                 if (GDALInvGeoTransform(adfGeoTransform.data(),
     909           2 :                                         adfReverseGeoTransform.data()) == TRUE)
     910             :                 {
     911           2 :                     OGRPolygon *poFramePoly = poFrameGeom->toPolygon();
     912           2 :                     if (!poFramePoly->IsEmpty())
     913             :                     {
     914             :                         OGRLinearRing *poFrameRing =
     915           2 :                             poFramePoly->getExteriorRing();
     916          12 :                         for (int i = 0; i < poFrameRing->getNumPoints(); i++)
     917             :                         {
     918          10 :                             int nX = int(adfReverseGeoTransform[0] +
     919          10 :                                          poFrameRing->getX(i) *
     920          10 :                                              adfReverseGeoTransform[1] -
     921          10 :                                          0.5);
     922          10 :                             int nY = int(adfReverseGeoTransform[3] +
     923          10 :                                          poFrameRing->getY(i) *
     924          10 :                                              adfReverseGeoTransform[5] -
     925          10 :                                          0.5);
     926             : 
     927          10 :                             CPLDebug("RMF", "X: %d, Y: %d", nX, nY);
     928             : 
     929          10 :                             astFrameCoords.push_back({nX, nY});
     930             :                         }
     931             :                     }
     932             : 
     933           4 :                     if (astFrameCoords.empty() ||
     934           2 :                         astFrameCoords.size() > nMaxFramePointCount)
     935             :                     {
     936             :                         // CPLError(CE_Warning, CPLE_AppDefined, "Invalid frame WKT: %s", pszFrameWKT);
     937           0 :                         CPLDebug("RMF", "Write to header frame failed: no "
     938             :                                         "points or too many");
     939           0 :                         astFrameCoords.clear();
     940             :                     }
     941             :                     else
     942             :                     {
     943           2 :                         sHeader.nROISize = static_cast<GUInt32>(
     944           2 :                             sizeof(RSWFrame) +
     945             :                             sizeof(RSWFrameCoord) *
     946             :                                 astFrameCoords
     947           2 :                                     .size());  // Set real size and real point count
     948           2 :                         sHeader.iFrameFlag = 0;
     949             :                     }
     950             :                 }
     951             :                 else
     952             :                 {
     953           0 :                     CPLDebug("RMF", "Write to header frame failed: "
     954             :                                     "GDALInvGeoTransform == FALSE");
     955             :                 }
     956             :             }
     957           2 :             OGRGeometryFactory::destroyGeometry(poFrameGeom);
     958             :         }
     959             :         else
     960             :         {
     961           0 :             CPLDebug("RMF", "Write to header frame failed: "
     962             :                             "OGRGeometryFactory::createFromWkt error");
     963             :         }
     964             :     }
     965             : 
     966         200 :     vsi_l_offset iCurrentFileSize(GetLastOffset());
     967         200 :     sHeader.nFileSize0 = GetRMFOffset(iCurrentFileSize, &iCurrentFileSize);
     968         200 :     sHeader.nSize = sHeader.nFileSize0 - GetRMFOffset(nHeaderOffset, nullptr);
     969             :     /* -------------------------------------------------------------------- */
     970             :     /*  Write out the main header.                                          */
     971             :     /* -------------------------------------------------------------------- */
     972             :     {
     973         200 :         GByte abyHeader[RMF_HEADER_SIZE] = {};
     974             : 
     975         200 :         memcpy(abyHeader, sHeader.bySignature, RMF_SIGNATURE_SIZE);
     976         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.iVersion, 4);
     977         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nSize, 8);
     978         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nOvrOffset, 12);
     979         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.iUserID, 16);
     980         200 :         memcpy(abyHeader + 20, sHeader.byName, RMF_NAME_SIZE);
     981         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nBitDepth, 52);
     982         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nHeight, 56);
     983         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nWidth, 60);
     984         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nXTiles, 64);
     985         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nYTiles, 68);
     986         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nTileHeight, 72);
     987         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nTileWidth, 76);
     988         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nLastTileHeight, 80);
     989         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nLastTileWidth, 84);
     990         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nROIOffset, 88);
     991         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nROISize, 92);
     992         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nClrTblOffset, 96);
     993         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nClrTblSize, 100);
     994         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nTileTblOffset, 104);
     995         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nTileTblSize, 108);
     996         200 :         RMF_WRITE_LONG(abyHeader, sHeader.iMapType, 124);
     997         200 :         RMF_WRITE_LONG(abyHeader, sHeader.iProjection, 128);
     998         200 :         RMF_WRITE_LONG(abyHeader, sHeader.iEPSGCode, 132);
     999         200 :         RMF_WRITE_DOUBLE(abyHeader, sHeader.dfScale, 136);
    1000         200 :         RMF_WRITE_DOUBLE(abyHeader, sHeader.dfResolution, 144);
    1001         200 :         RMF_WRITE_DOUBLE(abyHeader, sHeader.dfPixelSize, 152);
    1002         200 :         RMF_WRITE_DOUBLE(abyHeader, sHeader.dfLLY, 160);
    1003         200 :         RMF_WRITE_DOUBLE(abyHeader, sHeader.dfLLX, 168);
    1004         200 :         RMF_WRITE_DOUBLE(abyHeader, sHeader.dfStdP1, 176);
    1005         200 :         RMF_WRITE_DOUBLE(abyHeader, sHeader.dfStdP2, 184);
    1006         200 :         RMF_WRITE_DOUBLE(abyHeader, sHeader.dfCenterLong, 192);
    1007         200 :         RMF_WRITE_DOUBLE(abyHeader, sHeader.dfCenterLat, 200);
    1008         200 :         *(abyHeader + 208) = sHeader.iCompression;
    1009         200 :         *(abyHeader + 209) = sHeader.iMaskType;
    1010         200 :         *(abyHeader + 210) = sHeader.iMaskStep;
    1011         200 :         *(abyHeader + 211) = sHeader.iFrameFlag;
    1012         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nFlagsTblOffset, 212);
    1013         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nFlagsTblSize, 216);
    1014         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nFileSize0, 220);
    1015         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nFileSize1, 224);
    1016         200 :         *(abyHeader + 228) = sHeader.iUnknown;
    1017         200 :         *(abyHeader + 244) = sHeader.iGeorefFlag;
    1018         200 :         *(abyHeader + 245) = sHeader.iInverse;
    1019         200 :         *(abyHeader + 246) = sHeader.iJpegQuality;
    1020         200 :         memcpy(abyHeader + 248, sHeader.abyInvisibleColors,
    1021             :                sizeof(sHeader.abyInvisibleColors));
    1022         200 :         RMF_WRITE_DOUBLE(abyHeader, sHeader.adfElevMinMax[0], 280);
    1023         200 :         RMF_WRITE_DOUBLE(abyHeader, sHeader.adfElevMinMax[1], 288);
    1024         200 :         RMF_WRITE_DOUBLE(abyHeader, sHeader.dfNoData, 296);
    1025         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.iElevationUnit, 304);
    1026         200 :         *(abyHeader + 308) = sHeader.iElevationType;
    1027         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nExtHdrOffset, 312);
    1028         200 :         RMF_WRITE_ULONG(abyHeader, sHeader.nExtHdrSize, 316);
    1029             : 
    1030         200 :         VSIFSeekL(fp, nHeaderOffset, SEEK_SET);
    1031         200 :         VSIFWriteL(abyHeader, 1, sizeof(abyHeader), fp);
    1032             :     }
    1033             : 
    1034             :     /* -------------------------------------------------------------------- */
    1035             :     /*  Write out the extended header.                                      */
    1036             :     /* -------------------------------------------------------------------- */
    1037             : 
    1038         200 :     if (sHeader.nExtHdrOffset && sHeader.nExtHdrSize >= RMF_MIN_EXT_HEADER_SIZE)
    1039             :     {
    1040         200 :         if (sHeader.nExtHdrSize > RMF_MAX_EXT_HEADER_SIZE)
    1041             :         {
    1042           0 :             CPLError(CE_Failure, CPLE_FileIO, "RMF File malformed");
    1043           0 :             return CE_Failure;
    1044             :         }
    1045             :         GByte *pabyExtHeader =
    1046         200 :             reinterpret_cast<GByte *>(CPLCalloc(sHeader.nExtHdrSize, 1));
    1047             : 
    1048         200 :         RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nEllipsoid, 24);
    1049         200 :         RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nVertDatum, 28);
    1050         200 :         RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nDatum, 32);
    1051         200 :         RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nZone, 36);
    1052             : 
    1053         200 :         VSIFSeekL(fp, GetFileOffset(sHeader.nExtHdrOffset), SEEK_SET);
    1054         200 :         VSIFWriteL(pabyExtHeader, 1, sHeader.nExtHdrSize, fp);
    1055             : 
    1056         200 :         CPLFree(pabyExtHeader);
    1057             :     }
    1058             : 
    1059             :     /* -------------------------------------------------------------------- */
    1060             :     /*  Write out the color table.                                          */
    1061             :     /* -------------------------------------------------------------------- */
    1062             : 
    1063         200 :     if (sHeader.nClrTblOffset && sHeader.nClrTblSize)
    1064             :     {
    1065          59 :         VSIFSeekL(fp, GetFileOffset(sHeader.nClrTblOffset), SEEK_SET);
    1066          59 :         VSIFWriteL(pabyColorTable, 1, sHeader.nClrTblSize, fp);
    1067             :     }
    1068             : 
    1069         200 :     if (sHeader.nROIOffset && sHeader.nROISize)
    1070             :     {
    1071             :         GByte *pabyROI =
    1072           2 :             reinterpret_cast<GByte *>(CPLCalloc(sHeader.nROISize, 1));
    1073           2 :         memset(pabyROI, 0, sHeader.nROISize);
    1074             : 
    1075           2 :         auto nPointCount = astFrameCoords.size();
    1076           2 :         size_t offset = 0;
    1077           2 :         RMF_WRITE_LONG(pabyROI, nPolygonType, offset);
    1078           2 :         offset += 4;
    1079           2 :         RMF_WRITE_LONG(pabyROI, static_cast<GInt32>((4 + nPointCount * 2) * 4),
    1080             :                        offset);
    1081           2 :         offset += 4;
    1082           2 :         RMF_WRITE_LONG(pabyROI, 0, offset);
    1083           2 :         offset += 4;
    1084           2 :         RMF_WRITE_LONG(pabyROI, static_cast<GInt32>(32768 * nPointCount * 2),
    1085             :                        offset);
    1086           2 :         offset += 4;
    1087             : 
    1088             :         // Write points
    1089          12 :         for (size_t i = 0; i < nPointCount; i++)
    1090             :         {
    1091          10 :             RMF_WRITE_LONG(pabyROI, astFrameCoords[i].nX, offset);
    1092          10 :             offset += 4;
    1093          10 :             RMF_WRITE_LONG(pabyROI, astFrameCoords[i].nY, offset);
    1094          10 :             offset += 4;
    1095             :         }
    1096             : 
    1097           2 :         VSIFSeekL(fp, GetFileOffset(sHeader.nROIOffset), SEEK_SET);
    1098           2 :         VSIFWriteL(pabyROI, 1, sHeader.nROISize, fp);
    1099             : 
    1100           2 :         CPLFree(pabyROI);
    1101             :     }
    1102             : 
    1103         200 :     if (sHeader.nFlagsTblOffset && sHeader.nFlagsTblSize)
    1104             :     {
    1105             :         GByte *pabyFlagsTbl =
    1106         200 :             reinterpret_cast<GByte *>(CPLCalloc(sHeader.nFlagsTblSize, 1));
    1107             : 
    1108         200 :         if (sHeader.iFrameFlag == 0)
    1109             :         {
    1110             :             // TODO: Add more strictly check for flag value
    1111           2 :             memset(
    1112             :                 pabyFlagsTbl, 2,
    1113             :                 sHeader
    1114           2 :                     .nFlagsTblSize);  // Mark all blocks as intersected with ROI. 0 - complete outside, 1 - complete inside.
    1115             :         }
    1116             :         else
    1117             :         {
    1118         198 :             memset(pabyFlagsTbl, 0, sHeader.nFlagsTblSize);
    1119             :         }
    1120             : 
    1121         200 :         VSIFSeekL(fp, GetFileOffset(sHeader.nFlagsTblOffset), SEEK_SET);
    1122         200 :         VSIFWriteL(pabyFlagsTbl, 1, sHeader.nFlagsTblSize, fp);
    1123             : 
    1124         200 :         CPLFree(pabyFlagsTbl);
    1125             :     }
    1126             : 
    1127             : #undef RMF_WRITE_DOUBLE
    1128             : #undef RMF_WRITE_ULONG
    1129             : #undef RMF_WRITE_LONG
    1130             : 
    1131             :     /* -------------------------------------------------------------------- */
    1132             :     /*  Write out the block table, swap if needed.                          */
    1133             :     /* -------------------------------------------------------------------- */
    1134             : 
    1135         200 :     VSIFSeekL(fp, GetFileOffset(sHeader.nTileTblOffset), SEEK_SET);
    1136             : 
    1137             : #ifdef CPL_MSB
    1138             :     GUInt32 *paiTilesSwapped =
    1139             :         reinterpret_cast<GUInt32 *>(CPLMalloc(sHeader.nTileTblSize));
    1140             :     if (!paiTilesSwapped)
    1141             :         return CE_Failure;
    1142             : 
    1143             :     memcpy(paiTilesSwapped, paiTiles, sHeader.nTileTblSize);
    1144             :     for (GUInt32 i = 0; i < sHeader.nTileTblSize / sizeof(GUInt32); i++)
    1145             :         CPL_SWAP32PTR(paiTilesSwapped + i);
    1146             :     VSIFWriteL(paiTilesSwapped, 1, sHeader.nTileTblSize, fp);
    1147             : 
    1148             :     CPLFree(paiTilesSwapped);
    1149             : #else
    1150         200 :     VSIFWriteL(paiTiles, 1, sHeader.nTileTblSize, fp);
    1151             : #endif
    1152             : 
    1153         200 :     bHeaderDirty = false;
    1154             : 
    1155         200 :     return CE_None;
    1156             : }
    1157             : 
    1158             : /************************************************************************/
    1159             : /*                             FlushCache()                             */
    1160             : /************************************************************************/
    1161             : 
    1162         411 : CPLErr RMFDataset::FlushCache(bool bAtClosing)
    1163             : 
    1164             : {
    1165         411 :     CPLErr eErr = GDALDataset::FlushCache(bAtClosing);
    1166             : 
    1167         549 :     if (poCompressData != nullptr &&
    1168         138 :         poCompressData->oThreadPool.GetThreadCount() > 0)
    1169             :     {
    1170           9 :         poCompressData->oThreadPool.WaitCompletion();
    1171             :     }
    1172             : 
    1173         411 :     if (bAtClosing && eRMFType == RMFT_MTW && eAccess == GA_Update)
    1174             :     {
    1175          77 :         GDALRasterBand *poBand = GetRasterBand(1);
    1176             : 
    1177          77 :         if (poBand)
    1178             :         {
    1179             :             // ComputeRasterMinMax can setup error in case of dataset full
    1180             :             // from NoData values, but it  makes no sense here.
    1181          77 :             CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    1182          77 :             poBand->ComputeRasterMinMax(FALSE, sHeader.adfElevMinMax);
    1183          77 :             bHeaderDirty = true;
    1184             :         }
    1185             :     }
    1186         411 :     if (bHeaderDirty && WriteHeader() != CE_None)
    1187           0 :         eErr = CE_Failure;
    1188         411 :     return eErr;
    1189             : }
    1190             : 
    1191             : /************************************************************************/
    1192             : /*                              Identify()                              */
    1193             : /************************************************************************/
    1194             : 
    1195       58806 : int RMFDataset::Identify(GDALOpenInfo *poOpenInfo)
    1196             : 
    1197             : {
    1198       58806 :     if (poOpenInfo->pabyHeader == nullptr)
    1199       53675 :         return FALSE;
    1200             : 
    1201        5131 :     if (memcmp(poOpenInfo->pabyHeader, RMF_SigRSW, sizeof(RMF_SigRSW)) != 0 &&
    1202        4922 :         memcmp(poOpenInfo->pabyHeader, RMF_SigRSW_BE, sizeof(RMF_SigRSW_BE)) !=
    1203        4910 :             0 &&
    1204        4910 :         memcmp(poOpenInfo->pabyHeader, RMF_SigMTW, sizeof(RMF_SigMTW)) != 0)
    1205        4792 :         return FALSE;
    1206             : 
    1207         339 :     return TRUE;
    1208             : }
    1209             : 
    1210             : /************************************************************************/
    1211             : /*                                Open()                                */
    1212             : /************************************************************************/
    1213             : 
    1214         128 : GDALDataset *RMFDataset::Open(GDALOpenInfo *poOpenInfo)
    1215             : {
    1216         128 :     auto poDS = Open(poOpenInfo, nullptr, 0);
    1217         128 :     if (poDS == nullptr)
    1218             :     {
    1219           9 :         return nullptr;
    1220             :     }
    1221             : 
    1222         119 :     RMFDataset *poCurrentLayer = poDS;
    1223         119 :     RMFDataset *poParent = poCurrentLayer;
    1224         119 :     const int nMaxPossibleOvCount = 64;
    1225             : 
    1226         200 :     for (int iOv = 0; iOv < nMaxPossibleOvCount && poCurrentLayer != nullptr;
    1227             :          ++iOv)
    1228             :     {
    1229         200 :         poCurrentLayer = poCurrentLayer->OpenOverview(poParent, poOpenInfo);
    1230         200 :         if (poCurrentLayer == nullptr)
    1231         119 :             break;
    1232          81 :         poParent->poOvrDatasets.push_back(poCurrentLayer);
    1233             :     }
    1234             : 
    1235         119 :     return poDS;
    1236             : }
    1237             : 
    1238         213 : RMFDataset *RMFDataset::Open(GDALOpenInfo *poOpenInfo, RMFDataset *poParentDS,
    1239             :                              vsi_l_offset nNextHeaderOffset)
    1240             : {
    1241         341 :     if (!Identify(poOpenInfo) ||
    1242         128 :         (poParentDS == nullptr && poOpenInfo->fpL == nullptr))
    1243           2 :         return nullptr;
    1244             : 
    1245             :     /* -------------------------------------------------------------------- */
    1246             :     /*  Create a corresponding GDALDataset.                                 */
    1247             :     /* -------------------------------------------------------------------- */
    1248         211 :     RMFDataset *poDS = new RMFDataset();
    1249             : 
    1250         211 :     if (poParentDS == nullptr)
    1251             :     {
    1252         128 :         poDS->fp = poOpenInfo->fpL;
    1253         128 :         poOpenInfo->fpL = nullptr;
    1254         128 :         poDS->nHeaderOffset = 0;
    1255         128 :         poDS->poParentDS = nullptr;
    1256             :     }
    1257             :     else
    1258             :     {
    1259          83 :         poDS->fp = poParentDS->fp;
    1260          83 :         poDS->poParentDS = poParentDS;
    1261          83 :         poDS->nHeaderOffset = nNextHeaderOffset;
    1262             :     }
    1263         211 :     poDS->eAccess = poOpenInfo->eAccess;
    1264             : 
    1265             : #define RMF_READ_SHORT(ptr, value, offset)                                     \
    1266             :     do                                                                         \
    1267             :     {                                                                          \
    1268             :         memcpy(&(value), reinterpret_cast<GInt16 *>((ptr) + (offset)),         \
    1269             :                sizeof(GInt16));                                                \
    1270             :         if (poDS->bBigEndian)                                                  \
    1271             :         {                                                                      \
    1272             :             CPL_MSBPTR16(&(value));                                            \
    1273             :         }                                                                      \
    1274             :         else                                                                   \
    1275             :         {                                                                      \
    1276             :             CPL_LSBPTR16(&(value));                                            \
    1277             :         }                                                                      \
    1278             :     } while (false);
    1279             : 
    1280             : #define RMF_READ_ULONG(ptr, value, offset)                                     \
    1281             :     do                                                                         \
    1282             :     {                                                                          \
    1283             :         memcpy(&(value), reinterpret_cast<GUInt32 *>((ptr) + (offset)),        \
    1284             :                sizeof(GUInt32));                                               \
    1285             :         if (poDS->bBigEndian)                                                  \
    1286             :         {                                                                      \
    1287             :             CPL_MSBPTR32(&(value));                                            \
    1288             :         }                                                                      \
    1289             :         else                                                                   \
    1290             :         {                                                                      \
    1291             :             CPL_LSBPTR32(&(value));                                            \
    1292             :         }                                                                      \
    1293             :     } while (false);
    1294             : 
    1295             : #define RMF_READ_LONG(ptr, value, offset) RMF_READ_ULONG(ptr, value, offset)
    1296             : 
    1297             : #define RMF_READ_DOUBLE(ptr, value, offset)                                    \
    1298             :     do                                                                         \
    1299             :     {                                                                          \
    1300             :         memcpy(&(value), reinterpret_cast<double *>((ptr) + (offset)),         \
    1301             :                sizeof(double));                                                \
    1302             :         if (poDS->bBigEndian)                                                  \
    1303             :         {                                                                      \
    1304             :             CPL_MSBPTR64(&(value));                                            \
    1305             :         }                                                                      \
    1306             :         else                                                                   \
    1307             :         {                                                                      \
    1308             :             CPL_LSBPTR64(&(value));                                            \
    1309             :         }                                                                      \
    1310             :     } while (false);
    1311             : 
    1312             :     /* -------------------------------------------------------------------- */
    1313             :     /*  Read the main header.                                               */
    1314             :     /* -------------------------------------------------------------------- */
    1315             : 
    1316             :     {
    1317         211 :         GByte abyHeader[RMF_HEADER_SIZE] = {};
    1318             : 
    1319         211 :         VSIFSeekL(poDS->fp, nNextHeaderOffset, SEEK_SET);
    1320         211 :         if (VSIFReadL(abyHeader, 1, sizeof(abyHeader), poDS->fp) !=
    1321             :             sizeof(abyHeader))
    1322             :         {
    1323           0 :             delete poDS;
    1324           0 :             return nullptr;
    1325             :         }
    1326             : 
    1327         211 :         if (memcmp(abyHeader, RMF_SigMTW, sizeof(RMF_SigMTW)) == 0)
    1328             :         {
    1329          76 :             poDS->eRMFType = RMFT_MTW;
    1330             :         }
    1331         135 :         else if (memcmp(abyHeader, RMF_SigRSW_BE, sizeof(RMF_SigRSW_BE)) == 0)
    1332             :         {
    1333           6 :             poDS->eRMFType = RMFT_RSW;
    1334           6 :             poDS->bBigEndian = true;
    1335             :         }
    1336             :         else
    1337             :         {
    1338         129 :             poDS->eRMFType = RMFT_RSW;
    1339             :         }
    1340             : 
    1341         211 :         memcpy(poDS->sHeader.bySignature, abyHeader, RMF_SIGNATURE_SIZE);
    1342         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.iVersion, 4);
    1343         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nSize, 8);
    1344         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nOvrOffset, 12);
    1345         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.iUserID, 16);
    1346         211 :         memcpy(poDS->sHeader.byName, abyHeader + 20,
    1347             :                sizeof(poDS->sHeader.byName));
    1348         211 :         poDS->sHeader.byName[sizeof(poDS->sHeader.byName) - 1] = '\0';
    1349         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nBitDepth, 52);
    1350         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nHeight, 56);
    1351         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nWidth, 60);
    1352         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nXTiles, 64);
    1353         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nYTiles, 68);
    1354         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileHeight, 72);
    1355         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileWidth, 76);
    1356         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nLastTileHeight, 80);
    1357         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nLastTileWidth, 84);
    1358         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nROIOffset, 88);
    1359         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nROISize, 92);
    1360         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nClrTblOffset, 96);
    1361         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nClrTblSize, 100);
    1362         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileTblOffset, 104);
    1363         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileTblSize, 108);
    1364         211 :         RMF_READ_LONG(abyHeader, poDS->sHeader.iMapType, 124);
    1365         211 :         RMF_READ_LONG(abyHeader, poDS->sHeader.iProjection, 128);
    1366         211 :         RMF_READ_LONG(abyHeader, poDS->sHeader.iEPSGCode, 132);
    1367         211 :         RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfScale, 136);
    1368         211 :         RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfResolution, 144);
    1369         211 :         RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfPixelSize, 152);
    1370         211 :         RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfLLY, 160);
    1371         211 :         RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfLLX, 168);
    1372         211 :         RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfStdP1, 176);
    1373         211 :         RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfStdP2, 184);
    1374         211 :         RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfCenterLong, 192);
    1375         211 :         RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfCenterLat, 200);
    1376         211 :         poDS->sHeader.iCompression = *(abyHeader + 208);
    1377         211 :         poDS->sHeader.iMaskType = *(abyHeader + 209);
    1378         211 :         poDS->sHeader.iMaskStep = *(abyHeader + 210);
    1379         211 :         poDS->sHeader.iFrameFlag = *(abyHeader + 211);
    1380         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nFlagsTblOffset, 212);
    1381         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nFlagsTblSize, 216);
    1382         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nFileSize0, 220);
    1383         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nFileSize1, 224);
    1384         211 :         poDS->sHeader.iUnknown = *(abyHeader + 228);
    1385         211 :         poDS->sHeader.iGeorefFlag = *(abyHeader + 244);
    1386         211 :         poDS->sHeader.iInverse = *(abyHeader + 245);
    1387         211 :         poDS->sHeader.iJpegQuality = *(abyHeader + 246);
    1388         211 :         memcpy(poDS->sHeader.abyInvisibleColors, abyHeader + 248,
    1389             :                sizeof(poDS->sHeader.abyInvisibleColors));
    1390         211 :         RMF_READ_DOUBLE(abyHeader, poDS->sHeader.adfElevMinMax[0], 280);
    1391         211 :         RMF_READ_DOUBLE(abyHeader, poDS->sHeader.adfElevMinMax[1], 288);
    1392         211 :         RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfNoData, 296);
    1393             : 
    1394         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.iElevationUnit, 304);
    1395         211 :         poDS->sHeader.iElevationType = *(abyHeader + 308);
    1396         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nExtHdrOffset, 312);
    1397         211 :         RMF_READ_ULONG(abyHeader, poDS->sHeader.nExtHdrSize, 316);
    1398         211 :         poDS->SetMetadataItem(MD_SCALE_KEY,
    1399         211 :                               CPLSPrintf("1 : %u", int(poDS->sHeader.dfScale)));
    1400         211 :         poDS->SetMetadataItem(MD_NAME_KEY,
    1401         211 :                               CPLSPrintf("%s", poDS->sHeader.byName));
    1402         211 :         poDS->SetMetadataItem(MD_VERSION_KEY,
    1403             :                               CPLSPrintf("%d", poDS->sHeader.iVersion));
    1404         211 :         poDS->SetMetadataItem(MD_MATH_BASE_MAP_TYPE_KEY,
    1405             :                               CPLSPrintf("%d", poDS->sHeader.iMapType));
    1406         211 :         poDS->SetMetadataItem(MD_MATH_BASE_PROJECTION_KEY,
    1407             :                               CPLSPrintf("%d", poDS->sHeader.iProjection));
    1408             :     }
    1409             : 
    1410         211 :     if (poDS->sHeader.nTileTblSize % (sizeof(GUInt32) * 2))
    1411             :     {
    1412           0 :         CPLError(CE_Warning, CPLE_IllegalArg, "Invalid tile table size.");
    1413           0 :         delete poDS;
    1414           0 :         return nullptr;
    1415             :     }
    1416             : 
    1417             :     bool bInvalidTileSize;
    1418             :     try
    1419             :     {
    1420             :         uint64_t nMaxTileBits =
    1421         211 :             (CPLSM(static_cast<uint64_t>(2)) *
    1422         422 :              CPLSM(static_cast<uint64_t>(poDS->sHeader.nTileWidth)) *
    1423         422 :              CPLSM(static_cast<uint64_t>(poDS->sHeader.nTileHeight)) *
    1424         422 :              CPLSM(static_cast<uint64_t>(poDS->sHeader.nBitDepth)))
    1425         211 :                 .v();
    1426         211 :         bInvalidTileSize =
    1427             :             (nMaxTileBits >
    1428         211 :              static_cast<uint64_t>(std::numeric_limits<GUInt32>::max()));
    1429             :     }
    1430           0 :     catch (...)
    1431             :     {
    1432           0 :         bInvalidTileSize = true;
    1433             :     }
    1434         211 :     if (bInvalidTileSize)
    1435             :     {
    1436           0 :         CPLError(CE_Warning, CPLE_IllegalArg,
    1437             :                  "Invalid tile size. Width %lu, height %lu, bit depth %lu.",
    1438           0 :                  static_cast<unsigned long>(poDS->sHeader.nTileWidth),
    1439           0 :                  static_cast<unsigned long>(poDS->sHeader.nTileHeight),
    1440           0 :                  static_cast<unsigned long>(poDS->sHeader.nBitDepth));
    1441           0 :         delete poDS;
    1442           0 :         return nullptr;
    1443             :     }
    1444             : 
    1445         211 :     if (poDS->sHeader.nLastTileWidth > poDS->sHeader.nTileWidth ||
    1446         211 :         poDS->sHeader.nLastTileHeight > poDS->sHeader.nTileHeight)
    1447             :     {
    1448           0 :         CPLError(CE_Warning, CPLE_IllegalArg,
    1449             :                  "Invalid last tile size %lu x %lu. "
    1450             :                  "It can't be greater than %lu x %lu.",
    1451           0 :                  static_cast<unsigned long>(poDS->sHeader.nLastTileWidth),
    1452           0 :                  static_cast<unsigned long>(poDS->sHeader.nLastTileHeight),
    1453           0 :                  static_cast<unsigned long>(poDS->sHeader.nTileWidth),
    1454           0 :                  static_cast<unsigned long>(poDS->sHeader.nTileHeight));
    1455           0 :         delete poDS;
    1456           0 :         return nullptr;
    1457             :     }
    1458             : 
    1459         211 :     if (poParentDS != nullptr)
    1460             :     {
    1461          83 :         if (0 != memcmp(poDS->sHeader.bySignature,
    1462          83 :                         poParentDS->sHeader.bySignature, RMF_SIGNATURE_SIZE))
    1463             :         {
    1464           2 :             CPLError(CE_Warning, CPLE_IllegalArg,
    1465             :                      "Invalid subheader signature.");
    1466           2 :             delete poDS;
    1467           2 :             return nullptr;
    1468             :         }
    1469             :     }
    1470             : 
    1471             :     /* -------------------------------------------------------------------- */
    1472             :     /*  Read the extended header.                                           */
    1473             :     /* -------------------------------------------------------------------- */
    1474             : 
    1475         209 :     if (poDS->sHeader.nExtHdrOffset &&
    1476         198 :         poDS->sHeader.nExtHdrSize >= RMF_MIN_EXT_HEADER_SIZE)
    1477             :     {
    1478         198 :         if (poDS->sHeader.nExtHdrSize > RMF_MAX_EXT_HEADER_SIZE)
    1479             :         {
    1480           0 :             CPLError(CE_Failure, CPLE_FileIO, "RMF File malformed");
    1481           0 :             delete poDS;
    1482           0 :             return nullptr;
    1483             :         }
    1484             :         GByte *pabyExtHeader =
    1485         198 :             reinterpret_cast<GByte *>(CPLCalloc(poDS->sHeader.nExtHdrSize, 1));
    1486         198 :         if (pabyExtHeader == nullptr)
    1487             :         {
    1488           0 :             delete poDS;
    1489           0 :             return nullptr;
    1490             :         }
    1491             : 
    1492         198 :         VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nExtHdrOffset),
    1493             :                   SEEK_SET);
    1494         198 :         VSIFReadL(pabyExtHeader, 1, poDS->sHeader.nExtHdrSize, poDS->fp);
    1495             : 
    1496         198 :         RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nEllipsoid, 24);
    1497         198 :         RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nVertDatum, 28);
    1498         198 :         RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nDatum, 32);
    1499         198 :         RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nZone, 36);
    1500             : 
    1501         198 :         CPLFree(pabyExtHeader);
    1502             :     }
    1503             : 
    1504         209 :     CPLDebug("RMF", "Version %d", poDS->sHeader.iVersion);
    1505             : 
    1506         209 :     constexpr GUInt32 ROI_MAX_SIZE_TO_AVOID_EXCESSIVE_RAM_USAGE =
    1507             :         10 * 1024 * 1024;
    1508             : #ifdef DEBUG
    1509             : 
    1510         418 :     CPLDebug("RMF",
    1511             :              "%s image has width %d, height %d, bit depth %d, "
    1512             :              "compression scheme %d, %s, nodata %f",
    1513         209 :              (poDS->eRMFType == RMFT_MTW) ? "MTW" : "RSW", poDS->sHeader.nWidth,
    1514             :              poDS->sHeader.nHeight, poDS->sHeader.nBitDepth,
    1515         209 :              poDS->sHeader.iCompression,
    1516         209 :              poDS->bBigEndian ? "big endian" : "little endian",
    1517             :              poDS->sHeader.dfNoData);
    1518         209 :     CPLDebug("RMF",
    1519             :              "Size %d, offset to overview %#lx, user ID %d, "
    1520             :              "ROI offset %#lx, ROI size %d",
    1521             :              poDS->sHeader.nSize,
    1522         209 :              static_cast<unsigned long>(poDS->sHeader.nOvrOffset),
    1523             :              poDS->sHeader.iUserID,
    1524         209 :              static_cast<unsigned long>(poDS->sHeader.nROIOffset),
    1525             :              poDS->sHeader.nROISize);
    1526         209 :     CPLDebug("RMF", "Map type %d, projection %d, scale %f, resolution %f, ",
    1527             :              poDS->sHeader.iMapType, poDS->sHeader.iProjection,
    1528             :              poDS->sHeader.dfScale, poDS->sHeader.dfResolution);
    1529         209 :     CPLDebug("RMF", "EPSG %d ", poDS->sHeader.iEPSGCode);
    1530         209 :     CPLDebug("RMF", "Georeferencing: pixel size %f, LLX %f, LLY %f",
    1531             :              poDS->sHeader.dfPixelSize, poDS->sHeader.dfLLX,
    1532             :              poDS->sHeader.dfLLY);
    1533             : 
    1534         209 :     if (poDS->sHeader.nROIOffset &&
    1535         116 :         poDS->sHeader.nROISize >= sizeof(RSWFrame) &&
    1536          12 :         poDS->sHeader.nROISize <= ROI_MAX_SIZE_TO_AVOID_EXCESSIVE_RAM_USAGE)
    1537             :     {
    1538             :         GByte *pabyROI = reinterpret_cast<GByte *>(
    1539          12 :             VSI_MALLOC_VERBOSE(poDS->sHeader.nROISize));
    1540          12 :         if (pabyROI == nullptr)
    1541             :         {
    1542           0 :             delete poDS;
    1543           0 :             return nullptr;
    1544             :         }
    1545             : 
    1546          12 :         VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nROIOffset),
    1547             :                   SEEK_SET);
    1548          12 :         if (VSIFReadL(pabyROI, poDS->sHeader.nROISize, 1, poDS->fp) != 1)
    1549             :         {
    1550           0 :             CPLError(CE_Failure, CPLE_FileIO, "Cannot read ROI");
    1551           0 :             CPLFree(pabyROI);
    1552           0 :             delete poDS;
    1553           0 :             return nullptr;
    1554             :         }
    1555             : 
    1556             :         GInt32 nValue;
    1557             : 
    1558          12 :         CPLDebug("RMF", "ROI coordinates:");
    1559             :         /* coverity[tainted_data] */
    1560        3504 :         for (GUInt32 i = 0; i + sizeof(nValue) <= poDS->sHeader.nROISize;
    1561        3492 :              i += sizeof(nValue))
    1562             :         {
    1563        3492 :             RMF_READ_LONG(pabyROI, nValue, i);
    1564        3492 :             CPLDebug("RMF", "%d", nValue);
    1565             :         }
    1566             : 
    1567          12 :         CPLFree(pabyROI);
    1568             :     }
    1569             : #endif
    1570         418 :     if (poDS->sHeader.nWidth >= INT_MAX || poDS->sHeader.nHeight >= INT_MAX ||
    1571         209 :         !GDALCheckDatasetDimensions(poDS->sHeader.nWidth,
    1572         209 :                                     poDS->sHeader.nHeight))
    1573             :     {
    1574           0 :         delete poDS;
    1575           0 :         return nullptr;
    1576             :     }
    1577             : 
    1578             :     /* -------------------------------------------------------------------- */
    1579             :     /*  Read array of blocks offsets/sizes.                                 */
    1580             :     /* -------------------------------------------------------------------- */
    1581             : 
    1582             :     // To avoid useless excessive memory allocation
    1583         209 :     if (poDS->sHeader.nTileTblSize > 1000000)
    1584             :     {
    1585           0 :         VSIFSeekL(poDS->fp, 0, SEEK_END);
    1586           0 :         vsi_l_offset nFileSize = VSIFTellL(poDS->fp);
    1587           0 :         if (nFileSize < poDS->sHeader.nTileTblSize)
    1588             :         {
    1589           0 :             delete poDS;
    1590           0 :             return nullptr;
    1591             :         }
    1592             :     }
    1593             : 
    1594         209 :     if (VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nTileTblOffset),
    1595         209 :                   SEEK_SET) < 0)
    1596             :     {
    1597           0 :         delete poDS;
    1598           0 :         return nullptr;
    1599             :     }
    1600             : 
    1601         209 :     poDS->paiTiles =
    1602         209 :         reinterpret_cast<GUInt32 *>(VSIMalloc(poDS->sHeader.nTileTblSize));
    1603         209 :     if (!poDS->paiTiles)
    1604             :     {
    1605           0 :         delete poDS;
    1606           0 :         return nullptr;
    1607             :     }
    1608             : 
    1609         209 :     if (VSIFReadL(poDS->paiTiles, 1, poDS->sHeader.nTileTblSize, poDS->fp) <
    1610         209 :         poDS->sHeader.nTileTblSize)
    1611             :     {
    1612           9 :         CPLDebug("RMF", "Can't read tiles offsets/sizes table.");
    1613           9 :         delete poDS;
    1614           9 :         return nullptr;
    1615             :     }
    1616             : 
    1617             : #ifdef CPL_MSB
    1618             :     if (!poDS->bBigEndian)
    1619             :     {
    1620             :         for (GUInt32 i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32);
    1621             :              i++)
    1622             :             CPL_SWAP32PTR(poDS->paiTiles + i);
    1623             :     }
    1624             : #else
    1625         200 :     if (poDS->bBigEndian)
    1626             :     {
    1627          30 :         for (GUInt32 i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32);
    1628             :              i++)
    1629          24 :             CPL_SWAP32PTR(poDS->paiTiles + i);
    1630             :     }
    1631             : #endif
    1632             : 
    1633             : #ifdef DEBUG
    1634         200 :     CPLDebug("RMF", "List of block offsets/sizes:");
    1635             : 
    1636         646 :     for (GUInt32 i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32);
    1637         446 :          i += 2)
    1638             :     {
    1639         446 :         CPLDebug("RMF", "    %u / %u", poDS->paiTiles[i],
    1640         446 :                  poDS->paiTiles[i + 1]);
    1641             :     }
    1642             : #endif
    1643             : 
    1644             :     /* -------------------------------------------------------------------- */
    1645             :     /*  Set up essential image parameters.                                  */
    1646             :     /* -------------------------------------------------------------------- */
    1647         200 :     GDALDataType eType = GDT_Byte;
    1648             : 
    1649         200 :     poDS->nRasterXSize = poDS->sHeader.nWidth;
    1650         200 :     poDS->nRasterYSize = poDS->sHeader.nHeight;
    1651             : 
    1652         200 :     if (poDS->eRMFType == RMFT_RSW)
    1653             :     {
    1654         126 :         switch (poDS->sHeader.nBitDepth)
    1655             :         {
    1656          58 :             case 32:
    1657             :             case 24:
    1658             :             case 16:
    1659          58 :                 poDS->nBands = 3;
    1660          58 :                 break;
    1661          68 :             case 1:
    1662             :             case 4:
    1663             :             case 8:
    1664          68 :                 if (poParentDS != nullptr &&
    1665          26 :                     poParentDS->poColorTable != nullptr)
    1666             :                 {
    1667          26 :                     poDS->poColorTable = poParentDS->poColorTable->Clone();
    1668             :                 }
    1669             :                 else
    1670             :                 {
    1671             :                     // Allocate memory for colour table and read it
    1672          42 :                     poDS->nColorTableSize = 1 << poDS->sHeader.nBitDepth;
    1673          42 :                     GUInt32 nExpectedColorTableBytes =
    1674          42 :                         poDS->nColorTableSize * 4;
    1675          42 :                     if (nExpectedColorTableBytes > poDS->sHeader.nClrTblSize)
    1676             :                     {
    1677             :                         // We could probably test for strict equality in
    1678             :                         // the above test ???
    1679           0 :                         CPLDebug("RMF",
    1680             :                                  "Wrong color table size. "
    1681             :                                  "Expected %u, got %u.",
    1682             :                                  nExpectedColorTableBytes,
    1683             :                                  poDS->sHeader.nClrTblSize);
    1684           0 :                         delete poDS;
    1685           0 :                         return nullptr;
    1686             :                     }
    1687          42 :                     poDS->pabyColorTable = reinterpret_cast<GByte *>(
    1688          42 :                         VSIMalloc(nExpectedColorTableBytes));
    1689          42 :                     if (poDS->pabyColorTable == nullptr)
    1690             :                     {
    1691           0 :                         CPLDebug("RMF", "Can't allocate color table.");
    1692           0 :                         delete poDS;
    1693           0 :                         return nullptr;
    1694             :                     }
    1695          42 :                     if (VSIFSeekL(
    1696             :                             poDS->fp,
    1697             :                             poDS->GetFileOffset(poDS->sHeader.nClrTblOffset),
    1698          42 :                             SEEK_SET) < 0)
    1699             :                     {
    1700           0 :                         CPLDebug("RMF", "Can't seek to color table location.");
    1701           0 :                         delete poDS;
    1702           0 :                         return nullptr;
    1703             :                     }
    1704          42 :                     if (VSIFReadL(poDS->pabyColorTable, 1,
    1705             :                                   nExpectedColorTableBytes,
    1706          42 :                                   poDS->fp) < nExpectedColorTableBytes)
    1707             :                     {
    1708           0 :                         CPLDebug("RMF", "Can't read color table.");
    1709           0 :                         delete poDS;
    1710           0 :                         return nullptr;
    1711             :                     }
    1712             : 
    1713          42 :                     poDS->poColorTable = new GDALColorTable();
    1714        9326 :                     for (GUInt32 i = 0; i < poDS->nColorTableSize; i++)
    1715             :                     {
    1716        9284 :                         const GDALColorEntry oEntry = {
    1717        9284 :                             poDS->pabyColorTable[i * 4],      // Red
    1718        9284 :                             poDS->pabyColorTable[i * 4 + 1],  // Green
    1719        9284 :                             poDS->pabyColorTable[i * 4 + 2],  // Blue
    1720             :                             255                               // Alpha
    1721        9284 :                         };
    1722             : 
    1723        9284 :                         poDS->poColorTable->SetColorEntry(i, &oEntry);
    1724             :                     }
    1725             :                 }
    1726          68 :                 poDS->nBands = 1;
    1727          68 :                 break;
    1728           0 :             default:
    1729           0 :                 CPLError(CE_Warning, CPLE_IllegalArg,
    1730             :                          "Invalid RSW bit depth %lu.",
    1731           0 :                          static_cast<unsigned long>(poDS->sHeader.nBitDepth));
    1732           0 :                 delete poDS;
    1733           0 :                 return nullptr;
    1734             :         }
    1735         126 :         eType = GDT_Byte;
    1736             :     }
    1737             :     else
    1738             :     {
    1739          74 :         poDS->nBands = 1;
    1740          74 :         if (poDS->sHeader.nBitDepth == 8)
    1741             :         {
    1742           0 :             eType = GDT_Byte;
    1743             :         }
    1744          74 :         else if (poDS->sHeader.nBitDepth == 16)
    1745             :         {
    1746           0 :             eType = GDT_Int16;
    1747             :         }
    1748          74 :         else if (poDS->sHeader.nBitDepth == 32)
    1749             :         {
    1750           9 :             eType = GDT_Int32;
    1751             :         }
    1752          65 :         else if (poDS->sHeader.nBitDepth == 64)
    1753             :         {
    1754          65 :             eType = GDT_Float64;
    1755             :         }
    1756             :         else
    1757             :         {
    1758           0 :             CPLError(CE_Warning, CPLE_IllegalArg, "Invalid MTW bit depth %lu.",
    1759           0 :                      static_cast<unsigned long>(poDS->sHeader.nBitDepth));
    1760           0 :             delete poDS;
    1761           0 :             return nullptr;
    1762             :         }
    1763             :     }
    1764             : 
    1765         200 :     if (poDS->sHeader.nTileWidth == 0 || poDS->sHeader.nTileWidth > INT_MAX ||
    1766         200 :         poDS->sHeader.nTileHeight == 0 || poDS->sHeader.nTileHeight > INT_MAX)
    1767             :     {
    1768           0 :         CPLDebug("RMF", "Invalid tile dimension : %u x %u",
    1769             :                  poDS->sHeader.nTileWidth, poDS->sHeader.nTileHeight);
    1770           0 :         delete poDS;
    1771           0 :         return nullptr;
    1772             :     }
    1773             : 
    1774         200 :     const int nDataSize = GDALGetDataTypeSizeBytes(eType);
    1775         200 :     const int nBlockXSize = static_cast<int>(poDS->sHeader.nTileWidth);
    1776         200 :     const int nBlockYSize = static_cast<int>(poDS->sHeader.nTileHeight);
    1777         200 :     if (nDataSize == 0 || nBlockXSize > INT_MAX / nBlockYSize ||
    1778         200 :         nBlockYSize > INT_MAX / nDataSize ||
    1779         200 :         nBlockXSize > INT_MAX / (nBlockYSize * nDataSize))
    1780             :     {
    1781           0 :         CPLDebug("RMF", "Too big raster / tile dimension");
    1782           0 :         delete poDS;
    1783           0 :         return nullptr;
    1784             :     }
    1785             : 
    1786         200 :     poDS->nXTiles = DIV_ROUND_UP(poDS->nRasterXSize, nBlockXSize);
    1787         200 :     poDS->nYTiles = DIV_ROUND_UP(poDS->nRasterYSize, nBlockYSize);
    1788             : 
    1789             : #ifdef DEBUG
    1790         200 :     CPLDebug("RMF", "Image is %d tiles wide, %d tiles long", poDS->nXTiles,
    1791             :              poDS->nYTiles);
    1792             : #endif
    1793             : 
    1794             :     /* -------------------------------------------------------------------- */
    1795             :     /*  Choose compression scheme.                                          */
    1796             :     /* -------------------------------------------------------------------- */
    1797         200 :     if (CE_None != poDS->SetupCompression(eType, poOpenInfo->pszFilename))
    1798             :     {
    1799           0 :         delete poDS;
    1800           0 :         return nullptr;
    1801             :     }
    1802             : 
    1803         200 :     if (poOpenInfo->eAccess == GA_Update)
    1804             :     {
    1805          20 :         if (poParentDS == nullptr)
    1806             :         {
    1807          12 :             if (CE_None !=
    1808          12 :                 poDS->InitCompressorData(poOpenInfo->papszOpenOptions))
    1809             :             {
    1810           0 :                 delete poDS;
    1811           0 :                 return nullptr;
    1812             :             }
    1813             :         }
    1814             :         else
    1815             :         {
    1816           8 :             poDS->poCompressData = poParentDS->poCompressData;
    1817             :         }
    1818             :     }
    1819             :     /* -------------------------------------------------------------------- */
    1820             :     /*  Create band information objects.                                    */
    1821             :     /* -------------------------------------------------------------------- */
    1822         516 :     for (int iBand = 1; iBand <= poDS->nBands; iBand++)
    1823         316 :         poDS->SetBand(iBand, new RMFRasterBand(poDS, iBand, eType));
    1824             : 
    1825         200 :     poDS->SetupNBits();
    1826             : 
    1827         200 :     if (poDS->nBands > 1)
    1828             :     {
    1829          58 :         poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
    1830             :     }
    1831             :     /* -------------------------------------------------------------------- */
    1832             :     /*  Set up projection.                                                  */
    1833             :     /*                                                                      */
    1834             :     /*  XXX: If projection value is not specified, but image still have     */
    1835             :     /*  georeferencing information, assume Gauss-Kruger projection.         */
    1836             :     /* -------------------------------------------------------------------- */
    1837         200 :     if (poDS->sHeader.iEPSGCode > RMF_EPSG_MIN_CODE ||
    1838         186 :         poDS->sHeader.iProjection > 0 ||
    1839         117 :         (poDS->sHeader.dfPixelSize != 0.0 && poDS->sHeader.dfLLX != 0.0 &&
    1840          15 :          poDS->sHeader.dfLLY != 0.0))
    1841             :     {
    1842          98 :         GInt32 nProj =
    1843          98 :             (poDS->sHeader.iProjection) ? poDS->sHeader.iProjection : 1;
    1844          98 :         double padfPrjParams[8] = {poDS->sHeader.dfStdP1,
    1845          98 :                                    poDS->sHeader.dfStdP2,
    1846          98 :                                    poDS->sHeader.dfCenterLat,
    1847          98 :                                    poDS->sHeader.dfCenterLong,
    1848             :                                    1.0,
    1849             :                                    0.0,
    1850             :                                    0.0,
    1851          98 :                                    0.0};
    1852             : 
    1853             :         // XXX: Compute zone number for Gauss-Kruger (Transverse Mercator)
    1854             :         // projection if it is not specified.
    1855          98 :         if (nProj == 1L && poDS->sHeader.dfCenterLong == 0.0)
    1856             :         {
    1857           6 :             if (poDS->sExtHeader.nZone == 0)
    1858             :             {
    1859           0 :                 double centerXCoord =
    1860           0 :                     poDS->sHeader.dfLLX +
    1861           0 :                     (poDS->nRasterXSize * poDS->sHeader.dfPixelSize / 2.0);
    1862           0 :                 padfPrjParams[7] = floor((centerXCoord - 500000.0) / 1000000.0);
    1863             :             }
    1864             :             else
    1865             :             {
    1866           6 :                 padfPrjParams[7] = poDS->sExtHeader.nZone;
    1867             :             }
    1868             :         }
    1869             : 
    1870          98 :         OGRErr res = OGRERR_FAILURE;
    1871          98 :         if (nProj >= 0 &&
    1872          88 :             (poDS->sExtHeader.nDatum >= 0 || poDS->sExtHeader.nEllipsoid >= 0))
    1873             :         {
    1874          88 :             res = poDS->m_oSRS.importFromPanorama(
    1875          88 :                 nProj, poDS->sExtHeader.nDatum, poDS->sExtHeader.nEllipsoid,
    1876             :                 padfPrjParams);
    1877             :         }
    1878             : 
    1879         111 :         if (poDS->sHeader.iEPSGCode > RMF_EPSG_MIN_CODE &&
    1880          13 :             (OGRERR_NONE != res || poDS->m_oSRS.IsLocal()))
    1881             :         {
    1882           1 :             res = poDS->m_oSRS.importFromEPSG(poDS->sHeader.iEPSGCode);
    1883             :         }
    1884             : 
    1885             :         const char *pszSetVertCS =
    1886          98 :             CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "RMF_SET_VERTCS",
    1887             :                                  CPLGetConfigOption("RMF_SET_VERTCS", "NO"));
    1888          98 :         if (CPLTestBool(pszSetVertCS) && res == OGRERR_NONE &&
    1889           0 :             poDS->sExtHeader.nVertDatum > 0)
    1890             :         {
    1891           0 :             poDS->m_oSRS.importVertCSFromPanorama(poDS->sExtHeader.nVertDatum);
    1892             :         }
    1893             :     }
    1894             : 
    1895             :     /* -------------------------------------------------------------------- */
    1896             :     /*  Set up georeferencing.                                              */
    1897             :     /* -------------------------------------------------------------------- */
    1898         200 :     if ((poDS->eRMFType == RMFT_RSW && poDS->sHeader.iGeorefFlag) ||
    1899         128 :         (poDS->eRMFType == RMFT_MTW && poDS->sHeader.dfPixelSize != 0.0))
    1900             :     {
    1901         146 :         poDS->adfGeoTransform[0] = poDS->sHeader.dfLLX;
    1902         292 :         poDS->adfGeoTransform[3] =
    1903         146 :             poDS->sHeader.dfLLY +
    1904         146 :             poDS->nRasterYSize * poDS->sHeader.dfPixelSize;
    1905         146 :         poDS->adfGeoTransform[1] = poDS->sHeader.dfPixelSize;
    1906         146 :         poDS->adfGeoTransform[5] = -poDS->sHeader.dfPixelSize;
    1907         146 :         poDS->adfGeoTransform[2] = 0.0;
    1908         146 :         poDS->adfGeoTransform[4] = 0.0;
    1909             :     }
    1910             : 
    1911             :     /* -------------------------------------------------------------------- */
    1912             :     /*  Set units.                                                          */
    1913             :     /* -------------------------------------------------------------------- */
    1914             : 
    1915         200 :     if (poDS->eRMFType == RMFT_MTW)
    1916             :     {
    1917          74 :         CPLFree(poDS->pszUnitType);
    1918          74 :         poDS->pszUnitType = RMFUnitTypeToStr(poDS->sHeader.iElevationUnit);
    1919             :     }
    1920             : 
    1921             :     /* -------------------------------------------------------------------- */
    1922             :     /*  Report some other dataset related information.                      */
    1923             :     /* -------------------------------------------------------------------- */
    1924             : 
    1925         200 :     if (poDS->eRMFType == RMFT_MTW)
    1926             :     {
    1927          74 :         char szTemp[256] = {};
    1928             : 
    1929          74 :         snprintf(szTemp, sizeof(szTemp), "%g", poDS->sHeader.adfElevMinMax[0]);
    1930          74 :         poDS->SetMetadataItem("ELEVATION_MINIMUM", szTemp);
    1931             : 
    1932          74 :         snprintf(szTemp, sizeof(szTemp), "%g", poDS->sHeader.adfElevMinMax[1]);
    1933          74 :         poDS->SetMetadataItem("ELEVATION_MAXIMUM", szTemp);
    1934             : 
    1935          74 :         poDS->SetMetadataItem("ELEVATION_UNITS", poDS->pszUnitType);
    1936             : 
    1937          74 :         snprintf(szTemp, sizeof(szTemp), "%d", poDS->sHeader.iElevationType);
    1938          74 :         poDS->SetMetadataItem("ELEVATION_TYPE", szTemp);
    1939             :     }
    1940             : 
    1941             :     /* -------------------------------------------------------------------- */
    1942             :     /*      Check for overviews.                                            */
    1943             :     /* -------------------------------------------------------------------- */
    1944         200 :     if (nNextHeaderOffset == 0 && poParentDS == nullptr)
    1945             :     {
    1946         119 :         poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
    1947             :     }
    1948             : 
    1949             :     /* Set frame */
    1950         200 :     if (poDS->sHeader.nROIOffset &&
    1951         107 :         poDS->sHeader.nROISize >= sizeof(RSWFrame) &&
    1952          12 :         poDS->sHeader.nROISize <= ROI_MAX_SIZE_TO_AVOID_EXCESSIVE_RAM_USAGE)
    1953             :     {
    1954             :         GByte *pabyROI = reinterpret_cast<GByte *>(
    1955          12 :             VSI_MALLOC_VERBOSE(poDS->sHeader.nROISize));
    1956          12 :         if (pabyROI == nullptr)
    1957             :         {
    1958           0 :             delete poDS;
    1959           0 :             return nullptr;
    1960             :         }
    1961             : 
    1962          12 :         VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nROIOffset),
    1963             :                   SEEK_SET);
    1964          12 :         if (VSIFReadL(pabyROI, poDS->sHeader.nROISize, 1, poDS->fp) != 1)
    1965             :         {
    1966           0 :             CPLError(CE_Failure, CPLE_FileIO, "Cannot read ROI");
    1967           0 :             CPLFree(pabyROI);
    1968           0 :             delete poDS;
    1969           0 :             return nullptr;
    1970             :         }
    1971             : 
    1972             :         GInt32 nFrameType;
    1973          12 :         RMF_READ_LONG(pabyROI, nFrameType, 0);
    1974          12 :         if (nFrameType == nPolygonType)
    1975             :         {
    1976          24 :             CPLString osWKT = "POLYGON((";
    1977          12 :             bool bFirst = true;
    1978             : 
    1979          12 :             CPLDebug("RMF", "ROI coordinates:");
    1980             :             /* coverity[tainted_data] */
    1981          12 :             for (GUInt32 i = sizeof(RSWFrame);
    1982        1734 :                  i + sizeof(RSWFrameCoord) <= poDS->sHeader.nROISize;
    1983        1722 :                  i += sizeof(RSWFrameCoord))
    1984             :             {
    1985             :                 GInt32 nX, nY;
    1986        1722 :                 RMF_READ_LONG(pabyROI, nX, i);
    1987        1722 :                 RMF_READ_LONG(pabyROI, nY, i + 4);
    1988             : 
    1989        1722 :                 CPLDebug("RMF", "X: %d, Y: %d", nX, nY);
    1990             : 
    1991        1722 :                 double dfX = poDS->adfGeoTransform[0] +
    1992        1722 :                              nX * poDS->adfGeoTransform[1] +
    1993        1722 :                              nY * poDS->adfGeoTransform[2];
    1994        1722 :                 double dfY = poDS->adfGeoTransform[3] +
    1995        1722 :                              nX * poDS->adfGeoTransform[4] +
    1996        1722 :                              nY * poDS->adfGeoTransform[5];
    1997             : 
    1998        1722 :                 if (bFirst)
    1999             :                 {
    2000          12 :                     osWKT += CPLSPrintf("%f %f", dfX, dfY);
    2001          12 :                     bFirst = false;
    2002             :                 }
    2003             :                 else
    2004             :                 {
    2005        1710 :                     osWKT += CPLSPrintf(", %f %f", dfX, dfY);
    2006             :                 }
    2007             :             }
    2008          12 :             osWKT += "))";
    2009          12 :             CPLDebug("RMF", "Frame WKT: %s", osWKT.c_str());
    2010          12 :             poDS->SetMetadataItem(MD_FRAME_KEY, osWKT);
    2011             :         }
    2012          12 :         CPLFree(pabyROI);
    2013             :     }
    2014             : 
    2015             : #undef RMF_READ_DOUBLE
    2016             : #undef RMF_READ_LONG
    2017             : #undef RMF_READ_ULONG
    2018             : 
    2019         200 :     if (poDS->sHeader.nFlagsTblOffset && poDS->sHeader.nFlagsTblSize)
    2020             :     {
    2021         107 :         VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nFlagsTblOffset),
    2022             :                   SEEK_SET);
    2023         107 :         CPLDebug("RMF", "Blocks flags:");
    2024             :         /* coverity[tainted_data] */
    2025         416 :         for (GUInt32 i = 0; i < poDS->sHeader.nFlagsTblSize; i += sizeof(GByte))
    2026             :         {
    2027             :             GByte nValue;
    2028         309 :             if (VSIFReadL(&nValue, 1, sizeof(nValue), poDS->fp) !=
    2029             :                 sizeof(nValue))
    2030             :             {
    2031           0 :                 CPLDebug("RMF", "Cannot read Block flag at index %u", i);
    2032           0 :                 break;
    2033             :             }
    2034         309 :             CPLDebug("RMF", "Block %u -- flag %d", i, nValue);
    2035             :         }
    2036             :     }
    2037         200 :     return poDS;
    2038             : }
    2039             : 
    2040             : /************************************************************************/
    2041             : /*                               Create()                               */
    2042             : /************************************************************************/
    2043          89 : GDALDataset *RMFDataset::Create(const char *pszFilename, int nXSize, int nYSize,
    2044             :                                 int nBandsIn, GDALDataType eType,
    2045             :                                 char **papszParamList)
    2046             : {
    2047          89 :     return Create(pszFilename, nXSize, nYSize, nBandsIn, eType, papszParamList,
    2048          89 :                   nullptr, 1.0);
    2049             : }
    2050             : 
    2051         123 : GDALDataset *RMFDataset::Create(const char *pszFilename, int nXSize, int nYSize,
    2052             :                                 int nBandsIn, GDALDataType eType,
    2053             :                                 char **papszParamList, RMFDataset *poParentDS,
    2054             :                                 double dfOvFactor)
    2055             : 
    2056             : {
    2057         123 :     if (nBandsIn != 1 && nBandsIn != 3)
    2058             :     {
    2059           7 :         CPLError(CE_Failure, CPLE_NotSupported,
    2060             :                  "RMF driver doesn't support %d bands. Must be 1 or 3.",
    2061             :                  nBandsIn);
    2062             : 
    2063           7 :         return nullptr;
    2064             :     }
    2065             : 
    2066         116 :     if (nBandsIn == 1 && eType != GDT_Byte && eType != GDT_Int16 &&
    2067          52 :         eType != GDT_Int32 && eType != GDT_Float64)
    2068             :     {
    2069          17 :         CPLError(
    2070             :             CE_Failure, CPLE_AppDefined,
    2071             :             "Attempt to create RMF dataset with an illegal data type (%s), "
    2072             :             "only Byte, Int16, Int32 and Float64 types supported "
    2073             :             "by the format for single-band images.",
    2074             :             GDALGetDataTypeName(eType));
    2075             : 
    2076          17 :         return nullptr;
    2077             :     }
    2078             : 
    2079          99 :     if (nBandsIn == 3 && eType != GDT_Byte)
    2080             :     {
    2081          13 :         CPLError(
    2082             :             CE_Failure, CPLE_AppDefined,
    2083             :             "Attempt to create RMF dataset with an illegal data type (%s), "
    2084             :             "only Byte type supported by the format for three-band images.",
    2085             :             GDALGetDataTypeName(eType));
    2086             : 
    2087          13 :         return nullptr;
    2088             :     }
    2089             : 
    2090             :     /* -------------------------------------------------------------------- */
    2091             :     /*  Create the dataset.                                                 */
    2092             :     /* -------------------------------------------------------------------- */
    2093          86 :     RMFDataset *poDS = new RMFDataset();
    2094             : 
    2095          86 :     GUInt32 nBlockXSize =
    2096          86 :         (nXSize < RMF_DEFAULT_BLOCKXSIZE) ? nXSize : RMF_DEFAULT_BLOCKXSIZE;
    2097          86 :     GUInt32 nBlockYSize =
    2098          86 :         (nYSize < RMF_DEFAULT_BLOCKYSIZE) ? nYSize : RMF_DEFAULT_BLOCKYSIZE;
    2099             :     double dfScale;
    2100             :     double dfResolution;
    2101             :     double dfPixelSize;
    2102          86 :     if (poParentDS == nullptr)
    2103             :     {
    2104          52 :         poDS->fp = VSIFOpenL(pszFilename, "w+b");
    2105          52 :         if (poDS->fp == nullptr)
    2106             :         {
    2107           3 :             CPLError(CE_Failure, CPLE_OpenFailed, "Unable to create file %s.",
    2108             :                      pszFilename);
    2109           3 :             delete poDS;
    2110           3 :             return nullptr;
    2111             :         }
    2112             : 
    2113             :         const char *pszScaleValue =
    2114          49 :             CSLFetchNameValue(papszParamList, MD_SCALE_KEY);
    2115          49 :         if (pszScaleValue != nullptr && CPLStrnlen(pszScaleValue, 10) > 4)
    2116             :         {
    2117           0 :             dfScale = atof(pszScaleValue + 4);
    2118             :         }
    2119             :         else
    2120             :         {
    2121          49 :             dfScale = RMF_DEFAULT_SCALE;
    2122             :         }
    2123          49 :         dfResolution = RMF_DEFAULT_RESOLUTION;
    2124          49 :         dfPixelSize = 1;
    2125             : 
    2126          49 :         if (CPLFetchBool(papszParamList, "MTW", false))
    2127          12 :             poDS->eRMFType = RMFT_MTW;
    2128             :         else
    2129          37 :             poDS->eRMFType = RMFT_RSW;
    2130             : 
    2131          49 :         GUInt32 iVersion = RMF_VERSION;
    2132          49 :         const char *pszRMFHUGE = CSLFetchNameValue(papszParamList, "RMFHUGE");
    2133             : 
    2134          49 :         if (pszRMFHUGE == nullptr)
    2135          35 :             pszRMFHUGE = "NO";  // Keep old behavior by default
    2136             : 
    2137          49 :         if (EQUAL(pszRMFHUGE, "NO"))
    2138             :         {
    2139          42 :             iVersion = RMF_VERSION;
    2140             :         }
    2141           7 :         else if (EQUAL(pszRMFHUGE, "YES"))
    2142             :         {
    2143           7 :             iVersion = RMF_VERSION_HUGE;
    2144             :         }
    2145           0 :         else if (EQUAL(pszRMFHUGE, "IF_SAFER"))
    2146             :         {
    2147             :             const double dfImageSize =
    2148           0 :                 static_cast<double>(nXSize) * static_cast<double>(nYSize) *
    2149           0 :                 static_cast<double>(nBandsIn) *
    2150           0 :                 static_cast<double>(GDALGetDataTypeSizeBytes(eType));
    2151           0 :             if (dfImageSize > 3.0 * 1024.0 * 1024.0 * 1024.0)
    2152             :             {
    2153           0 :                 iVersion = RMF_VERSION_HUGE;
    2154             :             }
    2155             :             else
    2156             :             {
    2157           0 :                 iVersion = RMF_VERSION;
    2158             :             }
    2159             :         }
    2160             : 
    2161          49 :         const char *pszValue = CSLFetchNameValue(papszParamList, "BLOCKXSIZE");
    2162          49 :         if (pszValue != nullptr)
    2163           1 :             nBlockXSize = atoi(pszValue);
    2164          49 :         if (static_cast<int>(nBlockXSize) <= 0)
    2165           0 :             nBlockXSize = RMF_DEFAULT_BLOCKXSIZE;
    2166             : 
    2167          49 :         pszValue = CSLFetchNameValue(papszParamList, "BLOCKYSIZE");
    2168          49 :         if (pszValue != nullptr)
    2169           1 :             nBlockYSize = atoi(pszValue);
    2170          49 :         if (static_cast<int>(nBlockYSize) <= 0)
    2171           0 :             nBlockYSize = RMF_DEFAULT_BLOCKXSIZE;
    2172             : 
    2173          49 :         if (poDS->eRMFType == RMFT_MTW)
    2174          12 :             memcpy(poDS->sHeader.bySignature, RMF_SigMTW, RMF_SIGNATURE_SIZE);
    2175             :         else
    2176          37 :             memcpy(poDS->sHeader.bySignature, RMF_SigRSW, RMF_SIGNATURE_SIZE);
    2177          49 :         poDS->sHeader.iVersion = iVersion;
    2178          49 :         poDS->sHeader.nOvrOffset = 0x00;
    2179             :     }
    2180             :     else
    2181             :     {
    2182          34 :         poDS->fp = poParentDS->fp;
    2183          34 :         memcpy(poDS->sHeader.bySignature, poParentDS->sHeader.bySignature,
    2184             :                RMF_SIGNATURE_SIZE);
    2185          34 :         poDS->sHeader.iVersion = poParentDS->sHeader.iVersion;
    2186          34 :         poDS->eRMFType = poParentDS->eRMFType;
    2187          34 :         nBlockXSize = poParentDS->sHeader.nTileWidth;
    2188          34 :         nBlockYSize = poParentDS->sHeader.nTileHeight;
    2189          34 :         dfScale = poParentDS->sHeader.dfScale;
    2190          34 :         dfResolution = poParentDS->sHeader.dfResolution / dfOvFactor;
    2191          34 :         dfPixelSize = poParentDS->sHeader.dfPixelSize * dfOvFactor;
    2192             : 
    2193          34 :         poDS->nHeaderOffset = poParentDS->GetLastOffset();
    2194          34 :         poParentDS->sHeader.nOvrOffset =
    2195          34 :             poDS->GetRMFOffset(poDS->nHeaderOffset, &poDS->nHeaderOffset);
    2196          34 :         poParentDS->bHeaderDirty = true;
    2197          34 :         VSIFSeekL(poDS->fp, poDS->nHeaderOffset, SEEK_SET);
    2198          34 :         poDS->poParentDS = poParentDS;
    2199          34 :         CPLDebug("RMF",
    2200             :                  "Create overview subfile at " CPL_FRMT_GUIB
    2201             :                  " with size %dx%d, parent overview offset %d",
    2202             :                  poDS->nHeaderOffset, nXSize, nYSize,
    2203             :                  poParentDS->sHeader.nOvrOffset);
    2204             :     }
    2205             :     /* -------------------------------------------------------------------- */
    2206             :     /*  Fill the RMFHeader                                                  */
    2207             :     /* -------------------------------------------------------------------- */
    2208          83 :     CPLDebug("RMF", "Version %d", poDS->sHeader.iVersion);
    2209             : 
    2210          83 :     poDS->sHeader.iUserID = 0x00;
    2211          83 :     memset(poDS->sHeader.byName, 0, sizeof(poDS->sHeader.byName));
    2212          83 :     poDS->sHeader.nBitDepth = GDALGetDataTypeSizeBits(eType) * nBandsIn;
    2213          83 :     poDS->sHeader.nHeight = nYSize;
    2214          83 :     poDS->sHeader.nWidth = nXSize;
    2215          83 :     poDS->sHeader.nTileWidth = nBlockXSize;
    2216          83 :     poDS->sHeader.nTileHeight = nBlockYSize;
    2217             : 
    2218          83 :     poDS->nXTiles = poDS->sHeader.nXTiles =
    2219          83 :         (nXSize + poDS->sHeader.nTileWidth - 1) / poDS->sHeader.nTileWidth;
    2220          83 :     poDS->nYTiles = poDS->sHeader.nYTiles =
    2221          83 :         (nYSize + poDS->sHeader.nTileHeight - 1) / poDS->sHeader.nTileHeight;
    2222          83 :     poDS->sHeader.nLastTileHeight = nYSize % poDS->sHeader.nTileHeight;
    2223          83 :     if (!poDS->sHeader.nLastTileHeight)
    2224          44 :         poDS->sHeader.nLastTileHeight = poDS->sHeader.nTileHeight;
    2225          83 :     poDS->sHeader.nLastTileWidth = nXSize % poDS->sHeader.nTileWidth;
    2226          83 :     if (!poDS->sHeader.nLastTileWidth)
    2227          40 :         poDS->sHeader.nLastTileWidth = poDS->sHeader.nTileWidth;
    2228             : 
    2229             :     // poDS->sHeader.nROIOffset = 0x00;
    2230             :     // poDS->sHeader.nROISize = 0x00;
    2231             : 
    2232          83 :     vsi_l_offset nCurPtr = poDS->nHeaderOffset + RMF_HEADER_SIZE;
    2233             : 
    2234             :     // Extended header
    2235          83 :     poDS->sHeader.nExtHdrOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
    2236          83 :     poDS->sHeader.nExtHdrSize = RMF_EXT_HEADER_SIZE;
    2237          83 :     nCurPtr += poDS->sHeader.nExtHdrSize;
    2238             : 
    2239             :     // Color table
    2240          83 :     if (poDS->eRMFType == RMFT_RSW && nBandsIn == 1)
    2241             :     {
    2242          34 :         if (poDS->sHeader.nBitDepth > 8)
    2243             :         {
    2244           6 :             CPLError(CE_Failure, CPLE_AppDefined,
    2245             :                      "Cannot create color table of RSW with nBitDepth = %d. "
    2246             :                      "Retry with MTW ?",
    2247             :                      poDS->sHeader.nBitDepth);
    2248           6 :             delete poDS;
    2249           6 :             return nullptr;
    2250             :         }
    2251             : 
    2252          28 :         poDS->sHeader.nClrTblOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
    2253          28 :         poDS->nColorTableSize = 1 << poDS->sHeader.nBitDepth;
    2254          28 :         poDS->sHeader.nClrTblSize = poDS->nColorTableSize * 4;
    2255          28 :         poDS->pabyColorTable =
    2256          28 :             static_cast<GByte *>(VSI_MALLOC_VERBOSE(poDS->sHeader.nClrTblSize));
    2257          28 :         if (poDS->pabyColorTable == nullptr)
    2258             :         {
    2259           0 :             delete poDS;
    2260           0 :             return nullptr;
    2261             :         }
    2262        7196 :         for (GUInt32 i = 0; i < poDS->nColorTableSize; i++)
    2263             :         {
    2264        7168 :             poDS->pabyColorTable[i * 4 + 0] = static_cast<GByte>(i);
    2265        7168 :             poDS->pabyColorTable[i * 4 + 1] = static_cast<GByte>(i);
    2266        7168 :             poDS->pabyColorTable[i * 4 + 2] = static_cast<GByte>(i);
    2267        7168 :             poDS->pabyColorTable[i * 4 + 3] = 0;
    2268             :         }
    2269          28 :         nCurPtr += poDS->sHeader.nClrTblSize;
    2270             :     }
    2271             :     else
    2272             :     {
    2273          49 :         poDS->sHeader.nClrTblOffset = 0x00;
    2274          49 :         poDS->sHeader.nClrTblSize = 0x00;
    2275             :     }
    2276             : 
    2277             :     // Add room for ROI (frame)
    2278          77 :     poDS->sHeader.nROIOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
    2279          77 :     poDS->sHeader.nROISize = 0x00;
    2280          77 :     nCurPtr +=
    2281             :         sizeof(RSWFrame) +
    2282             :         sizeof(RSWFrameCoord) *
    2283             :             nMaxFramePointCount;  // Allocate nMaxFramePointCount coordinates for frame
    2284             : 
    2285             :     // Add blocks flags
    2286          77 :     poDS->sHeader.nFlagsTblOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
    2287          77 :     poDS->sHeader.nFlagsTblSize =
    2288          77 :         sizeof(GByte) * poDS->sHeader.nXTiles * poDS->sHeader.nYTiles;
    2289          77 :     nCurPtr += poDS->sHeader.nFlagsTblSize;
    2290             : 
    2291             :     // Blocks table
    2292          77 :     poDS->sHeader.nTileTblOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
    2293          77 :     poDS->sHeader.nTileTblSize =
    2294          77 :         2 * sizeof(GUInt32) * poDS->sHeader.nXTiles * poDS->sHeader.nYTiles;
    2295          77 :     poDS->paiTiles =
    2296          77 :         reinterpret_cast<GUInt32 *>(CPLCalloc(poDS->sHeader.nTileTblSize, 1));
    2297             :     // nCurPtr += poDS->sHeader.nTileTblSize;
    2298          77 :     const GUInt32 nTileSize = poDS->sHeader.nTileWidth *
    2299          77 :                               poDS->sHeader.nTileHeight *
    2300          77 :                               GDALGetDataTypeSizeBytes(eType);
    2301          77 :     poDS->sHeader.nSize =
    2302          77 :         poDS->paiTiles[poDS->sHeader.nTileTblSize / 4 - 2] + nTileSize;
    2303             : 
    2304             :     // Elevation units
    2305          77 :     poDS->sHeader.iElevationUnit = RMFStrToUnitType(poDS->pszUnitType);
    2306             : 
    2307          77 :     poDS->sHeader.iMapType = -1;
    2308          77 :     poDS->sHeader.iProjection = -1;
    2309          77 :     poDS->sHeader.iEPSGCode = -1;
    2310          77 :     poDS->sHeader.dfScale = dfScale;
    2311          77 :     poDS->sHeader.dfResolution = dfResolution;
    2312          77 :     poDS->sHeader.dfPixelSize = dfPixelSize;
    2313          77 :     poDS->sHeader.iMaskType = 0;
    2314          77 :     poDS->sHeader.iMaskStep = 0;
    2315          77 :     poDS->sHeader.iFrameFlag = 1;  // 1 - Frame not using
    2316             :     // poDS->sHeader.nFlagsTblOffset = 0x00;
    2317             :     // poDS->sHeader.nFlagsTblSize = 0x00;
    2318          77 :     poDS->sHeader.nFileSize0 = 0x00;
    2319          77 :     poDS->sHeader.nFileSize1 = 0x00;
    2320          77 :     poDS->sHeader.iUnknown = 0;
    2321          77 :     poDS->sHeader.iGeorefFlag = 0;
    2322          77 :     poDS->sHeader.iInverse = 0;
    2323          77 :     poDS->sHeader.iJpegQuality = 0;
    2324          77 :     memset(poDS->sHeader.abyInvisibleColors, 0,
    2325             :            sizeof(poDS->sHeader.abyInvisibleColors));
    2326          77 :     poDS->sHeader.iElevationType = 0;
    2327             : 
    2328          77 :     poDS->nRasterXSize = nXSize;
    2329          77 :     poDS->nRasterYSize = nYSize;
    2330          77 :     poDS->eAccess = GA_Update;
    2331          77 :     poDS->nBands = nBandsIn;
    2332             : 
    2333          77 :     if (poParentDS == nullptr)
    2334             :     {
    2335          43 :         poDS->sHeader.adfElevMinMax[0] = 0.0;
    2336          43 :         poDS->sHeader.adfElevMinMax[1] = 0.0;
    2337          43 :         poDS->sHeader.dfNoData = 0.0;
    2338          43 :         poDS->sHeader.iCompression =
    2339          43 :             GetCompressionType(CSLFetchNameValue(papszParamList, "COMPRESS"));
    2340          43 :         if (CE_None != poDS->InitCompressorData(papszParamList))
    2341             :         {
    2342           0 :             delete poDS;
    2343           0 :             return nullptr;
    2344             :         }
    2345             : 
    2346          43 :         if (poDS->sHeader.iCompression == RMF_COMPRESSION_JPEG)
    2347             :         {
    2348             :             const char *pszJpegQuality =
    2349           1 :                 CSLFetchNameValue(papszParamList, "JPEG_QUALITY");
    2350           1 :             if (pszJpegQuality == nullptr)
    2351             :             {
    2352           1 :                 poDS->sHeader.iJpegQuality = 75;
    2353             :             }
    2354             :             else
    2355             :             {
    2356           0 :                 int iJpegQuality = atoi(pszJpegQuality);
    2357           0 :                 if (iJpegQuality < 10 || iJpegQuality > 100)
    2358             :                 {
    2359           0 :                     CPLError(CE_Failure, CPLE_IllegalArg,
    2360             :                              "JPEG_QUALITY=%s is not a legal value in the "
    2361             :                              "range 10-100.\n"
    2362             :                              "Defaulting to 75",
    2363             :                              pszJpegQuality);
    2364           0 :                     iJpegQuality = 75;
    2365             :                 }
    2366           0 :                 poDS->sHeader.iJpegQuality = static_cast<GByte>(iJpegQuality);
    2367             :             }
    2368             :         }
    2369             : 
    2370          43 :         if (CE_None != poDS->SetupCompression(eType, pszFilename))
    2371             :         {
    2372           0 :             delete poDS;
    2373           0 :             return nullptr;
    2374             :         }
    2375             :     }
    2376             :     else
    2377             :     {
    2378          34 :         poDS->sHeader.adfElevMinMax[0] = poParentDS->sHeader.adfElevMinMax[0];
    2379          34 :         poDS->sHeader.adfElevMinMax[1] = poParentDS->sHeader.adfElevMinMax[1];
    2380          34 :         poDS->sHeader.dfNoData = poParentDS->sHeader.dfNoData;
    2381          34 :         poDS->sHeader.iCompression = poParentDS->sHeader.iCompression;
    2382          34 :         poDS->sHeader.iJpegQuality = poParentDS->sHeader.iJpegQuality;
    2383          34 :         poDS->Decompress = poParentDS->Decompress;
    2384          34 :         poDS->Compress = poParentDS->Compress;
    2385          34 :         poDS->poCompressData = poParentDS->poCompressData;
    2386             :     }
    2387             : 
    2388          77 :     if (nBandsIn > 1)
    2389             :     {
    2390          13 :         poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
    2391             :     }
    2392             : 
    2393          77 :     poDS->WriteHeader();
    2394             : 
    2395             :     /* -------------------------------------------------------------------- */
    2396             :     /*      Create band information objects.                                */
    2397             :     /* -------------------------------------------------------------------- */
    2398         180 :     for (int iBand = 1; iBand <= poDS->nBands; iBand++)
    2399         103 :         poDS->SetBand(iBand, new RMFRasterBand(poDS, iBand, eType));
    2400             : 
    2401          77 :     poDS->SetupNBits();
    2402             : 
    2403          77 :     return GDALDataset::FromHandle(poDS);
    2404             : }
    2405             : 
    2406             : // GIS Panorama 11 was introduced new format for huge files (greater than 3 Gb)
    2407        3859 : vsi_l_offset RMFDataset::GetFileOffset(GUInt32 iRMFOffset) const
    2408             : {
    2409        3859 :     if (sHeader.iVersion >= RMF_VERSION_HUGE)
    2410             :     {
    2411        1142 :         return (static_cast<vsi_l_offset>(iRMFOffset)) * RMF_HUGE_OFFSET_FACTOR;
    2412             :     }
    2413             : 
    2414        2717 :     return static_cast<vsi_l_offset>(iRMFOffset);
    2415             : }
    2416             : 
    2417         966 : GUInt32 RMFDataset::GetRMFOffset(vsi_l_offset nFileOffset,
    2418             :                                  vsi_l_offset *pnNewFileOffset) const
    2419             : {
    2420         966 :     if (sHeader.iVersion >= RMF_VERSION_HUGE)
    2421             :     {
    2422             :         // Round offset to next RMF_HUGE_OFFSET_FACTOR
    2423         272 :         const GUInt32 iRMFOffset =
    2424         272 :             static_cast<GUInt32>((nFileOffset + (RMF_HUGE_OFFSET_FACTOR - 1)) /
    2425             :                                  RMF_HUGE_OFFSET_FACTOR);
    2426         272 :         if (pnNewFileOffset != nullptr)
    2427             :         {
    2428         205 :             *pnNewFileOffset = GetFileOffset(iRMFOffset);
    2429             :         }
    2430         272 :         return iRMFOffset;
    2431             :     }
    2432             : 
    2433         694 :     if (pnNewFileOffset != nullptr)
    2434             :     {
    2435         561 :         *pnNewFileOffset = nFileOffset;
    2436             :     }
    2437         694 :     return static_cast<GUInt32>(nFileOffset);
    2438             : }
    2439             : 
    2440         200 : RMFDataset *RMFDataset::OpenOverview(RMFDataset *poParent,
    2441             :                                      GDALOpenInfo *poOpenInfo)
    2442             : {
    2443         200 :     if (sHeader.nOvrOffset == 0)
    2444             :     {
    2445         111 :         return nullptr;
    2446             :     }
    2447             : 
    2448          89 :     if (poParent == nullptr)
    2449             :     {
    2450           0 :         return nullptr;
    2451             :     }
    2452             : 
    2453          89 :     vsi_l_offset nSubOffset = GetFileOffset(sHeader.nOvrOffset);
    2454             : 
    2455          89 :     CPLDebug("RMF",
    2456             :              "Try to open overview subfile at " CPL_FRMT_GUIB " for '%s'",
    2457             :              nSubOffset, poOpenInfo->pszFilename);
    2458             : 
    2459          89 :     if (!poParent->poOvrDatasets.empty())
    2460             :     {
    2461          49 :         if (poParent->GetFileOffset(poParent->sHeader.nOvrOffset) == nSubOffset)
    2462             :         {
    2463           2 :             CPLError(CE_Warning, CPLE_IllegalArg,
    2464             :                      "Recursive subdataset list is detected. "
    2465             :                      "Overview open failed.");
    2466           2 :             return nullptr;
    2467             :         }
    2468             : 
    2469          58 :         for (size_t n = 0; n != poParent->poOvrDatasets.size() - 1; ++n)
    2470             :         {
    2471          13 :             RMFDataset *poOvr(poParent->poOvrDatasets[n]);
    2472             : 
    2473          13 :             if (poOvr == nullptr)
    2474           0 :                 continue;
    2475          13 :             if (poOvr->GetFileOffset(poOvr->sHeader.nOvrOffset) == nSubOffset)
    2476             :             {
    2477           2 :                 CPLError(CE_Warning, CPLE_IllegalArg,
    2478             :                          "Recursive subdataset list is detected. "
    2479             :                          "Overview open failed.");
    2480           2 :                 return nullptr;
    2481             :             }
    2482             :         }
    2483             :     }
    2484             : 
    2485          85 :     size_t nHeaderSize(RMF_HEADER_SIZE);
    2486             :     GByte *pabyNewHeader;
    2487             :     pabyNewHeader = static_cast<GByte *>(
    2488          85 :         CPLRealloc(poOpenInfo->pabyHeader, nHeaderSize + 1));
    2489          85 :     if (pabyNewHeader == nullptr)
    2490             :     {
    2491           0 :         CPLError(CE_Warning, CPLE_OutOfMemory,
    2492             :                  "Can't allocate buffer for overview header");
    2493           0 :         return nullptr;
    2494             :     }
    2495             : 
    2496          85 :     poOpenInfo->pabyHeader = pabyNewHeader;
    2497          85 :     memset(poOpenInfo->pabyHeader, 0, nHeaderSize + 1);
    2498          85 :     VSIFSeekL(fp, nSubOffset, SEEK_SET);
    2499          85 :     poOpenInfo->nHeaderBytes =
    2500          85 :         static_cast<int>(VSIFReadL(poOpenInfo->pabyHeader, 1, nHeaderSize, fp));
    2501             : 
    2502          85 :     return Open(poOpenInfo, poParent, nSubOffset);
    2503             : }
    2504             : 
    2505          17 : CPLErr RMFDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
    2506             :                                    const int *panOverviewList, int nBandsIn,
    2507             :                                    const int *panBandList,
    2508             :                                    GDALProgressFunc pfnProgress,
    2509             :                                    void *pProgressData,
    2510             :                                    CSLConstList papszOptions)
    2511             : {
    2512          17 :     bool bUseGenericHandling = false;
    2513             : 
    2514          17 :     if (GetAccess() != GA_Update)
    2515             :     {
    2516           0 :         CPLDebug("RMF", "File open for read-only accessing, "
    2517             :                         "creating overviews externally.");
    2518             : 
    2519           0 :         bUseGenericHandling = true;
    2520             :     }
    2521             : 
    2522          17 :     if (bUseGenericHandling)
    2523             :     {
    2524           0 :         if (!poOvrDatasets.empty())
    2525             :         {
    2526           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2527             :                      "Cannot add external overviews when there are already "
    2528             :                      "internal overviews");
    2529           0 :             return CE_Failure;
    2530             :         }
    2531             : 
    2532           0 :         return GDALDataset::IBuildOverviews(
    2533             :             pszResampling, nOverviews, panOverviewList, nBandsIn, panBandList,
    2534           0 :             pfnProgress, pProgressData, papszOptions);
    2535             :     }
    2536             : 
    2537          17 :     if (nBandsIn != GetRasterCount())
    2538             :     {
    2539           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2540             :                  "Generation of overviews in RMF is only "
    2541             :                  "supported when operating on all bands.  "
    2542             :                  "Operation failed.");
    2543           0 :         return CE_Failure;
    2544             :     }
    2545             : 
    2546          17 :     if (nOverviews == 0)
    2547             :     {
    2548           0 :         if (poOvrDatasets.empty())
    2549             :         {
    2550           0 :             return GDALDataset::IBuildOverviews(
    2551             :                 pszResampling, nOverviews, panOverviewList, nBandsIn,
    2552           0 :                 panBandList, pfnProgress, pProgressData, papszOptions);
    2553             :         }
    2554           0 :         return CleanOverviews();
    2555             :     }
    2556             : 
    2557             :     // First destroy old overviews
    2558          17 :     if (CE_None != CleanOverviews())
    2559             :     {
    2560           0 :         return CE_Failure;
    2561             :     }
    2562             : 
    2563          17 :     CPLDebug("RMF", "Build overviews on dataset %d x %d size", GetRasterXSize(),
    2564             :              GetRasterYSize());
    2565             : 
    2566          17 :     GDALDataType eMainType = GetRasterBand(1)->GetRasterDataType();
    2567          17 :     RMFDataset *poParent = this;
    2568          17 :     double prevOvLevel = 1.0;
    2569          51 :     for (int n = 0; n != nOverviews; ++n)
    2570             :     {
    2571          34 :         int nOvLevel = panOverviewList[n];
    2572          34 :         const int nOXSize = (GetRasterXSize() + nOvLevel - 1) / nOvLevel;
    2573          34 :         const int nOYSize = (GetRasterYSize() + nOvLevel - 1) / nOvLevel;
    2574          34 :         CPLDebug("RMF", "\tCreate overview #%d size %d x %d", nOvLevel, nOXSize,
    2575             :                  nOYSize);
    2576             : 
    2577             :         RMFDataset *poOvrDataset;
    2578          34 :         poOvrDataset = static_cast<RMFDataset *>(RMFDataset::Create(
    2579             :             nullptr, nOXSize, nOYSize, GetRasterCount(), eMainType, nullptr,
    2580             :             poParent, nOvLevel / prevOvLevel));
    2581             : 
    2582          34 :         if (poOvrDataset == nullptr)
    2583             :         {
    2584           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2585             :                      "Can't create overview dataset #%d size %d x %d", nOvLevel,
    2586             :                      nOXSize, nOYSize);
    2587           0 :             return CE_Failure;
    2588             :         }
    2589             : 
    2590          34 :         prevOvLevel = nOvLevel;
    2591          34 :         poParent = poOvrDataset;
    2592          34 :         poOvrDatasets.push_back(poOvrDataset);
    2593             :     }
    2594             : 
    2595             :     GDALRasterBand ***papapoOverviewBands =
    2596          17 :         static_cast<GDALRasterBand ***>(CPLCalloc(sizeof(void *), nBandsIn));
    2597             :     GDALRasterBand **papoBandList =
    2598          17 :         static_cast<GDALRasterBand **>(CPLCalloc(sizeof(void *), nBandsIn));
    2599             : 
    2600          36 :     for (int iBand = 0; iBand < nBandsIn; ++iBand)
    2601             :     {
    2602          19 :         GDALRasterBand *poBand = GetRasterBand(panBandList[iBand]);
    2603             : 
    2604          19 :         papoBandList[iBand] = poBand;
    2605          38 :         papapoOverviewBands[iBand] = static_cast<GDALRasterBand **>(
    2606          19 :             CPLCalloc(sizeof(void *), poBand->GetOverviewCount()));
    2607             : 
    2608          57 :         for (int i = 0; i < nOverviews; ++i)
    2609             :         {
    2610          38 :             papapoOverviewBands[iBand][i] = poBand->GetOverview(i);
    2611             :         }
    2612             :     }
    2613             : #ifdef DEBUG
    2614          36 :     for (int iBand = 0; iBand < nBandsIn; ++iBand)
    2615             :     {
    2616          19 :         CPLDebug("RMF", "Try to create overview for #%d size %d x %d",
    2617          19 :                  iBand + 1, papoBandList[iBand]->GetXSize(),
    2618          19 :                  papoBandList[iBand]->GetYSize());
    2619          57 :         for (int i = 0; i < nOverviews; ++i)
    2620             :         {
    2621          38 :             CPLDebug("RMF", "\t%d x %d",
    2622          38 :                      papapoOverviewBands[iBand][i]->GetXSize(),
    2623          38 :                      papapoOverviewBands[iBand][i]->GetYSize());
    2624             :         }
    2625             :     }
    2626             : #endif  // DEBUG
    2627             :     CPLErr res;
    2628          17 :     res = GDALRegenerateOverviewsMultiBand(
    2629             :         nBandsIn, papoBandList, nOverviews, papapoOverviewBands, pszResampling,
    2630             :         pfnProgress, pProgressData, papszOptions);
    2631             : 
    2632          36 :     for (int iBand = 0; iBand < nBandsIn; ++iBand)
    2633             :     {
    2634          19 :         CPLFree(papapoOverviewBands[iBand]);
    2635             :     }
    2636             : 
    2637          17 :     CPLFree(papapoOverviewBands);
    2638          17 :     CPLFree(papoBandList);
    2639             : 
    2640          17 :     return res;
    2641             : }
    2642             : 
    2643          68 : CPLErr RMFDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
    2644             :                              int nXSize, int nYSize, void *pData, int nBufXSize,
    2645             :                              int nBufYSize, GDALDataType eBufType,
    2646             :                              int nBandCount, BANDMAP_TYPE panBandMap,
    2647             :                              GSpacing nPixelSpace, GSpacing nLineSpace,
    2648             :                              GSpacing nBandSpace,
    2649             :                              GDALRasterIOExtraArg *psExtraArg)
    2650             : {
    2651             : #ifdef DEBUG
    2652          68 :     CPLDebug("RMF", "Dataset %p, %s %d %d %d %d, %d %d", this,
    2653             :              (eRWFlag == GF_Read ? "Read" : "Write"), nXOff, nYOff, nXSize,
    2654             :              nYSize, nBufXSize, nBufYSize);
    2655             : #endif  // DEBUG
    2656          68 :     if (eRWFlag == GF_Read && poCompressData != nullptr &&
    2657           0 :         poCompressData->oThreadPool.GetThreadCount() > 0)
    2658             :     {
    2659           0 :         poCompressData->oThreadPool.WaitCompletion();
    2660             :     }
    2661             : 
    2662          68 :     return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
    2663             :                                   nBufXSize, nBufYSize, eBufType, nBandCount,
    2664             :                                   panBandMap, nPixelSpace, nLineSpace,
    2665          68 :                                   nBandSpace, psExtraArg);
    2666             : }
    2667             : 
    2668         238 : vsi_l_offset RMFDataset::GetLastOffset() const
    2669             : {
    2670         238 :     vsi_l_offset nLastTileOff = 0;
    2671         238 :     GUInt32 nTiles(sHeader.nTileTblSize / sizeof(GUInt32));
    2672             : 
    2673         768 :     for (GUInt32 n = 0; n < nTiles; n += 2)
    2674             :     {
    2675         530 :         vsi_l_offset nTileOffset = GetFileOffset(paiTiles[n]);
    2676         530 :         GUInt32 nTileBytes = paiTiles[n + 1];
    2677         530 :         nLastTileOff = std::max(nLastTileOff, nTileOffset + nTileBytes);
    2678             :     }
    2679             : 
    2680         238 :     nLastTileOff = std::max(nLastTileOff, GetFileOffset(sHeader.nROIOffset) +
    2681         238 :                                               sHeader.nROISize);
    2682         238 :     nLastTileOff = std::max(nLastTileOff, GetFileOffset(sHeader.nClrTblOffset) +
    2683         238 :                                               sHeader.nClrTblSize);
    2684         238 :     nLastTileOff =
    2685         238 :         std::max(nLastTileOff,
    2686         238 :                  GetFileOffset(sHeader.nTileTblOffset) + sHeader.nTileTblSize);
    2687         238 :     nLastTileOff =
    2688         238 :         std::max(nLastTileOff, GetFileOffset(sHeader.nFlagsTblOffset) +
    2689         238 :                                    sHeader.nFlagsTblSize);
    2690         238 :     nLastTileOff = std::max(nLastTileOff, GetFileOffset(sHeader.nExtHdrOffset) +
    2691         238 :                                               sHeader.nExtHdrSize);
    2692         238 :     return nLastTileOff;
    2693             : }
    2694             : 
    2695          17 : CPLErr RMFDataset::CleanOverviews()
    2696             : {
    2697          17 :     if (sHeader.nOvrOffset == 0)
    2698             :     {
    2699          13 :         return CE_None;
    2700             :     }
    2701             : 
    2702           4 :     if (GetAccess() != GA_Update)
    2703             :     {
    2704           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2705             :                  "File open for read-only accessing, "
    2706             :                  "overviews cleanup failed.");
    2707           0 :         return CE_Failure;
    2708             :     }
    2709             : 
    2710           4 :     if (poParentDS != nullptr)
    2711             :     {
    2712           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2713             :                  "Overviews cleanup for non-root dataset is not possible.");
    2714           0 :         return CE_Failure;
    2715             :     }
    2716             : 
    2717          12 :     for (size_t n = 0; n != poOvrDatasets.size(); ++n)
    2718             :     {
    2719           8 :         GDALClose(poOvrDatasets[n]);
    2720             :     }
    2721           4 :     poOvrDatasets.clear();
    2722             : 
    2723           4 :     vsi_l_offset nLastTileOff = GetLastOffset();
    2724             : 
    2725           4 :     if (0 != VSIFSeekL(fp, 0, SEEK_END))
    2726             :     {
    2727           0 :         CPLError(CE_Failure, CPLE_FileIO,
    2728             :                  "Failed to seek to end of file, "
    2729             :                  "overviews cleanup failed.");
    2730             :     }
    2731             : 
    2732           4 :     vsi_l_offset nFileSize = VSIFTellL(fp);
    2733           4 :     if (nFileSize < nLastTileOff)
    2734             :     {
    2735           0 :         CPLError(CE_Failure, CPLE_FileIO,
    2736             :                  "Invalid file offset, "
    2737             :                  "overviews cleanup failed.");
    2738           0 :         return CE_Failure;
    2739             :     }
    2740             : 
    2741           4 :     CPLDebug("RMF", "Truncate to " CPL_FRMT_GUIB, nLastTileOff);
    2742           4 :     CPLDebug("RMF", "File size:  " CPL_FRMT_GUIB, nFileSize);
    2743             : 
    2744           4 :     if (0 != VSIFTruncateL(fp, nLastTileOff))
    2745             :     {
    2746           0 :         CPLError(CE_Failure, CPLE_FileIO,
    2747             :                  "Failed to truncate file, "
    2748             :                  "overviews cleanup failed.");
    2749           0 :         return CE_Failure;
    2750             :     }
    2751             : 
    2752           4 :     sHeader.nOvrOffset = 0;
    2753           4 :     bHeaderDirty = true;
    2754             : 
    2755           4 :     return CE_None;
    2756             : }
    2757             : 
    2758             : /************************************************************************/
    2759             : /*                         GetCompressionType()                         */
    2760             : /************************************************************************/
    2761             : 
    2762          43 : GByte RMFDataset::GetCompressionType(const char *pszCompressName)
    2763             : {
    2764          43 :     if (pszCompressName == nullptr || EQUAL(pszCompressName, "NONE"))
    2765             :     {
    2766          35 :         return RMF_COMPRESSION_NONE;
    2767             :     }
    2768           8 :     else if (EQUAL(pszCompressName, "LZW"))
    2769             :     {
    2770           5 :         return RMF_COMPRESSION_LZW;
    2771             :     }
    2772           3 :     else if (EQUAL(pszCompressName, "JPEG"))
    2773             :     {
    2774           1 :         return RMF_COMPRESSION_JPEG;
    2775             :     }
    2776           2 :     else if (EQUAL(pszCompressName, "RMF_DEM"))
    2777             :     {
    2778           2 :         return RMF_COMPRESSION_DEM;
    2779             :     }
    2780             : 
    2781           0 :     CPLError(CE_Failure, CPLE_AppDefined,
    2782             :              "RMF: Unknown compression scheme <%s>.\n"
    2783             :              "Defaults to NONE compression.",
    2784             :              pszCompressName);
    2785           0 :     return RMF_COMPRESSION_NONE;
    2786             : }
    2787             : 
    2788             : /************************************************************************/
    2789             : /*                        SetupCompression()                            */
    2790             : /************************************************************************/
    2791             : 
    2792         243 : int RMFDataset::SetupCompression(GDALDataType eType, const char *pszFilename)
    2793             : {
    2794             :     /* -------------------------------------------------------------------- */
    2795             :     /*  XXX: The DEM compression method seems to be only applicable         */
    2796             :     /*  to Int32 data.                                                      */
    2797             :     /* -------------------------------------------------------------------- */
    2798         243 :     if (sHeader.iCompression == RMF_COMPRESSION_NONE)
    2799             :     {
    2800         173 :         Decompress = nullptr;
    2801         173 :         Compress = nullptr;
    2802             :     }
    2803          70 :     else if (sHeader.iCompression == RMF_COMPRESSION_LZW)
    2804             :     {
    2805          57 :         Decompress = &LZWDecompress;
    2806          57 :         Compress = &LZWCompress;
    2807          57 :         SetMetadataItem("COMPRESSION", "LZW", "IMAGE_STRUCTURE");
    2808             :     }
    2809          13 :     else if (sHeader.iCompression == RMF_COMPRESSION_JPEG)
    2810             :     {
    2811           3 :         if (eType != GDT_Byte || nBands != RMF_JPEG_BAND_COUNT ||
    2812           3 :             sHeader.nBitDepth != 24)
    2813             :         {
    2814           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2815             :                      "RMF support only 24 bpp JPEG compressed files.");
    2816           0 :             return CE_Failure;
    2817             :         }
    2818             : #ifdef HAVE_LIBJPEG
    2819           6 :         CPLString oBuf;
    2820           3 :         oBuf.Printf("%d", sHeader.iJpegQuality);
    2821           3 :         Decompress = &JPEGDecompress;
    2822           3 :         Compress = &JPEGCompress;
    2823           3 :         SetMetadataItem("JPEG_QUALITY", oBuf.c_str(), "IMAGE_STRUCTURE");
    2824           3 :         SetMetadataItem("COMPRESSION", "JPEG", "IMAGE_STRUCTURE");
    2825             : #else   // HAVE_LIBJPEG
    2826             :         CPLError(CE_Failure, CPLE_AppDefined,
    2827             :                  "JPEG codec is needed to open <%s>.\n"
    2828             :                  "Please rebuild GDAL with libjpeg support.",
    2829             :                  pszFilename);
    2830             :         return CE_Failure;
    2831             : #endif  // HAVE_LIBJPEG
    2832             :     }
    2833          10 :     else if (sHeader.iCompression == RMF_COMPRESSION_DEM &&
    2834          10 :              eType == GDT_Int32 && nBands == RMF_DEM_BAND_COUNT)
    2835             :     {
    2836          10 :         Decompress = &DEMDecompress;
    2837          10 :         Compress = &DEMCompress;
    2838          10 :         SetMetadataItem("COMPRESSION", "RMF_DEM", "IMAGE_STRUCTURE");
    2839             :     }
    2840             :     else
    2841             :     {
    2842           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2843           0 :                  "Unknown compression #%d at file <%s>.", sHeader.iCompression,
    2844             :                  pszFilename);
    2845           0 :         return CE_Failure;
    2846             :     }
    2847             : 
    2848         243 :     return CE_None;
    2849             : }
    2850             : 
    2851         190 : void RMFDataset::WriteTileJobFunc(void *pData)
    2852             : {
    2853         190 :     RMFCompressionJob *psJob = static_cast<RMFCompressionJob *>(pData);
    2854         190 :     RMFDataset *poDS = psJob->poDS;
    2855             : 
    2856             :     GByte *pabyTileData;
    2857             :     size_t nTileSize;
    2858             : 
    2859         190 :     if (poDS->Compress)
    2860             :     {
    2861             :         // RMF doesn't store compressed tiles with size greater than 80% of
    2862             :         // uncompressed size
    2863         131 :         GUInt32 nMaxCompressedTileSize =
    2864         131 :             static_cast<GUInt32>((psJob->nUncompressedBytes * 8) / 10);
    2865             :         size_t nCompressedBytes =
    2866         262 :             poDS->Compress(psJob->pabyUncompressedData,
    2867         131 :                            static_cast<GUInt32>(psJob->nUncompressedBytes),
    2868             :                            psJob->pabyCompressedData, nMaxCompressedTileSize,
    2869             :                            psJob->nXSize, psJob->nYSize, poDS);
    2870         131 :         if (nCompressedBytes == 0)
    2871             :         {
    2872          28 :             pabyTileData = psJob->pabyUncompressedData;
    2873          28 :             nTileSize = psJob->nUncompressedBytes;
    2874             :         }
    2875             :         else
    2876             :         {
    2877         103 :             pabyTileData = psJob->pabyCompressedData;
    2878         103 :             nTileSize = nCompressedBytes;
    2879             :         }
    2880             :     }
    2881             :     else
    2882             :     {
    2883          59 :         pabyTileData = psJob->pabyUncompressedData;
    2884          59 :         nTileSize = psJob->nUncompressedBytes;
    2885             :     }
    2886             : 
    2887             :     {
    2888         190 :         CPLMutexHolder oHolder(poDS->poCompressData->hWriteTileMutex);
    2889         190 :         psJob->eResult = poDS->WriteRawTile(
    2890             :             psJob->nBlockXOff, psJob->nBlockYOff, pabyTileData, nTileSize);
    2891             :     }
    2892         190 :     if (poDS->poCompressData->oThreadPool.GetThreadCount() > 0)
    2893             :     {
    2894         188 :         CPLMutexHolder oHolder(poDS->poCompressData->hReadyJobMutex);
    2895          94 :         poDS->poCompressData->asReadyJobs.push_back(psJob);
    2896             :     }
    2897         190 : }
    2898             : 
    2899          55 : CPLErr RMFDataset::InitCompressorData(char **papszParamList)
    2900             : {
    2901             :     const char *pszNumThreads =
    2902          55 :         CSLFetchNameValue(papszParamList, "NUM_THREADS");
    2903          55 :     if (pszNumThreads == nullptr)
    2904          51 :         pszNumThreads = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
    2905             : 
    2906          55 :     int nThreads = 0;
    2907          55 :     if (pszNumThreads != nullptr)
    2908             :     {
    2909           8 :         nThreads = EQUAL(pszNumThreads, "ALL_CPUS") ? CPLGetNumCPUs()
    2910           4 :                                                     : atoi(pszNumThreads);
    2911             :     }
    2912             : 
    2913          55 :     if (nThreads < 0)
    2914             :     {
    2915           0 :         nThreads = 0;
    2916             :     }
    2917          55 :     if (nThreads > 1024)
    2918             :     {
    2919           0 :         nThreads = 1024;
    2920             :     }
    2921             : 
    2922          55 :     poCompressData = std::make_shared<RMFCompressData>();
    2923          55 :     if (nThreads > 0)
    2924             :     {
    2925           3 :         if (!poCompressData->oThreadPool.Setup(nThreads, nullptr, nullptr))
    2926             :         {
    2927           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2928             :                      "Can't setup %d compressor threads", nThreads);
    2929           0 :             return CE_Failure;
    2930             :         }
    2931             :     }
    2932             : 
    2933          55 :     poCompressData->asJobs.resize(nThreads + 1);
    2934             : 
    2935          55 :     size_t nMaxTileBytes =
    2936          55 :         sHeader.nTileWidth * sHeader.nTileHeight * sHeader.nBitDepth / 8;
    2937             :     size_t nCompressBufferSize =
    2938          55 :         2 * nMaxTileBytes * poCompressData->asJobs.size();
    2939         110 :     poCompressData->pabyBuffers =
    2940          55 :         static_cast<GByte *>(VSIMalloc(nCompressBufferSize));
    2941             : 
    2942          55 :     CPLDebug("RMF", "Setup %d compressor threads and allocate %lu bytes buffer",
    2943             :              nThreads, static_cast<unsigned long>(nCompressBufferSize));
    2944          55 :     if (poCompressData->pabyBuffers == nullptr)
    2945             :     {
    2946           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    2947             :                  "Can't allocate compress buffer of size %lu.",
    2948             :                  static_cast<unsigned long>(nCompressBufferSize));
    2949           0 :         return CE_Failure;
    2950             :     }
    2951             : 
    2952         122 :     for (size_t i = 0; i != poCompressData->asJobs.size(); ++i)
    2953             :     {
    2954          67 :         RMFCompressionJob &sJob(poCompressData->asJobs[i]);
    2955          67 :         sJob.pabyCompressedData =
    2956          67 :             poCompressData->pabyBuffers + 2 * i * nMaxTileBytes;
    2957          67 :         sJob.pabyUncompressedData = sJob.pabyCompressedData + nMaxTileBytes;
    2958          67 :         poCompressData->asReadyJobs.push_back(&sJob);
    2959             :     }
    2960             : 
    2961          55 :     if (nThreads > 0)
    2962             :     {
    2963           3 :         poCompressData->hReadyJobMutex = CPLCreateMutex();
    2964           3 :         CPLReleaseMutex(poCompressData->hReadyJobMutex);
    2965           3 :         poCompressData->hWriteTileMutex = CPLCreateMutex();
    2966           3 :         CPLReleaseMutex(poCompressData->hWriteTileMutex);
    2967             :     }
    2968             : 
    2969          55 :     return CE_None;
    2970             : }
    2971             : 
    2972         190 : CPLErr RMFDataset::WriteTile(int nBlockXOff, int nBlockYOff, GByte *pabyData,
    2973             :                              size_t nBytes, GUInt32 nRawXSize,
    2974             :                              GUInt32 nRawYSize)
    2975             : {
    2976         190 :     RMFCompressionJob *poJob = nullptr;
    2977         190 :     if (poCompressData == nullptr)
    2978             :     {
    2979           0 :         CPLError(CE_Failure, CPLE_AppDefined, "RMF: Compress data is null");
    2980           0 :         return CE_Failure;
    2981             :     }
    2982             : 
    2983         190 :     if (poCompressData->oThreadPool.GetThreadCount() > 0)
    2984             :     {
    2985          94 :         size_t nJobs(poCompressData->asJobs.size());
    2986             : 
    2987          94 :         poCompressData->oThreadPool.WaitCompletion(static_cast<int>(nJobs - 1));
    2988             : 
    2989         188 :         CPLMutexHolder oHolder(poCompressData->hReadyJobMutex);
    2990          94 :         CPLAssert(!poCompressData->asReadyJobs.empty());
    2991          94 :         poJob = poCompressData->asReadyJobs.front();
    2992          94 :         poCompressData->asReadyJobs.pop_front();
    2993             :     }
    2994             :     else
    2995             :     {
    2996          96 :         poJob = poCompressData->asReadyJobs.front();
    2997             :     }
    2998             : 
    2999         190 :     if (poJob->eResult != CE_None)
    3000             :     {
    3001             :         // One of the previous jobs is not done.
    3002             :         // Detailed debug message is already emitted from WriteRawTile
    3003           0 :         return poJob->eResult;
    3004             :     }
    3005         190 :     poJob->poDS = this;
    3006         190 :     poJob->eResult = CE_Failure;
    3007         190 :     poJob->nBlockXOff = nBlockXOff;
    3008         190 :     poJob->nBlockYOff = nBlockYOff;
    3009         190 :     poJob->nUncompressedBytes = nBytes;
    3010         190 :     poJob->nXSize = nRawXSize;
    3011         190 :     poJob->nYSize = nRawYSize;
    3012             : 
    3013         190 :     memcpy(poJob->pabyUncompressedData, pabyData, nBytes);
    3014             : 
    3015         190 :     if (poCompressData->oThreadPool.GetThreadCount() > 0)
    3016             :     {
    3017          94 :         if (!poCompressData->oThreadPool.SubmitJob(WriteTileJobFunc, poJob))
    3018             :         {
    3019           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    3020             :                      "Can't submit job to thread pool.");
    3021           0 :             return CE_Failure;
    3022             :         }
    3023             :     }
    3024             :     else
    3025             :     {
    3026          96 :         WriteTileJobFunc(poJob);
    3027          96 :         if (poJob->eResult != CE_None)
    3028             :         {
    3029           0 :             return poJob->eResult;
    3030             :         }
    3031             :     }
    3032             : 
    3033         190 :     return CE_None;
    3034             : }
    3035             : 
    3036         190 : CPLErr RMFDataset::WriteRawTile(int nBlockXOff, int nBlockYOff, GByte *pabyData,
    3037             :                                 size_t nTileBytes)
    3038             : {
    3039         190 :     CPLAssert(nBlockXOff >= 0 && nBlockYOff >= 0 && pabyData != nullptr &&
    3040             :               nTileBytes > 0);
    3041             : 
    3042         190 :     const GUInt32 nTile = nBlockYOff * nXTiles + nBlockXOff;
    3043             : 
    3044         190 :     vsi_l_offset nTileOffset = GetFileOffset(paiTiles[2 * nTile]);
    3045         190 :     size_t nTileSize = static_cast<size_t>(paiTiles[2 * nTile + 1]);
    3046             : 
    3047         190 :     if (nTileOffset && nTileSize <= nTileBytes)
    3048             :     {
    3049           0 :         if (VSIFSeekL(fp, nTileOffset, SEEK_SET) < 0)
    3050             :         {
    3051           0 :             CPLError(
    3052             :                 CE_Failure, CPLE_FileIO,
    3053             :                 "Can't seek to offset %ld in output file to write data.\n%s",
    3054           0 :                 static_cast<long>(nTileOffset), VSIStrerror(errno));
    3055           0 :             return CE_Failure;
    3056             :         }
    3057             :     }
    3058             :     else
    3059             :     {
    3060         190 :         if (VSIFSeekL(fp, 0, SEEK_END) < 0)
    3061             :         {
    3062           0 :             CPLError(
    3063             :                 CE_Failure, CPLE_FileIO,
    3064             :                 "Can't seek to offset %ld in output file to write data.\n%s",
    3065           0 :                 static_cast<long>(nTileOffset), VSIStrerror(errno));
    3066           0 :             return CE_Failure;
    3067             :         }
    3068         190 :         nTileOffset = VSIFTellL(fp);
    3069         190 :         vsi_l_offset nNewTileOffset = 0;
    3070         190 :         paiTiles[2 * nTile] = GetRMFOffset(nTileOffset, &nNewTileOffset);
    3071             : 
    3072         190 :         if (nTileOffset != nNewTileOffset)
    3073             :         {
    3074          23 :             if (VSIFSeekL(fp, nNewTileOffset, SEEK_SET) < 0)
    3075             :             {
    3076           0 :                 CPLError(CE_Failure, CPLE_FileIO,
    3077             :                          "Can't seek to offset %ld in output file to "
    3078             :                          "write data.\n%s",
    3079           0 :                          static_cast<long>(nNewTileOffset), VSIStrerror(errno));
    3080           0 :                 return CE_Failure;
    3081             :             }
    3082             :         }
    3083         190 :         bHeaderDirty = true;
    3084             :     }
    3085             : 
    3086             : #ifdef CPL_MSB
    3087             :     // Compressed tiles are already with proper byte order
    3088             :     if (eRMFType == RMFT_MTW && sHeader.iCompression == RMF_COMPRESSION_NONE)
    3089             :     {
    3090             :         // Byte swap can be done in place
    3091             :         if (sHeader.nBitDepth == 16)
    3092             :         {
    3093             :             for (size_t i = 0; i < nTileBytes; i += 2)
    3094             :                 CPL_SWAP16PTR(pabyData + i);
    3095             :         }
    3096             :         else if (sHeader.nBitDepth == 32)
    3097             :         {
    3098             :             for (size_t i = 0; i < nTileBytes; i += 4)
    3099             :                 CPL_SWAP32PTR(pabyData + i);
    3100             :         }
    3101             :         else if (sHeader.nBitDepth == 64)
    3102             :         {
    3103             :             for (size_t i = 0; i < nTileBytes; i += 8)
    3104             :                 CPL_SWAPDOUBLE(pabyData + i);
    3105             :         }
    3106             :     }
    3107             : #endif
    3108             : 
    3109         190 :     bool bOk = (VSIFWriteL(pabyData, 1, nTileBytes, fp) == nTileBytes);
    3110             : 
    3111         190 :     if (!bOk)
    3112             :     {
    3113           0 :         CPLError(CE_Failure, CPLE_FileIO,
    3114             :                  "Can't write tile with X offset %d and Y offset %d.\n%s",
    3115           0 :                  nBlockXOff, nBlockYOff, VSIStrerror(errno));
    3116           0 :         return CE_Failure;
    3117             :     }
    3118             : 
    3119         190 :     paiTiles[2 * nTile + 1] = static_cast<GUInt32>(nTileBytes);
    3120         190 :     bHeaderDirty = true;
    3121             : 
    3122         190 :     return CE_None;
    3123             : }
    3124             : 
    3125         352 : CPLErr RMFDataset::ReadTile(int nBlockXOff, int nBlockYOff, GByte *pabyData,
    3126             :                             size_t nRawBytes, GUInt32 nRawXSize,
    3127             :                             GUInt32 nRawYSize, bool &bNullTile)
    3128             : {
    3129         352 :     bNullTile = false;
    3130             : 
    3131         352 :     const GUInt32 nTile = nBlockYOff * nXTiles + nBlockXOff;
    3132         352 :     if (2 * nTile + 1 >= sHeader.nTileTblSize / sizeof(GUInt32))
    3133             :     {
    3134           0 :         return CE_Failure;
    3135             :     }
    3136         352 :     vsi_l_offset nTileOffset = GetFileOffset(paiTiles[2 * nTile]);
    3137         352 :     GUInt32 nTileBytes = paiTiles[2 * nTile + 1];
    3138             :     // RMF doesn't store compressed tiles with size greater than 80% of
    3139             :     // uncompressed size. But just in case, select twice as many.
    3140         352 :     GUInt32 nMaxTileBytes =
    3141         352 :         2 * sHeader.nTileWidth * sHeader.nTileHeight * sHeader.nBitDepth / 8;
    3142             : 
    3143         352 :     if (nTileBytes >= nMaxTileBytes)
    3144             :     {
    3145           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3146             :                  "Invalid tile size %lu at offset %ld. Must be less than %lu",
    3147             :                  static_cast<unsigned long>(nTileBytes),
    3148             :                  static_cast<long>(nTileOffset),
    3149             :                  static_cast<unsigned long>(nMaxTileBytes));
    3150           0 :         return CE_Failure;
    3151             :     }
    3152             : 
    3153         352 :     if (nTileOffset == 0)
    3154             :     {
    3155           1 :         bNullTile = true;
    3156           1 :         return CE_None;
    3157             :     }
    3158             : 
    3159             : #ifdef DEBUG
    3160         351 :     CPLDebug("RMF", "Read RawSize [%d, %d], nTileBytes %d, nRawBytes %d",
    3161             :              nRawXSize, nRawYSize, static_cast<int>(nTileBytes),
    3162             :              static_cast<int>(nRawBytes));
    3163             : #endif  // DEBUG
    3164             : 
    3165         351 :     if (VSIFSeekL(fp, nTileOffset, SEEK_SET) < 0)
    3166             :     {
    3167             :         // XXX: We will not report error here, because file just may be
    3168             :         // in update state and data for this block will be available later
    3169           0 :         if (eAccess == GA_Update)
    3170           0 :             return CE_None;
    3171             : 
    3172           0 :         CPLError(CE_Failure, CPLE_FileIO,
    3173             :                  "Can't seek to offset %ld in input file to read data.\n%s",
    3174           0 :                  static_cast<long>(nTileOffset), VSIStrerror(errno));
    3175           0 :         return CE_Failure;
    3176             :     }
    3177             : 
    3178         351 :     if (Decompress == nullptr || nTileBytes == nRawBytes)
    3179             :     {
    3180         166 :         if (nTileBytes != nRawBytes)
    3181             :         {
    3182           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3183             :                      "RMF: Invalid tile size %lu, expected %lu",
    3184             :                      static_cast<unsigned long>(nTileBytes),
    3185             :                      static_cast<unsigned long>(nRawBytes));
    3186           0 :             return CE_Failure;
    3187             :         }
    3188             : 
    3189         166 :         if (VSIFReadL(pabyData, 1, nRawBytes, fp) < nRawBytes)
    3190             :         {
    3191           0 :             CPLError(CE_Failure, CPLE_FileIO,
    3192             :                      "RMF: Can't read at offset %lu from input file.\n%s",
    3193             :                      static_cast<unsigned long>(nTileOffset),
    3194           0 :                      VSIStrerror(errno));
    3195           0 :             return CE_Failure;
    3196             :         }
    3197             : 
    3198             : #ifdef CPL_MSB
    3199             :         if (eRMFType == RMFT_MTW)
    3200             :         {
    3201             :             if (sHeader.nBitDepth == 16)
    3202             :             {
    3203             :                 for (GUInt32 i = 0; i < nRawBytes; i += 2)
    3204             :                     CPL_SWAP16PTR(pabyData + i);
    3205             :             }
    3206             :             else if (sHeader.nBitDepth == 32)
    3207             :             {
    3208             :                 for (GUInt32 i = 0; i < nRawBytes; i += 4)
    3209             :                     CPL_SWAP32PTR(pabyData + i);
    3210             :             }
    3211             :             else if (sHeader.nBitDepth == 64)
    3212             :             {
    3213             :                 for (GUInt32 i = 0; i < nRawBytes; i += 8)
    3214             :                     CPL_SWAPDOUBLE(pabyData + i);
    3215             :             }
    3216             :         }
    3217             : #endif
    3218         166 :         return CE_None;
    3219             :     }
    3220             : 
    3221         185 :     if (pabyDecompressBuffer == nullptr)
    3222             :     {
    3223          21 :         pabyDecompressBuffer =
    3224          21 :             static_cast<GByte *>(VSIMalloc(std::max(1U, nMaxTileBytes)));
    3225          21 :         if (!pabyDecompressBuffer)
    3226             :         {
    3227           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    3228             :                      "Can't allocate decompress buffer of size %lu.\n%s",
    3229             :                      static_cast<unsigned long>(nMaxTileBytes),
    3230           0 :                      VSIStrerror(errno));
    3231           0 :             return CE_Failure;
    3232             :         }
    3233             :     }
    3234             : 
    3235         185 :     if (VSIFReadL(pabyDecompressBuffer, 1, nTileBytes, fp) < nTileBytes)
    3236             :     {
    3237           0 :         CPLError(CE_Failure, CPLE_FileIO,
    3238             :                  "RMF: Can't read at offset %lu from input file.\n%s",
    3239           0 :                  static_cast<unsigned long>(nTileOffset), VSIStrerror(errno));
    3240           0 :         return CE_Failure;
    3241             :     }
    3242             : 
    3243             :     size_t nDecompressedSize =
    3244         185 :         Decompress(pabyDecompressBuffer, nTileBytes, pabyData,
    3245             :                    static_cast<GUInt32>(nRawBytes), nRawXSize, nRawYSize);
    3246             : 
    3247         185 :     if (nDecompressedSize != static_cast<size_t>(nRawBytes))
    3248             :     {
    3249           0 :         CPLError(CE_Failure, CPLE_FileIO,
    3250             :                  "Can't decompress tile xOff %d yOff %d. "
    3251             :                  "Raw tile size is %lu but decompressed is %lu. "
    3252             :                  "Compressed tile size is %lu",
    3253             :                  nBlockXOff, nBlockYOff, static_cast<unsigned long>(nRawBytes),
    3254             :                  static_cast<unsigned long>(nDecompressedSize),
    3255             :                  static_cast<unsigned long>(nTileBytes));
    3256           0 :         return CE_Failure;
    3257             :     }
    3258             :     // We don't need to swap bytes here,
    3259             :     // because decompressed data is in proper byte order
    3260         185 :     return CE_None;
    3261             : }
    3262             : 
    3263         277 : void RMFDataset::SetupNBits()
    3264             : {
    3265         277 :     int nBitDepth = 0;
    3266         277 :     if (sHeader.nBitDepth < 8 && nBands == 1)
    3267             :     {
    3268           6 :         nBitDepth = static_cast<int>(sHeader.nBitDepth);
    3269             :     }
    3270         271 :     else if (sHeader.nBitDepth == 16 && nBands == 3 && eRMFType == RMFT_RSW)
    3271             :     {
    3272           0 :         nBitDepth = 5;
    3273             :     }
    3274             : 
    3275         277 :     if (nBitDepth > 0)
    3276             :     {
    3277           6 :         char szNBits[32] = {};
    3278           6 :         snprintf(szNBits, sizeof(szNBits), "%d", nBitDepth);
    3279          12 :         for (int iBand = 1; iBand <= nBands; iBand++)
    3280             :         {
    3281           6 :             GetRasterBand(iBand)->SetMetadataItem("NBITS", szNBits,
    3282           6 :                                                   "IMAGE_STRUCTURE");
    3283             :         }
    3284             :     }
    3285         277 : }
    3286             : 
    3287             : /************************************************************************/
    3288             : /*                        GDALRegister_RMF()                            */
    3289             : /************************************************************************/
    3290             : 
    3291        1889 : void GDALRegister_RMF()
    3292             : 
    3293             : {
    3294        1889 :     if (GDALGetDriverByName("RMF") != nullptr)
    3295         282 :         return;
    3296             : 
    3297        1607 :     GDALDriver *poDriver = new GDALDriver();
    3298             : 
    3299        1607 :     poDriver->SetDescription("RMF");
    3300        1607 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    3301        1607 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Raster Matrix Format");
    3302        1607 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/rmf.html");
    3303        1607 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "rsw");
    3304        1607 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
    3305        1607 :                               "Byte Int16 Int32 Float64");
    3306        1607 :     poDriver->SetMetadataItem(
    3307             :         GDAL_DMD_CREATIONOPTIONLIST,
    3308             :         "<CreationOptionList>"
    3309             :         "   <Option name='MTW' type='boolean' description='Create MTW DEM "
    3310             :         "matrix'/>"
    3311             :         "   <Option name='BLOCKXSIZE' type='int' description='Tile Width'/>"
    3312             :         "   <Option name='BLOCKYSIZE' type='int' description='Tile Height'/>"
    3313             :         "   <Option name='RMFHUGE' type='string-select' description='Creation "
    3314             :         "of huge RMF file (Supported by GIS Panorama since v11)'>"
    3315             :         "     <Value>NO</Value>"
    3316             :         "     <Value>YES</Value>"
    3317             :         "     <Value>IF_SAFER</Value>"
    3318             :         "   </Option>"
    3319             :         "   <Option name='COMPRESS' type='string-select' default='NONE'>"
    3320             :         "     <Value>NONE</Value>"
    3321             :         "     <Value>LZW</Value>"
    3322             :         "     <Value>JPEG</Value>"
    3323             :         "     <Value>RMF_DEM</Value>"
    3324             :         "   </Option>"
    3325             :         "   <Option name='JPEG_QUALITY' type='int' description='JPEG quality "
    3326             :         "1-100' default='75'/>"
    3327             :         "   <Option name='NUM_THREADS' type='string' description='Number of "
    3328             :         "worker threads for compression. Can be set to ALL_CPUS' default='1'/>"
    3329        1607 :         "</CreationOptionList>");
    3330        1607 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    3331             : 
    3332        1607 :     poDriver->pfnIdentify = RMFDataset::Identify;
    3333        1607 :     poDriver->pfnOpen = RMFDataset::Open;
    3334        1607 :     poDriver->pfnCreate = RMFDataset::Create;
    3335        1607 :     poDriver->SetMetadataItem(
    3336             :         GDAL_DMD_OPENOPTIONLIST,
    3337             :         "<OpenOptionList>"
    3338             :         "  <Option name='RMF_SET_VERTCS' type='string' description='Layers "
    3339             :         "spatial reference will include vertical coordinate system description "
    3340             :         "if exist' default='NO'/>"
    3341        1607 :         "</OpenOptionList>");
    3342             : 
    3343        1607 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    3344             : }
    3345             : 
    3346             : /************************************************************************/
    3347             : /*                            RMFCompressData                           */
    3348             : /************************************************************************/
    3349             : 
    3350          55 : RMFCompressData::RMFCompressData() : pabyBuffers(nullptr)
    3351             : {
    3352          55 : }
    3353             : 
    3354          55 : RMFCompressData::~RMFCompressData()
    3355             : {
    3356          55 :     if (pabyBuffers != nullptr)
    3357             :     {
    3358          55 :         VSIFree(pabyBuffers);
    3359             :     }
    3360             : 
    3361          55 :     if (hWriteTileMutex != nullptr)
    3362             :     {
    3363           3 :         CPLDestroyMutex(hWriteTileMutex);
    3364             :     }
    3365             : 
    3366          55 :     if (hReadyJobMutex != nullptr)
    3367             :     {
    3368           3 :         CPLDestroyMutex(hReadyJobMutex);
    3369             :     }
    3370          55 : }
    3371             : 
    3372             : GDALSuggestedBlockAccessPattern
    3373          21 : RMFRasterBand::GetSuggestedBlockAccessPattern() const
    3374             : {
    3375          21 :     return GSBAP_RANDOM;
    3376             : }
    3377             : 
    3378        1507 : CPLErr RMFDataset::SetMetadataItem(const char *pszName, const char *pszValue,
    3379             :                                    const char *pszDomain)
    3380             : {
    3381        1507 :     if (GetAccess() == GA_Update)
    3382             :     {
    3383         190 :         CPLDebug("RMF", "SetMetadataItem: %s=%s", pszName, pszValue);
    3384         190 :         if (EQUAL(pszName, MD_NAME_KEY))
    3385             :         {
    3386          20 :             memcpy(sHeader.byName, pszValue,
    3387             :                    CPLStrnlen(pszValue, RMF_NAME_SIZE));
    3388          20 :             bHeaderDirty = true;
    3389             :         }
    3390         170 :         else if (EQUAL(pszName, MD_SCALE_KEY) && CPLStrnlen(pszValue, 10) > 4)
    3391             :         {
    3392          20 :             sHeader.dfScale = atof(pszValue + 4);
    3393          20 :             sHeader.dfResolution = sHeader.dfScale / sHeader.dfPixelSize;
    3394          20 :             bHeaderDirty = true;
    3395             :         }
    3396         150 :         else if (EQUAL(pszName, MD_FRAME_KEY))
    3397             :         {
    3398           0 :             bHeaderDirty = true;
    3399             :         }
    3400             :     }
    3401        1507 :     return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    3402             : }
    3403             : 
    3404          39 : CPLErr RMFDataset::SetMetadata(char **papszMetadata, const char *pszDomain)
    3405             : {
    3406          39 :     if (GetAccess() == GA_Update)
    3407             :     {
    3408          39 :         auto pszName = CSLFetchNameValue(papszMetadata, MD_NAME_KEY);
    3409          39 :         if (pszName != nullptr)
    3410             :         {
    3411          21 :             memcpy(sHeader.byName, pszName, CPLStrnlen(pszName, RMF_NAME_SIZE));
    3412          21 :             bHeaderDirty = true;
    3413             : 
    3414          21 :             CPLDebug("RMF", "SetMetadata: %s", pszName);
    3415             :         }
    3416          39 :         auto pszScale = CSLFetchNameValue(papszMetadata, MD_SCALE_KEY);
    3417          39 :         if (pszScale != nullptr && CPLStrnlen(pszScale, 10) > 4)
    3418             :         {
    3419          21 :             sHeader.dfScale = atof(pszScale + 4);
    3420          21 :             sHeader.dfResolution = sHeader.dfScale / sHeader.dfPixelSize;
    3421          21 :             bHeaderDirty = true;
    3422             : 
    3423          21 :             CPLDebug("RMF", "SetMetadata: %s", pszScale);
    3424             :         }
    3425          39 :         auto pszFrame = CSLFetchNameValue(papszMetadata, MD_FRAME_KEY);
    3426          39 :         if (pszFrame != nullptr)
    3427             :         {
    3428           2 :             bHeaderDirty = true;
    3429             : 
    3430           2 :             CPLDebug("RMF", "SetMetadata: %s", pszFrame);
    3431             :         }
    3432             :     }
    3433          39 :     return GDALDataset::SetMetadata(papszMetadata, pszDomain);
    3434             : }

Generated by: LCOV version 1.14