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

Generated by: LCOV version 1.14