• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.23
/frmts/rmf/rmfdataset.cpp
1
/******************************************************************************
2
 *
3
 * Project:  Raster Matrix Format
4
 * Purpose:  Read/write raster files used in GIS "Integratsia"
5
 *           (also known as "Panorama" GIS).
6
 * Author:   Andrey Kiselev, dron@ak4719.spb.edu
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2005, Andrey Kiselev <dron@ak4719.spb.edu>
10
 * Copyright (c) 2007-2012, Even Rouault <even dot rouault at spatialys.com>
11
 * Copyright (c) 2023, NextGIS <info@nextgis.com>
12
 *
13
 * SPDX-License-Identifier: MIT
14
 ****************************************************************************/
15
#include <algorithm>
16
#include <array>
17
#include <limits>
18

19
#include "cpl_string.h"
20
#include "gdal_frmts.h"
21
#include "ogr_spatialref.h"
22

23
#include "rmfdataset.h"
24

25
#include "cpl_safemaths.hpp"
26

27
constexpr int RMF_DEFAULT_BLOCKXSIZE = 256;
28
constexpr int RMF_DEFAULT_BLOCKYSIZE = 256;
29

30
static const char RMF_SigRSW[] = {'R', 'S', 'W', '\0'};
31
static const char RMF_SigRSW_BE[] = {'\0', 'W', 'S', 'R'};
32
static const char RMF_SigMTW[] = {'M', 'T', 'W', '\0'};
33

34
static const char RMF_UnitsEmpty[] = "";
35
static const char RMF_UnitsM[] = "m";
36
static const char RMF_UnitsCM[] = "cm";
37
static const char RMF_UnitsDM[] = "dm";
38
static const char RMF_UnitsMM[] = "mm";
39

40
constexpr double RMF_DEFAULT_SCALE = 10000.0;
41
constexpr double RMF_DEFAULT_RESOLUTION = 100.0;
42

43
constexpr const char *MD_VERSION_KEY = "VERSION";
44
constexpr const char *MD_NAME_KEY = "NAME";
45
constexpr const char *MD_SCALE_KEY = "SCALE";
46
constexpr const char *MD_FRAME_KEY = "FRAME";
47

48
constexpr const char *MD_MATH_BASE_MAP_TYPE_KEY = "MATH_BASE.Map type";
49
constexpr const char *MD_MATH_BASE_PROJECTION_KEY = "MATH_BASE.Projection";
50

51
constexpr int nMaxFramePointCount = 2048;
52
constexpr GInt32 nPolygonType =
53
    2147385342;  // 2147385342 magic number for polygon
54

55
/* -------------------------------------------------------------------- */
56
/*  Note: Due to the fact that in the early versions of RMF             */
57
/*  format the field of the iEPSGCode was marked as a 'reserved',       */
58
/*  in the header on its place in many cases garbage values were written.*/
59
/*  Most of them can be weeded out by the minimum EPSG code value.      */
60
/*                                                                      */
61
/*  see: Surveying and Positioning Guidance Note Number 7, part 1       */
62
/*       Using the EPSG Geodetic Parameter Dataset p. 22                */
63
/*       http://www.epsg.org/Portals/0/373-07-1.pdf                     */
64
/* -------------------------------------------------------------------- */
65
constexpr GInt32 RMF_EPSG_MIN_CODE = 1024;
66

67
static char *RMFUnitTypeToStr(GUInt32 iElevationUnit)
74✔
68
{
69
    switch (iElevationUnit)
74✔
70
    {
71
        case 0:
68✔
72
            return CPLStrdup(RMF_UnitsM);
68✔
73
        case 1:
×
74
            return CPLStrdup(RMF_UnitsDM);
×
75
        case 2:
1✔
76
            return CPLStrdup(RMF_UnitsCM);
1✔
77
        case 3:
5✔
78
            return CPLStrdup(RMF_UnitsMM);
5✔
79
        default:
×
80
            return CPLStrdup(RMF_UnitsEmpty);
×
81
    }
82
}
83

84
static GUInt32 RMFStrToUnitType(const char *pszUnit, int *pbSuccess = nullptr)
80✔
85
{
86
    if (pbSuccess != nullptr)
80✔
87
    {
88
        *pbSuccess = TRUE;
3✔
89
    }
90
    if (EQUAL(pszUnit, RMF_UnitsM))
80✔
91
        return 0;
×
92
    else if (EQUAL(pszUnit, RMF_UnitsDM))
80✔
93
        return 1;
×
94
    else if (EQUAL(pszUnit, RMF_UnitsCM))
80✔
95
        return 2;
1✔
96
    else if (EQUAL(pszUnit, RMF_UnitsMM))
79✔
97
        return 3;
1✔
98
    else
99
    {
100
        // There is no 'invalid unit' in RMF format. So meter is default...
101
        if (pbSuccess != nullptr)
78✔
102
        {
103
            *pbSuccess = FALSE;
1✔
104
        }
105
        return 0;
78✔
106
    }
107
}
108

109
/************************************************************************/
110
/* ==================================================================== */
111
/*                            RMFRasterBand                             */
112
/* ==================================================================== */
113
/************************************************************************/
114

115
/************************************************************************/
116
/*                           RMFRasterBand()                            */
117
/************************************************************************/
118

119
RMFRasterBand::RMFRasterBand(RMFDataset *poDSIn, int nBandIn,
419✔
120
                             GDALDataType eType)
419✔
121
    : nLastTileWidth(poDSIn->GetRasterXSize() % poDSIn->sHeader.nTileWidth),
838✔
122
      nLastTileHeight(poDSIn->GetRasterYSize() % poDSIn->sHeader.nTileHeight),
838✔
123
      nDataSize(GDALGetDataTypeSizeBytes(eType))
419✔
124
{
125
    poDS = poDSIn;
419✔
126
    nBand = nBandIn;
419✔
127

128
    eDataType = eType;
419✔
129
    nBlockXSize = poDSIn->sHeader.nTileWidth;
419✔
130
    nBlockYSize = poDSIn->sHeader.nTileHeight;
419✔
131
    nBlockSize = nBlockXSize * nBlockYSize;
419✔
132
    nBlockBytes = nBlockSize * nDataSize;
419✔
133

134
#ifdef DEBUG
135
    CPLDebug("RMF",
419✔
136
             "Band %d: tile width is %d, tile height is %d, "
137
             " last tile width %u, last tile height %u, "
138
             "bytes per pixel is %d, data type size is %d",
139
             nBand, nBlockXSize, nBlockYSize, nLastTileWidth, nLastTileHeight,
140
             poDSIn->sHeader.nBitDepth / 8, nDataSize);
419✔
141
#endif
142
}
419✔
143

144
/************************************************************************/
145
/*                           ~RMFRasterBand()                           */
146
/************************************************************************/
147

148
RMFRasterBand::~RMFRasterBand()
838✔
149
{
150
}
838✔
151

152
/************************************************************************/
153
/*                             IReadBlock()                             */
154
/************************************************************************/
155

156
CPLErr RMFRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
454✔
157
{
158
    RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
454✔
159

160
    CPLAssert(poGDS != nullptr && nBlockXOff >= 0 && nBlockYOff >= 0 &&
454✔
161
              pImage != nullptr);
162

163
    memset(pImage, 0, nBlockBytes);
454✔
164

165
    GUInt32 nRawXSize = nBlockXSize;
454✔
166
    GUInt32 nRawYSize = nBlockYSize;
454✔
167

168
    if (nLastTileWidth &&
454✔
169
        static_cast<GUInt32>(nBlockXOff) == poGDS->nXTiles - 1)
381✔
170
        nRawXSize = nLastTileWidth;
176✔
171

172
    if (nLastTileHeight &&
454✔
173
        static_cast<GUInt32>(nBlockYOff) == poGDS->nYTiles - 1)
365✔
174
        nRawYSize = nLastTileHeight;
198✔
175

176
    GUInt32 nRawBytes = nRawXSize * nRawYSize * poGDS->sHeader.nBitDepth / 8;
454✔
177

178
    // Direct read optimization
179
    if (poGDS->nBands == 1 && poGDS->sHeader.nBitDepth >= 8 &&
454✔
180
        nRawXSize == static_cast<GUInt32>(nBlockXSize) &&
183✔
181
        nRawYSize == static_cast<GUInt32>(nBlockYSize))
71✔
182
    {
183
        bool bNullTile = false;
67✔
184
        if (CE_None != poGDS->ReadTile(nBlockXOff, nBlockYOff,
67✔
185
                                       reinterpret_cast<GByte *>(pImage),
186
                                       nRawBytes, nRawXSize, nRawYSize,
187
                                       bNullTile))
188
        {
189
            CPLError(CE_Failure, CPLE_FileIO,
×
190
                     "Failed to read tile xOff %d yOff %d", nBlockXOff,
191
                     nBlockYOff);
192
            return CE_Failure;
×
193
        }
194
        if (bNullTile)
67✔
195
        {
196
            const int nChunkSize =
197
                std::max(1, GDALGetDataTypeSizeBytes(eDataType));
1✔
198
            const GPtrDiff_t nWords =
1✔
199
                static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize;
1✔
200
            GDALCopyWords64(&poGDS->sHeader.dfNoData, GDT_Float64, 0, pImage,
1✔
201
                            eDataType, nChunkSize, nWords);
202
        }
203
        return CE_None;
67✔
204
    }
205
#ifdef DEBUG
206
    CPLDebug("RMF", "IReadBlock nBand %d, RawSize [%d, %d], Bits %u", nBand,
387✔
207
             nRawXSize, nRawYSize, poGDS->sHeader.nBitDepth);
208
#endif  // DEBUG
209
    if (poGDS->pabyCurrentTile == nullptr ||
387✔
210
        poGDS->nCurrentTileXOff != nBlockXOff ||
290✔
211
        poGDS->nCurrentTileYOff != nBlockYOff ||
102✔
212
        poGDS->nCurrentTileBytes != nRawBytes)
102✔
213
    {
214
        if (poGDS->pabyCurrentTile == nullptr)
285✔
215
        {
216
            GUInt32 nMaxTileBytes = poGDS->sHeader.nTileWidth *
97✔
217
                                    poGDS->sHeader.nTileHeight *
97✔
218
                                    poGDS->sHeader.nBitDepth / 8;
97✔
219
            poGDS->pabyCurrentTile = reinterpret_cast<GByte *>(
97✔
220
                VSIMalloc(std::max(1U, nMaxTileBytes)));
97✔
221
            if (!poGDS->pabyCurrentTile)
97✔
222
            {
223
                CPLError(CE_Failure, CPLE_OutOfMemory,
×
224
                         "Can't allocate tile block of size %lu.\n%s",
225
                         static_cast<unsigned long>(nMaxTileBytes),
226
                         VSIStrerror(errno));
×
227
                poGDS->nCurrentTileBytes = 0;
×
228
                return CE_Failure;
×
229
            }
230
        }
231

232
        poGDS->nCurrentTileXOff = nBlockXOff;
285✔
233
        poGDS->nCurrentTileYOff = nBlockYOff;
285✔
234
        poGDS->nCurrentTileBytes = nRawBytes;
285✔
235

236
        if (CE_None != poGDS->ReadTile(nBlockXOff, nBlockYOff,
285✔
237
                                       poGDS->pabyCurrentTile, nRawBytes,
238
                                       nRawXSize, nRawYSize,
239
                                       poGDS->bCurrentTileIsNull))
285✔
240
        {
241
            CPLError(CE_Failure, CPLE_FileIO,
×
242
                     "Failed to read tile xOff %d yOff %d", nBlockXOff,
243
                     nBlockYOff);
244
            poGDS->nCurrentTileBytes = 0;
×
245
            return CE_Failure;
×
246
        }
247
    }
248

249
    /* -------------------------------------------------------------------- */
250
    /*  Deinterleave pixels from input buffer.                              */
251
    /* -------------------------------------------------------------------- */
252

253
    if (poGDS->bCurrentTileIsNull)
387✔
254
    {
255
        const int nChunkSize = std::max(1, GDALGetDataTypeSizeBytes(eDataType));
×
256
        const GPtrDiff_t nWords =
×
257
            static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize;
×
258
        GDALCopyWords64(&poGDS->sHeader.dfNoData, GDT_Float64, 0, pImage,
×
259
                        eDataType, nChunkSize, nWords);
260
        return CE_None;
×
261
    }
262
    else if ((poGDS->eRMFType == RMFT_RSW &&
387✔
263
              (poGDS->sHeader.nBitDepth == 8 ||
283✔
264
               poGDS->sHeader.nBitDepth == 24 ||
271✔
265
               poGDS->sHeader.nBitDepth == 32)) ||
9✔
266
             (poGDS->eRMFType == RMFT_MTW))
107✔
267
    {
268
        const size_t nTilePixelSize = poGDS->sHeader.nBitDepth / 8;
384✔
269
        const size_t nTileLineSize = nTilePixelSize * nRawXSize;
384✔
270
        const size_t nBlockLineSize =
384✔
271
            static_cast<size_t>(nDataSize) * nBlockXSize;
384✔
272
        int iDstBand = (poGDS->nBands - nBand);
384✔
273
        for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
50,423✔
274
        {
275
            GByte *pabySrc;
276
            GByte *pabyDst;
277
            pabySrc = poGDS->pabyCurrentTile + iLine * nTileLineSize +
50,039✔
278
                      iDstBand * nDataSize;
50,039✔
279
            pabyDst =
50,039✔
280
                reinterpret_cast<GByte *>(pImage) + iLine * nBlockLineSize;
50,039✔
281
            GDALCopyWords(pabySrc, eDataType, static_cast<int>(nTilePixelSize),
50,039✔
282
                          pabyDst, eDataType, static_cast<int>(nDataSize),
50,039✔
283
                          nRawXSize);
284
        }
285
        return CE_None;
384✔
286
    }
287
    else if (poGDS->eRMFType == RMFT_RSW && poGDS->sHeader.nBitDepth == 16 &&
3✔
288
             poGDS->nBands == 3)
×
289
    {
290
        const size_t nTilePixelBits = poGDS->sHeader.nBitDepth;
×
291
        const size_t nTileLineSize = nTilePixelBits * nRawXSize / 8;
×
292
        const size_t nBlockLineSize =
×
293
            static_cast<size_t>(nDataSize) * nBlockXSize;
×
294

295
        for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
×
296
        {
297
            GUInt16 *pabySrc;
298
            GByte *pabyDst;
299
            pabySrc = reinterpret_cast<GUInt16 *>(poGDS->pabyCurrentTile +
×
300
                                                  iLine * nTileLineSize);
×
301
            pabyDst =
×
302
                reinterpret_cast<GByte *>(pImage) + iLine * nBlockLineSize;
×
303

304
            for (GUInt32 i = 0; i < nRawXSize; i++)
×
305
            {
306
                switch (nBand)
×
307
                {
308
                    case 1:
×
309
                        pabyDst[i] =
×
310
                            static_cast<GByte>((pabySrc[i] & 0x7c00) >> 7);
×
311
                        break;
×
312
                    case 2:
×
313
                        pabyDst[i] =
×
314
                            static_cast<GByte>((pabySrc[i] & 0x03e0) >> 2);
×
315
                        break;
×
316
                    case 3:
×
317
                        pabyDst[i] =
×
318
                            static_cast<GByte>((pabySrc[i] & 0x1F) << 3);
×
319
                        break;
×
320
                    default:
×
321
                        break;
×
322
                }
323
            }
324
        }
325
        return CE_None;
×
326
    }
327
    else if (poGDS->eRMFType == RMFT_RSW && poGDS->nBands == 1 &&
3✔
328
             poGDS->sHeader.nBitDepth == 4)
3✔
329
    {
330
        if (poGDS->nCurrentTileBytes != (nBlockSize + 1) / 2)
2✔
331
        {
332
            CPLError(CE_Failure, CPLE_AppDefined,
×
333
                     "Tile has %d bytes, %d were expected",
334
                     poGDS->nCurrentTileBytes, (nBlockSize + 1) / 2);
×
335
            return CE_Failure;
×
336
        }
337

338
        const size_t nTilePixelBits = poGDS->sHeader.nBitDepth;
2✔
339
        const size_t nTileLineSize = nTilePixelBits * nRawXSize / 8;
2✔
340
        const size_t nBlockLineSize =
2✔
341
            static_cast<size_t>(nDataSize) * nBlockXSize;
2✔
342

343
        for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
464✔
344
        {
345
            GByte *pabySrc;
346
            GByte *pabyDst;
347
            pabySrc = poGDS->pabyCurrentTile + iLine * nTileLineSize;
462✔
348
            pabyDst =
462✔
349
                reinterpret_cast<GByte *>(pImage) + iLine * nBlockLineSize;
462✔
350
            for (GUInt32 i = 0; i < nRawXSize; ++i)
112,266✔
351
            {
352
                if (i & 0x01)
111,804✔
353
                    pabyDst[i] = (*pabySrc++ & 0xF0) >> 4;
55,902✔
354
                else
355
                    pabyDst[i] = *pabySrc & 0x0F;
55,902✔
356
            }
357
        }
358
        return CE_None;
2✔
359
    }
360
    else if (poGDS->eRMFType == RMFT_RSW && poGDS->nBands == 1 &&
1✔
361
             poGDS->sHeader.nBitDepth == 1)
1✔
362
    {
363
        if (poGDS->nCurrentTileBytes != (nBlockSize + 7) / 8)
1✔
364
        {
365
            CPLError(CE_Failure, CPLE_AppDefined,
×
366
                     "Tile has %d bytes, %d were expected",
367
                     poGDS->nCurrentTileBytes, (nBlockSize + 7) / 8);
×
368
            return CE_Failure;
×
369
        }
370

371
        const size_t nTilePixelBits = poGDS->sHeader.nBitDepth;
1✔
372
        const size_t nTileLineSize = nTilePixelBits * nRawXSize / 8;
1✔
373
        const size_t nBlockLineSize =
1✔
374
            static_cast<size_t>(nDataSize) * nBlockXSize;
1✔
375

376
        for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
232✔
377
        {
378
            GByte *pabySrc;
379
            GByte *pabyDst;
380
            pabySrc = poGDS->pabyCurrentTile + iLine * nTileLineSize;
231✔
381
            pabyDst =
231✔
382
                reinterpret_cast<GByte *>(pImage) + iLine * nBlockLineSize;
231✔
383

384
            for (GUInt32 i = 0; i < nRawXSize; ++i)
57,519✔
385
            {
386
                switch (i & 0x7)
57,288✔
387
                {
388
                    case 0:
7,161✔
389
                        pabyDst[i] = (*pabySrc & 0x80) >> 7;
7,161✔
390
                        break;
7,161✔
391
                    case 1:
7,161✔
392
                        pabyDst[i] = (*pabySrc & 0x40) >> 6;
7,161✔
393
                        break;
7,161✔
394
                    case 2:
7,161✔
395
                        pabyDst[i] = (*pabySrc & 0x20) >> 5;
7,161✔
396
                        break;
7,161✔
397
                    case 3:
7,161✔
398
                        pabyDst[i] = (*pabySrc & 0x10) >> 4;
7,161✔
399
                        break;
7,161✔
400
                    case 4:
7,161✔
401
                        pabyDst[i] = (*pabySrc & 0x08) >> 3;
7,161✔
402
                        break;
7,161✔
403
                    case 5:
7,161✔
404
                        pabyDst[i] = (*pabySrc & 0x04) >> 2;
7,161✔
405
                        break;
7,161✔
406
                    case 6:
7,161✔
407
                        pabyDst[i] = (*pabySrc & 0x02) >> 1;
7,161✔
408
                        break;
7,161✔
409
                    case 7:
7,161✔
410
                        pabyDst[i] = *pabySrc++ & 0x01;
7,161✔
411
                        break;
7,161✔
412
                    default:
×
413
                        break;
×
414
                }
415
            }
416
        }
417
        return CE_None;
1✔
418
    }
419

420
    CPLError(CE_Failure, CPLE_AppDefined,
×
421
             "Invalid block data type. BitDepth %d, nBands %d",
422
             static_cast<int>(poGDS->sHeader.nBitDepth), poGDS->nBands);
×
423

424
    return CE_Failure;
×
425
}
426

427
/************************************************************************/
428
/*                            IWriteBlock()                             */
429
/************************************************************************/
430

431
CPLErr RMFRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage)
454✔
432
{
433
    CPLAssert(poDS != nullptr && nBlockXOff >= 0 && nBlockYOff >= 0 &&
454✔
434
              pImage != nullptr);
435

436
    RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
454✔
437

438
    // First drop current tile read by IReadBlock
439
    poGDS->nCurrentTileBytes = 0;
454✔
440

441
    GUInt32 nRawXSize = nBlockXSize;
454✔
442
    GUInt32 nRawYSize = nBlockYSize;
454✔
443

444
    if (nLastTileWidth &&
454✔
445
        static_cast<GUInt32>(nBlockXOff) == poGDS->nXTiles - 1)
426✔
446
        nRawXSize = nLastTileWidth;
103✔
447

448
    if (nLastTileHeight &&
454✔
449
        static_cast<GUInt32>(nBlockYOff) == poGDS->nYTiles - 1)
402✔
450
        nRawYSize = nLastTileHeight;
136✔
451

452
    const size_t nTilePixelSize =
454✔
453
        static_cast<size_t>(nDataSize) * poGDS->nBands;
454✔
454
    const size_t nTileLineSize = nTilePixelSize * nRawXSize;
454✔
455
    const size_t nTileSize = nTileLineSize * nRawYSize;
454✔
456
    const size_t nBlockLineSize = static_cast<size_t>(nDataSize) * nBlockXSize;
454✔
457

458
#ifdef DEBUG
459
    CPLDebug(
454✔
460
        "RMF",
461
        "IWriteBlock BlockSize [%d, %d], RawSize [%d, %d], size %d, nBand %d",
462
        nBlockXSize, nBlockYSize, nRawXSize, nRawYSize,
463
        static_cast<int>(nTileSize), nBand);
464
#endif  // DEBUG
465

466
    if (poGDS->nBands == 1 && nRawXSize == static_cast<GUInt32>(nBlockXSize) &&
454✔
467
        nRawYSize == static_cast<GUInt32>(nBlockYSize))
24✔
468
    {  // Immediate write
469
        return poGDS->WriteTile(
46✔
470
            nBlockXOff, nBlockYOff, reinterpret_cast<GByte *>(pImage),
471
            static_cast<size_t>(nRawXSize) * nRawYSize * nDataSize, nRawXSize,
23✔
472
            nRawYSize);
23✔
473
    }
474
    else
475
    {  // Try to construct full tile in memory and write later
476
        const GUInt32 nTile = nBlockYOff * poGDS->nXTiles + nBlockXOff;
431✔
477

478
        // Find tile
479
        auto poTile(poGDS->oUnfinishedTiles.find(nTile));
431✔
480
        if (poTile == poGDS->oUnfinishedTiles.end())
431✔
481
        {
482
            RMFTileData oTile;
167✔
483
            oTile.oData.resize(nTileSize);
167✔
484
            // If not found, but exist on disk than read it
485
            if (poGDS->paiTiles[2 * nTile + 1])
167✔
486
            {
487
                CPLErr eRes;
488
                bool bNullTile = false;
×
489
                eRes =
490
                    poGDS->ReadTile(nBlockXOff, nBlockYOff, oTile.oData.data(),
×
491
                                    nTileSize, nRawXSize, nRawYSize, bNullTile);
492
                if (eRes != CE_None)
×
493
                {
494
                    CPLError(CE_Failure, CPLE_FileIO,
×
495
                             "Can't read block with offset [%d, %d]",
496
                             nBlockXOff, nBlockYOff);
497
                    return eRes;
×
498
                }
499
            }
500
            poTile = poGDS->oUnfinishedTiles.insert(
501
                poGDS->oUnfinishedTiles.end(), std::make_pair(nTile, oTile));
167✔
502
        }
503

504
        GByte *pabyTileData = poTile->second.oData.data();
431✔
505

506
        // Copy new data to a tile
507
        int iDstBand = (poGDS->nBands - nBand);
431✔
508
        for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
79,381✔
509
        {
510
            const GByte *pabySrc;
511
            GByte *pabyDst;
512
            pabySrc = reinterpret_cast<const GByte *>(pImage) +
78,950✔
513
                      iLine * nBlockLineSize;
78,950✔
514
            pabyDst =
78,950✔
515
                pabyTileData + iLine * nTileLineSize + iDstBand * nDataSize;
78,950✔
516
            GDALCopyWords(pabySrc, eDataType, static_cast<int>(nDataSize),
78,950✔
517
                          pabyDst, eDataType, static_cast<int>(nTilePixelSize),
518
                          nRawXSize);
519
        }
520
        ++poTile->second.nBandsWritten;
431✔
521

522
        // Write to disk if tile is finished
523
        if (poTile->second.nBandsWritten == poGDS->nBands)
431✔
524
        {
525
            poGDS->WriteTile(nBlockXOff, nBlockYOff, pabyTileData, nTileSize,
167✔
526
                             nRawXSize, nRawYSize);
527
            poGDS->oUnfinishedTiles.erase(poTile);
167✔
528
        }
529
#ifdef DEBUG
530
        CPLDebug("RMF", "poGDS->oUnfinishedTiles.size() %d",
431✔
531
                 static_cast<int>(poGDS->oUnfinishedTiles.size()));
431✔
532
#endif  // DEBUG
533
    }
534

535
    return CE_None;
431✔
536
}
537

538
/************************************************************************/
539
/*                          GetNoDataValue()                            */
540
/************************************************************************/
541

542
double RMFRasterBand::GetNoDataValue(int *pbSuccess)
191✔
543

544
{
545
    RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
191✔
546

547
    if (pbSuccess)
191✔
548
        *pbSuccess = TRUE;
164✔
549

550
    return poGDS->sHeader.dfNoData;
191✔
551
}
552

553
CPLErr RMFRasterBand::SetNoDataValue(double dfNoData)
27✔
554
{
555
    RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
27✔
556

557
    poGDS->sHeader.dfNoData = dfNoData;
27✔
558
    poGDS->bHeaderDirty = true;
27✔
559

560
    return CE_None;
27✔
561
}
562

563
/************************************************************************/
564
/*                            GetUnitType()                             */
565
/************************************************************************/
566

567
const char *RMFRasterBand::GetUnitType()
10✔
568

569
{
570
    RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
10✔
571

572
    return poGDS->pszUnitType;
10✔
573
}
574

575
/************************************************************************/
576
/*                            SetUnitType()                             */
577
/************************************************************************/
578

579
CPLErr RMFRasterBand::SetUnitType(const char *pszNewValue)
3✔
580

581
{
582
    RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
3✔
583
    int bSuccess = FALSE;
3✔
584
    int iNewUnit = RMFStrToUnitType(pszNewValue, &bSuccess);
3✔
585

586
    if (bSuccess)
3✔
587
    {
588
        CPLFree(poGDS->pszUnitType);
2✔
589
        poGDS->pszUnitType = CPLStrdup(pszNewValue);
2✔
590
        poGDS->sHeader.iElevationUnit = iNewUnit;
2✔
591
        poGDS->bHeaderDirty = true;
2✔
592
        return CE_None;
2✔
593
    }
594
    else
595
    {
596
        CPLError(CE_Warning, CPLE_NotSupported,
1✔
597
                 "RMF driver does not support '%s' elevation units. "
598
                 "Possible values are: m, dm, cm, mm.",
599
                 pszNewValue);
600
        return CE_Failure;
1✔
601
    }
602
}
603

604
/************************************************************************/
605
/*                           GetColorTable()                            */
606
/************************************************************************/
607

608
GDALColorTable *RMFRasterBand::GetColorTable()
28✔
609
{
610
    RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
28✔
611

612
    return poGDS->poColorTable;
28✔
613
}
614

615
/************************************************************************/
616
/*                           SetColorTable()                            */
617
/************************************************************************/
618

619
CPLErr RMFRasterBand::SetColorTable(GDALColorTable *poColorTable)
8✔
620
{
621
    RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
8✔
622

623
    if (poColorTable)
8✔
624
    {
625
        if (poGDS->eRMFType == RMFT_RSW && poGDS->nBands == 1)
8✔
626
        {
627
            if (!poGDS->pabyColorTable)
8✔
628
                return CE_Failure;
×
629

630
            GDALColorEntry oEntry;
631
            for (GUInt32 i = 0; i < poGDS->nColorTableSize; i++)
2,056✔
632
            {
633
                poColorTable->GetColorEntryAsRGB(i, &oEntry);
2,048✔
634
                // Red
635
                poGDS->pabyColorTable[i * 4 + 0] =
2,048✔
636
                    static_cast<GByte>(oEntry.c1);
2,048✔
637
                // Green
638
                poGDS->pabyColorTable[i * 4 + 1] =
2,048✔
639
                    static_cast<GByte>(oEntry.c2);
2,048✔
640
                // Blue
641
                poGDS->pabyColorTable[i * 4 + 2] =
2,048✔
642
                    static_cast<GByte>(oEntry.c3);
2,048✔
643
                poGDS->pabyColorTable[i * 4 + 3] = 0;
2,048✔
644
            }
645

646
            poGDS->bHeaderDirty = true;
8✔
647
        }
648
        return CE_None;
8✔
649
    }
650

651
    return CE_Failure;
×
652
}
653

654
int RMFRasterBand::GetOverviewCount()
59✔
655
{
656
    RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
59✔
657
    if (poGDS->poOvrDatasets.empty())
59✔
658
        return GDALRasterBand::GetOverviewCount();
×
659
    else
660
        return static_cast<int>(poGDS->poOvrDatasets.size());
59✔
661
}
662

663
GDALRasterBand *RMFRasterBand::GetOverview(int i)
79✔
664
{
665
    RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
79✔
666
    size_t n = static_cast<size_t>(i);
79✔
667
    if (poGDS->poOvrDatasets.empty())
79✔
668
        return GDALRasterBand::GetOverview(i);
×
669
    else
670
        return poGDS->poOvrDatasets[n]->GetRasterBand(nBand);
79✔
671
}
672

673
CPLErr RMFRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
694✔
674
                                int nXSize, int nYSize, void *pData,
675
                                int nBufXSize, int nBufYSize,
676
                                GDALDataType eType, GSpacing nPixelSpace,
677
                                GSpacing nLineSpace,
678
                                GDALRasterIOExtraArg *psExtraArg)
679
{
680
    RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
694✔
681

682
    if (eRWFlag == GF_Read && poGDS->poCompressData != nullptr &&
743✔
683
        poGDS->poCompressData->oThreadPool.GetThreadCount() > 0)
49✔
684
    {
685
        poGDS->poCompressData->oThreadPool.WaitCompletion();
9✔
686
    }
687

688
    return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
694✔
689
                                     pData, nBufXSize, nBufYSize, eType,
690
                                     nPixelSpace, nLineSpace, psExtraArg);
694✔
691
}
692

693
/************************************************************************/
694
/*                       GetColorInterpretation()                       */
695
/************************************************************************/
696

697
GDALColorInterp RMFRasterBand::GetColorInterpretation()
104✔
698
{
699
    RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
104✔
700

701
    if (poGDS->nBands == 3)
104✔
702
    {
703
        if (nBand == 1)
53✔
704
            return GCI_RedBand;
18✔
705
        else if (nBand == 2)
35✔
706
            return GCI_GreenBand;
17✔
707
        else if (nBand == 3)
18✔
708
            return GCI_BlueBand;
18✔
709

710
        return GCI_Undefined;
×
711
    }
712

713
    if (poGDS->eRMFType == RMFT_RSW)
51✔
714
        return GCI_PaletteIndex;
28✔
715

716
    return GCI_Undefined;
23✔
717
}
718

719
/************************************************************************/
720
/* ==================================================================== */
721
/*                              RMFDataset                              */
722
/* ==================================================================== */
723
/************************************************************************/
724

725
/************************************************************************/
726
/*                           RMFDataset()                               */
727
/************************************************************************/
728

729
RMFDataset::RMFDataset() : pszUnitType(CPLStrdup(RMF_UnitsEmpty))
297✔
730
{
731
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
297✔
732
    nBands = 0;
297✔
733
    memset(&sHeader, 0, sizeof(sHeader));
297✔
734
    memset(&sExtHeader, 0, sizeof(sExtHeader));
297✔
735
}
297✔
736

737
/************************************************************************/
738
/*                            ~RMFDataset()                             */
739
/************************************************************************/
740

741
RMFDataset::~RMFDataset()
594✔
742
{
743
    RMFDataset::FlushCache(true);
297✔
744
    for (size_t n = 0; n != poOvrDatasets.size(); ++n)
404✔
745
    {
746
        poOvrDatasets[n]->RMFDataset::FlushCache(true);
107✔
747
    }
748

749
    VSIFree(paiTiles);
297✔
750
    VSIFree(pabyDecompressBuffer);
297✔
751
    VSIFree(pabyCurrentTile);
297✔
752
    CPLFree(pszUnitType);
297✔
753
    CPLFree(pabyColorTable);
297✔
754
    if (poColorTable != nullptr)
297✔
755
        delete poColorTable;
68✔
756

757
    for (size_t n = 0; n != poOvrDatasets.size(); ++n)
404✔
758
    {
759
        GDALClose(poOvrDatasets[n]);
107✔
760
    }
761

762
    if (fp != nullptr && poParentDS == nullptr)
297✔
763
    {
764
        VSIFCloseL(fp);
177✔
765
    }
766
}
594✔
767

768
/************************************************************************/
769
/*                          GetGeoTransform()                           */
770
/************************************************************************/
771

772
CPLErr RMFDataset::GetGeoTransform(GDALGeoTransform &gt) const
47✔
773
{
774
    gt = m_gt;
47✔
775

776
    if (sHeader.iGeorefFlag)
47✔
777
        return CE_None;
41✔
778

779
    return CE_Failure;
6✔
780
}
781

782
/************************************************************************/
783
/*                          SetGeoTransform()                           */
784
/************************************************************************/
785

786
CPLErr RMFDataset::SetGeoTransform(const GDALGeoTransform &gt)
41✔
787
{
788
    m_gt = gt;
41✔
789
    sHeader.dfPixelSize = m_gt[1];
41✔
790
    if (sHeader.dfPixelSize != 0.0)
41✔
791
        sHeader.dfResolution = sHeader.dfScale / sHeader.dfPixelSize;
41✔
792
    sHeader.dfLLX = m_gt[0];
41✔
793
    sHeader.dfLLY = m_gt[3] - nRasterYSize * sHeader.dfPixelSize;
41✔
794
    sHeader.iGeorefFlag = 1;
41✔
795

796
    bHeaderDirty = true;
41✔
797

798
    return CE_None;
41✔
799
}
800

801
/************************************************************************/
802
/*                          GetSpatialRef()                             */
803
/************************************************************************/
804

805
const OGRSpatialReference *RMFDataset::GetSpatialRef() const
34✔
806

807
{
808
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
34✔
809
}
810

811
/************************************************************************/
812
/*                           SetSpatialRef()                            */
813
/************************************************************************/
814

815
CPLErr RMFDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
41✔
816

817
{
818
    m_oSRS.Clear();
41✔
819
    if (poSRS)
41✔
820
        m_oSRS = *poSRS;
41✔
821

822
    bHeaderDirty = true;
41✔
823

824
    return CE_None;
41✔
825
}
826

827
/************************************************************************/
828
/*                           WriteHeader()                              */
829
/************************************************************************/
830

831
CPLErr RMFDataset::WriteHeader()
200✔
832
{
833
    /* -------------------------------------------------------------------- */
834
    /*  Setup projection.                                                   */
835
    /* -------------------------------------------------------------------- */
836
    if (!m_oSRS.IsEmpty())
200✔
837
    {
838
        long iProjection = 0;
52✔
839
        long iDatum = 0;
52✔
840
        long iEllips = 0;
52✔
841
        long iZone = 0;
52✔
842
        int iVertCS = 0;
52✔
843
        double adfPrjParams[7] = {};
52✔
844

845
        m_oSRS.exportToPanorama(&iProjection, &iDatum, &iEllips, &iZone,
52✔
846
                                adfPrjParams);
847
        m_oSRS.exportVertCSToPanorama(&iVertCS);
52✔
848
        sHeader.iProjection = static_cast<GInt32>(iProjection);
52✔
849
        sHeader.dfStdP1 = adfPrjParams[0];
52✔
850
        sHeader.dfStdP2 = adfPrjParams[1];
52✔
851
        sHeader.dfCenterLat = adfPrjParams[2];
52✔
852
        sHeader.dfCenterLong = adfPrjParams[3];
52✔
853
        if (m_oSRS.GetAuthorityName(nullptr) != nullptr &&
52✔
854
            m_oSRS.GetAuthorityCode(nullptr) != nullptr &&
62✔
855
            EQUAL(m_oSRS.GetAuthorityName(nullptr), "EPSG"))
10✔
856
        {
857
            sHeader.iEPSGCode = atoi(m_oSRS.GetAuthorityCode(nullptr));
10✔
858
        }
859

860
        sExtHeader.nEllipsoid = static_cast<GInt32>(iEllips);
52✔
861
        sExtHeader.nDatum = static_cast<GInt32>(iDatum);
52✔
862
        sExtHeader.nZone = static_cast<GInt32>(iZone);
52✔
863
        sExtHeader.nVertDatum = static_cast<GInt32>(iVertCS);
52✔
864

865
        // Set map type
866
        auto pszMapType = GetMetadataItem(MD_MATH_BASE_MAP_TYPE_KEY);
52✔
867
        if (pszMapType != nullptr)
52✔
868
        {
869
            sHeader.iMapType = static_cast<GInt32>(atoi(pszMapType));
31✔
870
        }
871
    }
872

873
#define RMF_WRITE_LONG(ptr, value, offset)                                     \
874
    do                                                                         \
875
    {                                                                          \
876
        GInt32 iLong = CPL_LSBWORD32(value);                                   \
877
        memcpy((ptr) + (offset), &iLong, 4);                                   \
878
    } while (false);
879

880
#define RMF_WRITE_ULONG(ptr, value, offset)                                    \
881
    do                                                                         \
882
    {                                                                          \
883
        GUInt32 iULong = CPL_LSBWORD32(value);                                 \
884
        memcpy((ptr) + (offset), &iULong, 4);                                  \
885
    } while (false);
886

887
#define RMF_WRITE_DOUBLE(ptr, value, offset)                                   \
888
    do                                                                         \
889
    {                                                                          \
890
        double dfDouble = (value);                                             \
891
        CPL_LSBPTR64(&dfDouble);                                               \
892
        memcpy((ptr) + (offset), &dfDouble, 8);                                \
893
    } while (false);
894

895
    // Frame if present
896
    std::vector<RSWFrameCoord> astFrameCoords;
400✔
897
    auto pszFrameWKT = GetMetadataItem(MD_FRAME_KEY);
200✔
898
    if (pszFrameWKT != nullptr)
200✔
899
    {
900
        CPLDebug("RMF", "Write to header frame: %s", pszFrameWKT);
2✔
901
        OGRGeometry *poFrameGeom = nullptr;
2✔
902
        if (OGRGeometryFactory::createFromWkt(pszFrameWKT, nullptr,
2✔
903
                                              &poFrameGeom) == OGRERR_NONE)
2✔
904
        {
905
            if (poFrameGeom->getGeometryType() == wkbPolygon)
2✔
906
            {
907
                GDALGeoTransform reverseGT;
2✔
908
                if (m_gt.GetInverse(reverseGT))
2✔
909
                {
910
                    OGRPolygon *poFramePoly = poFrameGeom->toPolygon();
2✔
911
                    if (!poFramePoly->IsEmpty())
2✔
912
                    {
913
                        OGRLinearRing *poFrameRing =
914
                            poFramePoly->getExteriorRing();
2✔
915
                        for (int i = 0; i < poFrameRing->getNumPoints(); i++)
12✔
916
                        {
917
                            int nX =
918
                                int(reverseGT[0] +
10✔
919
                                    poFrameRing->getX(i) * reverseGT[1] - 0.5);
10✔
920
                            int nY =
921
                                int(reverseGT[3] +
10✔
922
                                    poFrameRing->getY(i) * reverseGT[5] - 0.5);
10✔
923

924
                            CPLDebug("RMF", "X: %d, Y: %d", nX, nY);
10✔
925

926
                            astFrameCoords.push_back({nX, nY});
10✔
927
                        }
928
                    }
929

930
                    if (astFrameCoords.empty() ||
4✔
931
                        astFrameCoords.size() > nMaxFramePointCount)
2✔
932
                    {
933
                        // CPLError(CE_Warning, CPLE_AppDefined, "Invalid frame WKT: %s", pszFrameWKT);
934
                        CPLDebug("RMF", "Write to header frame failed: no "
×
935
                                        "points or too many");
936
                        astFrameCoords.clear();
×
937
                    }
938
                    else
939
                    {
940
                        sHeader.nROISize = static_cast<GUInt32>(
2✔
941
                            sizeof(RSWFrame) +
2✔
942
                            sizeof(RSWFrameCoord) *
943
                                astFrameCoords
944
                                    .size());  // Set real size and real point count
2✔
945
                        sHeader.iFrameFlag = 0;
2✔
946
                    }
947
                }
948
                else
949
                {
950
                    CPLDebug("RMF", "Write to header frame failed: "
×
951
                                    "GDALInvGeoTransform == FALSE");
952
                }
953
            }
954
            OGRGeometryFactory::destroyGeometry(poFrameGeom);
2✔
955
        }
956
        else
957
        {
958
            CPLDebug("RMF", "Write to header frame failed: "
×
959
                            "OGRGeometryFactory::createFromWkt error");
960
        }
961
    }
962

963
    vsi_l_offset iCurrentFileSize(GetLastOffset());
200✔
964
    sHeader.nFileSize0 = GetRMFOffset(iCurrentFileSize, &iCurrentFileSize);
200✔
965
    sHeader.nSize = sHeader.nFileSize0 - GetRMFOffset(nHeaderOffset, nullptr);
200✔
966
    /* -------------------------------------------------------------------- */
967
    /*  Write out the main header.                                          */
968
    /* -------------------------------------------------------------------- */
969
    {
970
        GByte abyHeader[RMF_HEADER_SIZE] = {};
200✔
971

972
        memcpy(abyHeader, sHeader.bySignature, RMF_SIGNATURE_SIZE);
200✔
973
        RMF_WRITE_ULONG(abyHeader, sHeader.iVersion, 4);
200✔
974
        RMF_WRITE_ULONG(abyHeader, sHeader.nSize, 8);
200✔
975
        RMF_WRITE_ULONG(abyHeader, sHeader.nOvrOffset, 12);
200✔
976
        RMF_WRITE_ULONG(abyHeader, sHeader.iUserID, 16);
200✔
977
        memcpy(abyHeader + 20, sHeader.byName, RMF_NAME_SIZE);
200✔
978
        RMF_WRITE_ULONG(abyHeader, sHeader.nBitDepth, 52);
200✔
979
        RMF_WRITE_ULONG(abyHeader, sHeader.nHeight, 56);
200✔
980
        RMF_WRITE_ULONG(abyHeader, sHeader.nWidth, 60);
200✔
981
        RMF_WRITE_ULONG(abyHeader, sHeader.nXTiles, 64);
200✔
982
        RMF_WRITE_ULONG(abyHeader, sHeader.nYTiles, 68);
200✔
983
        RMF_WRITE_ULONG(abyHeader, sHeader.nTileHeight, 72);
200✔
984
        RMF_WRITE_ULONG(abyHeader, sHeader.nTileWidth, 76);
200✔
985
        RMF_WRITE_ULONG(abyHeader, sHeader.nLastTileHeight, 80);
200✔
986
        RMF_WRITE_ULONG(abyHeader, sHeader.nLastTileWidth, 84);
200✔
987
        RMF_WRITE_ULONG(abyHeader, sHeader.nROIOffset, 88);
200✔
988
        RMF_WRITE_ULONG(abyHeader, sHeader.nROISize, 92);
200✔
989
        RMF_WRITE_ULONG(abyHeader, sHeader.nClrTblOffset, 96);
200✔
990
        RMF_WRITE_ULONG(abyHeader, sHeader.nClrTblSize, 100);
200✔
991
        RMF_WRITE_ULONG(abyHeader, sHeader.nTileTblOffset, 104);
200✔
992
        RMF_WRITE_ULONG(abyHeader, sHeader.nTileTblSize, 108);
200✔
993
        RMF_WRITE_LONG(abyHeader, sHeader.iMapType, 124);
200✔
994
        RMF_WRITE_LONG(abyHeader, sHeader.iProjection, 128);
200✔
995
        RMF_WRITE_LONG(abyHeader, sHeader.iEPSGCode, 132);
200✔
996
        RMF_WRITE_DOUBLE(abyHeader, sHeader.dfScale, 136);
200✔
997
        RMF_WRITE_DOUBLE(abyHeader, sHeader.dfResolution, 144);
200✔
998
        RMF_WRITE_DOUBLE(abyHeader, sHeader.dfPixelSize, 152);
200✔
999
        RMF_WRITE_DOUBLE(abyHeader, sHeader.dfLLY, 160);
200✔
1000
        RMF_WRITE_DOUBLE(abyHeader, sHeader.dfLLX, 168);
200✔
1001
        RMF_WRITE_DOUBLE(abyHeader, sHeader.dfStdP1, 176);
200✔
1002
        RMF_WRITE_DOUBLE(abyHeader, sHeader.dfStdP2, 184);
200✔
1003
        RMF_WRITE_DOUBLE(abyHeader, sHeader.dfCenterLong, 192);
200✔
1004
        RMF_WRITE_DOUBLE(abyHeader, sHeader.dfCenterLat, 200);
200✔
1005
        *(abyHeader + 208) = sHeader.iCompression;
200✔
1006
        *(abyHeader + 209) = sHeader.iMaskType;
200✔
1007
        *(abyHeader + 210) = sHeader.iMaskStep;
200✔
1008
        *(abyHeader + 211) = sHeader.iFrameFlag;
200✔
1009
        RMF_WRITE_ULONG(abyHeader, sHeader.nFlagsTblOffset, 212);
200✔
1010
        RMF_WRITE_ULONG(abyHeader, sHeader.nFlagsTblSize, 216);
200✔
1011
        RMF_WRITE_ULONG(abyHeader, sHeader.nFileSize0, 220);
200✔
1012
        RMF_WRITE_ULONG(abyHeader, sHeader.nFileSize1, 224);
200✔
1013
        *(abyHeader + 228) = sHeader.iUnknown;
200✔
1014
        *(abyHeader + 244) = sHeader.iGeorefFlag;
200✔
1015
        *(abyHeader + 245) = sHeader.iInverse;
200✔
1016
        *(abyHeader + 246) = sHeader.iJpegQuality;
200✔
1017
        memcpy(abyHeader + 248, sHeader.abyInvisibleColors,
200✔
1018
               sizeof(sHeader.abyInvisibleColors));
1019
        RMF_WRITE_DOUBLE(abyHeader, sHeader.adfElevMinMax[0], 280);
200✔
1020
        RMF_WRITE_DOUBLE(abyHeader, sHeader.adfElevMinMax[1], 288);
200✔
1021
        RMF_WRITE_DOUBLE(abyHeader, sHeader.dfNoData, 296);
200✔
1022
        RMF_WRITE_ULONG(abyHeader, sHeader.iElevationUnit, 304);
200✔
1023
        *(abyHeader + 308) = sHeader.iElevationType;
200✔
1024
        RMF_WRITE_ULONG(abyHeader, sHeader.nExtHdrOffset, 312);
200✔
1025
        RMF_WRITE_ULONG(abyHeader, sHeader.nExtHdrSize, 316);
200✔
1026

1027
        VSIFSeekL(fp, nHeaderOffset, SEEK_SET);
200✔
1028
        VSIFWriteL(abyHeader, 1, sizeof(abyHeader), fp);
200✔
1029
    }
1030

1031
    /* -------------------------------------------------------------------- */
1032
    /*  Write out the extended header.                                      */
1033
    /* -------------------------------------------------------------------- */
1034

1035
    if (sHeader.nExtHdrOffset && sHeader.nExtHdrSize >= RMF_MIN_EXT_HEADER_SIZE)
200✔
1036
    {
1037
        if (sHeader.nExtHdrSize > RMF_MAX_EXT_HEADER_SIZE)
200✔
1038
        {
1039
            CPLError(CE_Failure, CPLE_FileIO, "RMF File malformed");
×
1040
            return CE_Failure;
×
1041
        }
1042
        GByte *pabyExtHeader =
1043
            reinterpret_cast<GByte *>(CPLCalloc(sHeader.nExtHdrSize, 1));
200✔
1044

1045
        RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nEllipsoid, 24);
200✔
1046
        RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nVertDatum, 28);
200✔
1047
        RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nDatum, 32);
200✔
1048
        RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nZone, 36);
200✔
1049

1050
        VSIFSeekL(fp, GetFileOffset(sHeader.nExtHdrOffset), SEEK_SET);
200✔
1051
        VSIFWriteL(pabyExtHeader, 1, sHeader.nExtHdrSize, fp);
200✔
1052

1053
        CPLFree(pabyExtHeader);
200✔
1054
    }
1055

1056
    /* -------------------------------------------------------------------- */
1057
    /*  Write out the color table.                                          */
1058
    /* -------------------------------------------------------------------- */
1059

1060
    if (sHeader.nClrTblOffset && sHeader.nClrTblSize)
200✔
1061
    {
1062
        VSIFSeekL(fp, GetFileOffset(sHeader.nClrTblOffset), SEEK_SET);
59✔
1063
        VSIFWriteL(pabyColorTable, 1, sHeader.nClrTblSize, fp);
59✔
1064
    }
1065

1066
    if (sHeader.nROIOffset && sHeader.nROISize)
200✔
1067
    {
1068
        GByte *pabyROI =
1069
            reinterpret_cast<GByte *>(CPLCalloc(sHeader.nROISize, 1));
2✔
1070
        memset(pabyROI, 0, sHeader.nROISize);
2✔
1071

1072
        auto nPointCount = astFrameCoords.size();
2✔
1073
        size_t offset = 0;
2✔
1074
        RMF_WRITE_LONG(pabyROI, nPolygonType, offset);
2✔
1075
        offset += 4;
2✔
1076
        RMF_WRITE_LONG(pabyROI, static_cast<GInt32>((4 + nPointCount * 2) * 4),
2✔
1077
                       offset);
1078
        offset += 4;
2✔
1079
        RMF_WRITE_LONG(pabyROI, 0, offset);
2✔
1080
        offset += 4;
2✔
1081
        RMF_WRITE_LONG(pabyROI, static_cast<GInt32>(32768 * nPointCount * 2),
2✔
1082
                       offset);
1083
        offset += 4;
2✔
1084

1085
        // Write points
1086
        for (size_t i = 0; i < nPointCount; i++)
12✔
1087
        {
1088
            RMF_WRITE_LONG(pabyROI, astFrameCoords[i].nX, offset);
10✔
1089
            offset += 4;
10✔
1090
            RMF_WRITE_LONG(pabyROI, astFrameCoords[i].nY, offset);
10✔
1091
            offset += 4;
10✔
1092
        }
1093

1094
        VSIFSeekL(fp, GetFileOffset(sHeader.nROIOffset), SEEK_SET);
2✔
1095
        VSIFWriteL(pabyROI, 1, sHeader.nROISize, fp);
2✔
1096

1097
        CPLFree(pabyROI);
2✔
1098
    }
1099

1100
    if (sHeader.nFlagsTblOffset && sHeader.nFlagsTblSize)
200✔
1101
    {
1102
        GByte *pabyFlagsTbl =
1103
            reinterpret_cast<GByte *>(CPLCalloc(sHeader.nFlagsTblSize, 1));
200✔
1104

1105
        if (sHeader.iFrameFlag == 0)
200✔
1106
        {
1107
            // TODO: Add more strictly check for flag value
1108
            memset(
2✔
1109
                pabyFlagsTbl, 2,
1110
                sHeader
1111
                    .nFlagsTblSize);  // Mark all blocks as intersected with ROI. 0 - complete outside, 1 - complete inside.
2✔
1112
        }
1113
        else
1114
        {
1115
            memset(pabyFlagsTbl, 0, sHeader.nFlagsTblSize);
198✔
1116
        }
1117

1118
        VSIFSeekL(fp, GetFileOffset(sHeader.nFlagsTblOffset), SEEK_SET);
200✔
1119
        VSIFWriteL(pabyFlagsTbl, 1, sHeader.nFlagsTblSize, fp);
200✔
1120

1121
        CPLFree(pabyFlagsTbl);
200✔
1122
    }
1123

1124
#undef RMF_WRITE_DOUBLE
1125
#undef RMF_WRITE_ULONG
1126
#undef RMF_WRITE_LONG
1127

1128
    /* -------------------------------------------------------------------- */
1129
    /*  Write out the block table, swap if needed.                          */
1130
    /* -------------------------------------------------------------------- */
1131

1132
    VSIFSeekL(fp, GetFileOffset(sHeader.nTileTblOffset), SEEK_SET);
200✔
1133

1134
#ifdef CPL_MSB
1135
    GUInt32 *paiTilesSwapped =
1136
        reinterpret_cast<GUInt32 *>(CPLMalloc(sHeader.nTileTblSize));
1137
    if (!paiTilesSwapped)
1138
        return CE_Failure;
1139

1140
    memcpy(paiTilesSwapped, paiTiles, sHeader.nTileTblSize);
1141
    for (GUInt32 i = 0; i < sHeader.nTileTblSize / sizeof(GUInt32); i++)
1142
        CPL_SWAP32PTR(paiTilesSwapped + i);
1143
    VSIFWriteL(paiTilesSwapped, 1, sHeader.nTileTblSize, fp);
1144

1145
    CPLFree(paiTilesSwapped);
1146
#else
1147
    VSIFWriteL(paiTiles, 1, sHeader.nTileTblSize, fp);
200✔
1148
#endif
1149

1150
    bHeaderDirty = false;
200✔
1151

1152
    return CE_None;
200✔
1153
}
1154

1155
/************************************************************************/
1156
/*                             FlushCache()                             */
1157
/************************************************************************/
1158

1159
CPLErr RMFDataset::FlushCache(bool bAtClosing)
411✔
1160

1161
{
1162
    CPLErr eErr = GDALDataset::FlushCache(bAtClosing);
411✔
1163

1164
    if (poCompressData != nullptr &&
549✔
1165
        poCompressData->oThreadPool.GetThreadCount() > 0)
138✔
1166
    {
1167
        poCompressData->oThreadPool.WaitCompletion();
9✔
1168
    }
1169

1170
    if (bAtClosing && eRMFType == RMFT_MTW && eAccess == GA_Update)
411✔
1171
    {
1172
        GDALRasterBand *poBand = GetRasterBand(1);
77✔
1173

1174
        if (poBand)
77✔
1175
        {
1176
            // ComputeRasterMinMax can setup error in case of dataset full
1177
            // from NoData values, but it  makes no sense here.
1178
            CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
77✔
1179
            poBand->ComputeRasterMinMax(FALSE, sHeader.adfElevMinMax);
77✔
1180
            bHeaderDirty = true;
77✔
1181
        }
1182
    }
1183
    if (bHeaderDirty && WriteHeader() != CE_None)
411✔
1184
        eErr = CE_Failure;
×
1185
    return eErr;
411✔
1186
}
1187

1188
/************************************************************************/
1189
/*                              Identify()                              */
1190
/************************************************************************/
1191

1192
int RMFDataset::Identify(GDALOpenInfo *poOpenInfo)
59,192✔
1193

1194
{
1195
    if (poOpenInfo->pabyHeader == nullptr)
59,192✔
1196
        return FALSE;
54,049✔
1197

1198
    if (memcmp(poOpenInfo->pabyHeader, RMF_SigRSW, sizeof(RMF_SigRSW)) != 0 &&
5,143✔
1199
        memcmp(poOpenInfo->pabyHeader, RMF_SigRSW_BE, sizeof(RMF_SigRSW_BE)) !=
4,934✔
1200
            0 &&
4,922✔
1201
        memcmp(poOpenInfo->pabyHeader, RMF_SigMTW, sizeof(RMF_SigMTW)) != 0)
4,922✔
1202
        return FALSE;
4,804✔
1203

1204
    return TRUE;
339✔
1205
}
1206

1207
/************************************************************************/
1208
/*                                Open()                                */
1209
/************************************************************************/
1210

1211
GDALDataset *RMFDataset::Open(GDALOpenInfo *poOpenInfo)
128✔
1212
{
1213
    auto poDS = Open(poOpenInfo, nullptr, 0);
128✔
1214
    if (poDS == nullptr)
128✔
1215
    {
1216
        return nullptr;
9✔
1217
    }
1218

1219
    RMFDataset *poCurrentLayer = poDS;
119✔
1220
    RMFDataset *poParent = poCurrentLayer;
119✔
1221
    const int nMaxPossibleOvCount = 64;
119✔
1222

1223
    for (int iOv = 0; iOv < nMaxPossibleOvCount && poCurrentLayer != nullptr;
200✔
1224
         ++iOv)
1225
    {
1226
        poCurrentLayer = poCurrentLayer->OpenOverview(poParent, poOpenInfo);
200✔
1227
        if (poCurrentLayer == nullptr)
200✔
1228
            break;
119✔
1229
        poParent->poOvrDatasets.push_back(poCurrentLayer);
81✔
1230
    }
1231

1232
    return poDS;
119✔
1233
}
1234

1235
RMFDataset *RMFDataset::Open(GDALOpenInfo *poOpenInfo, RMFDataset *poParentDS,
213✔
1236
                             vsi_l_offset nNextHeaderOffset)
1237
{
1238
    if (!Identify(poOpenInfo) ||
341✔
1239
        (poParentDS == nullptr && poOpenInfo->fpL == nullptr))
128✔
1240
        return nullptr;
2✔
1241

1242
    /* -------------------------------------------------------------------- */
1243
    /*  Create a corresponding GDALDataset.                                 */
1244
    /* -------------------------------------------------------------------- */
1245
    RMFDataset *poDS = new RMFDataset();
211✔
1246

1247
    if (poParentDS == nullptr)
211✔
1248
    {
1249
        poDS->fp = poOpenInfo->fpL;
128✔
1250
        poOpenInfo->fpL = nullptr;
128✔
1251
        poDS->nHeaderOffset = 0;
128✔
1252
        poDS->poParentDS = nullptr;
128✔
1253
    }
1254
    else
1255
    {
1256
        poDS->fp = poParentDS->fp;
83✔
1257
        poDS->poParentDS = poParentDS;
83✔
1258
        poDS->nHeaderOffset = nNextHeaderOffset;
83✔
1259
    }
1260
    poDS->eAccess = poOpenInfo->eAccess;
211✔
1261

1262
#define RMF_READ_SHORT(ptr, value, offset)                                     \
1263
    do                                                                         \
1264
    {                                                                          \
1265
        memcpy(&(value), reinterpret_cast<GInt16 *>((ptr) + (offset)),         \
1266
               sizeof(GInt16));                                                \
1267
        if (poDS->bBigEndian)                                                  \
1268
        {                                                                      \
1269
            CPL_MSBPTR16(&(value));                                            \
1270
        }                                                                      \
1271
        else                                                                   \
1272
        {                                                                      \
1273
            CPL_LSBPTR16(&(value));                                            \
1274
        }                                                                      \
1275
    } while (false);
1276

1277
#define RMF_READ_ULONG(ptr, value, offset)                                     \
1278
    do                                                                         \
1279
    {                                                                          \
1280
        memcpy(&(value), reinterpret_cast<GUInt32 *>((ptr) + (offset)),        \
1281
               sizeof(GUInt32));                                               \
1282
        if (poDS->bBigEndian)                                                  \
1283
        {                                                                      \
1284
            CPL_MSBPTR32(&(value));                                            \
1285
        }                                                                      \
1286
        else                                                                   \
1287
        {                                                                      \
1288
            CPL_LSBPTR32(&(value));                                            \
1289
        }                                                                      \
1290
    } while (false);
1291

1292
#define RMF_READ_LONG(ptr, value, offset) RMF_READ_ULONG(ptr, value, offset)
1293

1294
#define RMF_READ_DOUBLE(ptr, value, offset)                                    \
1295
    do                                                                         \
1296
    {                                                                          \
1297
        memcpy(&(value), reinterpret_cast<double *>((ptr) + (offset)),         \
1298
               sizeof(double));                                                \
1299
        if (poDS->bBigEndian)                                                  \
1300
        {                                                                      \
1301
            CPL_MSBPTR64(&(value));                                            \
1302
        }                                                                      \
1303
        else                                                                   \
1304
        {                                                                      \
1305
            CPL_LSBPTR64(&(value));                                            \
1306
        }                                                                      \
1307
    } while (false);
1308

1309
    /* -------------------------------------------------------------------- */
1310
    /*  Read the main header.                                               */
1311
    /* -------------------------------------------------------------------- */
1312

1313
    {
1314
        GByte abyHeader[RMF_HEADER_SIZE] = {};
211✔
1315

1316
        VSIFSeekL(poDS->fp, nNextHeaderOffset, SEEK_SET);
211✔
1317
        if (VSIFReadL(abyHeader, 1, sizeof(abyHeader), poDS->fp) !=
211✔
1318
            sizeof(abyHeader))
1319
        {
1320
            delete poDS;
×
1321
            return nullptr;
×
1322
        }
1323

1324
        if (memcmp(abyHeader, RMF_SigMTW, sizeof(RMF_SigMTW)) == 0)
211✔
1325
        {
1326
            poDS->eRMFType = RMFT_MTW;
76✔
1327
        }
1328
        else if (memcmp(abyHeader, RMF_SigRSW_BE, sizeof(RMF_SigRSW_BE)) == 0)
135✔
1329
        {
1330
            poDS->eRMFType = RMFT_RSW;
6✔
1331
            poDS->bBigEndian = true;
6✔
1332
        }
1333
        else
1334
        {
1335
            poDS->eRMFType = RMFT_RSW;
129✔
1336
        }
1337

1338
        memcpy(poDS->sHeader.bySignature, abyHeader, RMF_SIGNATURE_SIZE);
211✔
1339
        RMF_READ_ULONG(abyHeader, poDS->sHeader.iVersion, 4);
211✔
1340
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nSize, 8);
211✔
1341
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nOvrOffset, 12);
211✔
1342
        RMF_READ_ULONG(abyHeader, poDS->sHeader.iUserID, 16);
211✔
1343
        memcpy(poDS->sHeader.byName, abyHeader + 20,
211✔
1344
               sizeof(poDS->sHeader.byName));
1345
        poDS->sHeader.byName[sizeof(poDS->sHeader.byName) - 1] = '\0';
211✔
1346
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nBitDepth, 52);
211✔
1347
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nHeight, 56);
211✔
1348
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nWidth, 60);
211✔
1349
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nXTiles, 64);
211✔
1350
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nYTiles, 68);
211✔
1351
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileHeight, 72);
211✔
1352
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileWidth, 76);
211✔
1353
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nLastTileHeight, 80);
211✔
1354
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nLastTileWidth, 84);
211✔
1355
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nROIOffset, 88);
211✔
1356
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nROISize, 92);
211✔
1357
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nClrTblOffset, 96);
211✔
1358
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nClrTblSize, 100);
211✔
1359
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileTblOffset, 104);
211✔
1360
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileTblSize, 108);
211✔
1361
        RMF_READ_LONG(abyHeader, poDS->sHeader.iMapType, 124);
211✔
1362
        RMF_READ_LONG(abyHeader, poDS->sHeader.iProjection, 128);
211✔
1363
        RMF_READ_LONG(abyHeader, poDS->sHeader.iEPSGCode, 132);
211✔
1364
        RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfScale, 136);
211✔
1365
        RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfResolution, 144);
211✔
1366
        RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfPixelSize, 152);
211✔
1367
        RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfLLY, 160);
211✔
1368
        RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfLLX, 168);
211✔
1369
        RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfStdP1, 176);
211✔
1370
        RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfStdP2, 184);
211✔
1371
        RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfCenterLong, 192);
211✔
1372
        RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfCenterLat, 200);
211✔
1373
        poDS->sHeader.iCompression = *(abyHeader + 208);
211✔
1374
        poDS->sHeader.iMaskType = *(abyHeader + 209);
211✔
1375
        poDS->sHeader.iMaskStep = *(abyHeader + 210);
211✔
1376
        poDS->sHeader.iFrameFlag = *(abyHeader + 211);
211✔
1377
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nFlagsTblOffset, 212);
211✔
1378
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nFlagsTblSize, 216);
211✔
1379
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nFileSize0, 220);
211✔
1380
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nFileSize1, 224);
211✔
1381
        poDS->sHeader.iUnknown = *(abyHeader + 228);
211✔
1382
        poDS->sHeader.iGeorefFlag = *(abyHeader + 244);
211✔
1383
        poDS->sHeader.iInverse = *(abyHeader + 245);
211✔
1384
        poDS->sHeader.iJpegQuality = *(abyHeader + 246);
211✔
1385
        memcpy(poDS->sHeader.abyInvisibleColors, abyHeader + 248,
211✔
1386
               sizeof(poDS->sHeader.abyInvisibleColors));
1387
        RMF_READ_DOUBLE(abyHeader, poDS->sHeader.adfElevMinMax[0], 280);
211✔
1388
        RMF_READ_DOUBLE(abyHeader, poDS->sHeader.adfElevMinMax[1], 288);
211✔
1389
        RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfNoData, 296);
211✔
1390

1391
        RMF_READ_ULONG(abyHeader, poDS->sHeader.iElevationUnit, 304);
211✔
1392
        poDS->sHeader.iElevationType = *(abyHeader + 308);
211✔
1393
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nExtHdrOffset, 312);
211✔
1394
        RMF_READ_ULONG(abyHeader, poDS->sHeader.nExtHdrSize, 316);
211✔
1395
        poDS->SetMetadataItem(MD_SCALE_KEY,
211✔
1396
                              CPLSPrintf("1 : %u", int(poDS->sHeader.dfScale)));
211✔
1397
        poDS->SetMetadataItem(MD_NAME_KEY,
211✔
1398
                              CPLSPrintf("%s", poDS->sHeader.byName));
211✔
1399
        poDS->SetMetadataItem(MD_VERSION_KEY,
211✔
1400
                              CPLSPrintf("%d", poDS->sHeader.iVersion));
1401
        poDS->SetMetadataItem(MD_MATH_BASE_MAP_TYPE_KEY,
211✔
1402
                              CPLSPrintf("%d", poDS->sHeader.iMapType));
1403
        poDS->SetMetadataItem(MD_MATH_BASE_PROJECTION_KEY,
211✔
1404
                              CPLSPrintf("%d", poDS->sHeader.iProjection));
1405
    }
1406

1407
    if (poDS->sHeader.nTileTblSize % (sizeof(GUInt32) * 2))
211✔
1408
    {
1409
        CPLError(CE_Warning, CPLE_IllegalArg, "Invalid tile table size.");
×
1410
        delete poDS;
×
1411
        return nullptr;
×
1412
    }
1413

1414
    bool bInvalidTileSize;
1415
    try
1416
    {
1417
        uint64_t nMaxTileBits =
1418
            (CPLSM(static_cast<uint64_t>(2)) *
211✔
1419
             CPLSM(static_cast<uint64_t>(poDS->sHeader.nTileWidth)) *
422✔
1420
             CPLSM(static_cast<uint64_t>(poDS->sHeader.nTileHeight)) *
422✔
1421
             CPLSM(static_cast<uint64_t>(poDS->sHeader.nBitDepth)))
422✔
1422
                .v();
211✔
1423
        bInvalidTileSize =
211✔
1424
            (nMaxTileBits >
1425
             static_cast<uint64_t>(std::numeric_limits<GUInt32>::max()));
211✔
1426
    }
1427
    catch (...)
×
1428
    {
1429
        bInvalidTileSize = true;
×
1430
    }
1431
    if (bInvalidTileSize)
211✔
1432
    {
1433
        CPLError(CE_Warning, CPLE_IllegalArg,
×
1434
                 "Invalid tile size. Width %lu, height %lu, bit depth %lu.",
1435
                 static_cast<unsigned long>(poDS->sHeader.nTileWidth),
×
1436
                 static_cast<unsigned long>(poDS->sHeader.nTileHeight),
×
1437
                 static_cast<unsigned long>(poDS->sHeader.nBitDepth));
×
1438
        delete poDS;
×
1439
        return nullptr;
×
1440
    }
1441

1442
    if (poDS->sHeader.nLastTileWidth > poDS->sHeader.nTileWidth ||
211✔
1443
        poDS->sHeader.nLastTileHeight > poDS->sHeader.nTileHeight)
211✔
1444
    {
1445
        CPLError(CE_Warning, CPLE_IllegalArg,
×
1446
                 "Invalid last tile size %lu x %lu. "
1447
                 "It can't be greater than %lu x %lu.",
1448
                 static_cast<unsigned long>(poDS->sHeader.nLastTileWidth),
×
1449
                 static_cast<unsigned long>(poDS->sHeader.nLastTileHeight),
×
1450
                 static_cast<unsigned long>(poDS->sHeader.nTileWidth),
×
1451
                 static_cast<unsigned long>(poDS->sHeader.nTileHeight));
×
1452
        delete poDS;
×
1453
        return nullptr;
×
1454
    }
1455

1456
    if (poParentDS != nullptr)
211✔
1457
    {
1458
        if (0 != memcmp(poDS->sHeader.bySignature,
83✔
1459
                        poParentDS->sHeader.bySignature, RMF_SIGNATURE_SIZE))
83✔
1460
        {
1461
            CPLError(CE_Warning, CPLE_IllegalArg,
2✔
1462
                     "Invalid subheader signature.");
1463
            delete poDS;
2✔
1464
            return nullptr;
2✔
1465
        }
1466
    }
1467

1468
    /* -------------------------------------------------------------------- */
1469
    /*  Read the extended header.                                           */
1470
    /* -------------------------------------------------------------------- */
1471

1472
    if (poDS->sHeader.nExtHdrOffset &&
209✔
1473
        poDS->sHeader.nExtHdrSize >= RMF_MIN_EXT_HEADER_SIZE)
198✔
1474
    {
1475
        if (poDS->sHeader.nExtHdrSize > RMF_MAX_EXT_HEADER_SIZE)
198✔
1476
        {
1477
            CPLError(CE_Failure, CPLE_FileIO, "RMF File malformed");
×
1478
            delete poDS;
×
1479
            return nullptr;
×
1480
        }
1481
        GByte *pabyExtHeader =
1482
            reinterpret_cast<GByte *>(CPLCalloc(poDS->sHeader.nExtHdrSize, 1));
198✔
1483
        if (pabyExtHeader == nullptr)
198✔
1484
        {
1485
            delete poDS;
×
1486
            return nullptr;
×
1487
        }
1488

1489
        VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nExtHdrOffset),
198✔
1490
                  SEEK_SET);
1491
        VSIFReadL(pabyExtHeader, 1, poDS->sHeader.nExtHdrSize, poDS->fp);
198✔
1492

1493
        RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nEllipsoid, 24);
198✔
1494
        RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nVertDatum, 28);
198✔
1495
        RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nDatum, 32);
198✔
1496
        RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nZone, 36);
198✔
1497

1498
        CPLFree(pabyExtHeader);
198✔
1499
    }
1500

1501
    CPLDebug("RMF", "Version %d", poDS->sHeader.iVersion);
209✔
1502

1503
    constexpr GUInt32 ROI_MAX_SIZE_TO_AVOID_EXCESSIVE_RAM_USAGE =
209✔
1504
        10 * 1024 * 1024;
1505
#ifdef DEBUG
1506

1507
    CPLDebug("RMF",
418✔
1508
             "%s image has width %d, height %d, bit depth %d, "
1509
             "compression scheme %d, %s, nodata %f",
1510
             (poDS->eRMFType == RMFT_MTW) ? "MTW" : "RSW", poDS->sHeader.nWidth,
209✔
1511
             poDS->sHeader.nHeight, poDS->sHeader.nBitDepth,
1512
             poDS->sHeader.iCompression,
209✔
1513
             poDS->bBigEndian ? "big endian" : "little endian",
209✔
1514
             poDS->sHeader.dfNoData);
1515
    CPLDebug("RMF",
209✔
1516
             "Size %d, offset to overview %#lx, user ID %d, "
1517
             "ROI offset %#lx, ROI size %d",
1518
             poDS->sHeader.nSize,
1519
             static_cast<unsigned long>(poDS->sHeader.nOvrOffset),
209✔
1520
             poDS->sHeader.iUserID,
1521
             static_cast<unsigned long>(poDS->sHeader.nROIOffset),
209✔
1522
             poDS->sHeader.nROISize);
1523
    CPLDebug("RMF", "Map type %d, projection %d, scale %f, resolution %f, ",
209✔
1524
             poDS->sHeader.iMapType, poDS->sHeader.iProjection,
1525
             poDS->sHeader.dfScale, poDS->sHeader.dfResolution);
1526
    CPLDebug("RMF", "EPSG %d ", poDS->sHeader.iEPSGCode);
209✔
1527
    CPLDebug("RMF", "Georeferencing: pixel size %f, LLX %f, LLY %f",
209✔
1528
             poDS->sHeader.dfPixelSize, poDS->sHeader.dfLLX,
1529
             poDS->sHeader.dfLLY);
1530

1531
    if (poDS->sHeader.nROIOffset &&
209✔
1532
        poDS->sHeader.nROISize >= sizeof(RSWFrame) &&
116✔
1533
        poDS->sHeader.nROISize <= ROI_MAX_SIZE_TO_AVOID_EXCESSIVE_RAM_USAGE)
12✔
1534
    {
1535
        GByte *pabyROI = reinterpret_cast<GByte *>(
1536
            VSI_MALLOC_VERBOSE(poDS->sHeader.nROISize));
12✔
1537
        if (pabyROI == nullptr)
12✔
1538
        {
1539
            delete poDS;
×
1540
            return nullptr;
×
1541
        }
1542

1543
        VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nROIOffset),
12✔
1544
                  SEEK_SET);
1545
        if (VSIFReadL(pabyROI, poDS->sHeader.nROISize, 1, poDS->fp) != 1)
12✔
1546
        {
1547
            CPLError(CE_Failure, CPLE_FileIO, "Cannot read ROI");
×
1548
            CPLFree(pabyROI);
×
1549
            delete poDS;
×
1550
            return nullptr;
×
1551
        }
1552

1553
        GInt32 nValue;
1554

1555
        CPLDebug("RMF", "ROI coordinates:");
12✔
1556
        /* coverity[tainted_data] */
1557
        for (GUInt32 i = 0; i + sizeof(nValue) <= poDS->sHeader.nROISize;
3,504✔
1558
             i += sizeof(nValue))
3,492✔
1559
        {
1560
            RMF_READ_LONG(pabyROI, nValue, i);
3,492✔
1561
            CPLDebug("RMF", "%d", nValue);
3,492✔
1562
        }
1563

1564
        CPLFree(pabyROI);
12✔
1565
    }
1566
#endif
1567
    if (poDS->sHeader.nWidth >= INT_MAX || poDS->sHeader.nHeight >= INT_MAX ||
418✔
1568
        !GDALCheckDatasetDimensions(poDS->sHeader.nWidth,
209✔
1569
                                    poDS->sHeader.nHeight))
209✔
1570
    {
1571
        delete poDS;
×
1572
        return nullptr;
×
1573
    }
1574

1575
    /* -------------------------------------------------------------------- */
1576
    /*  Read array of blocks offsets/sizes.                                 */
1577
    /* -------------------------------------------------------------------- */
1578

1579
    // To avoid useless excessive memory allocation
1580
    if (poDS->sHeader.nTileTblSize > 1000000)
209✔
1581
    {
1582
        VSIFSeekL(poDS->fp, 0, SEEK_END);
×
1583
        vsi_l_offset nFileSize = VSIFTellL(poDS->fp);
×
1584
        if (nFileSize < poDS->sHeader.nTileTblSize)
×
1585
        {
1586
            delete poDS;
×
1587
            return nullptr;
×
1588
        }
1589
    }
1590

1591
    if (VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nTileTblOffset),
209✔
1592
                  SEEK_SET) < 0)
209✔
1593
    {
1594
        delete poDS;
×
1595
        return nullptr;
×
1596
    }
1597

1598
    poDS->paiTiles =
209✔
1599
        reinterpret_cast<GUInt32 *>(VSIMalloc(poDS->sHeader.nTileTblSize));
209✔
1600
    if (!poDS->paiTiles)
209✔
1601
    {
1602
        delete poDS;
×
1603
        return nullptr;
×
1604
    }
1605

1606
    if (VSIFReadL(poDS->paiTiles, 1, poDS->sHeader.nTileTblSize, poDS->fp) <
209✔
1607
        poDS->sHeader.nTileTblSize)
209✔
1608
    {
1609
        CPLDebug("RMF", "Can't read tiles offsets/sizes table.");
9✔
1610
        delete poDS;
9✔
1611
        return nullptr;
9✔
1612
    }
1613

1614
#ifdef CPL_MSB
1615
    if (!poDS->bBigEndian)
1616
    {
1617
        for (GUInt32 i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32);
1618
             i++)
1619
            CPL_SWAP32PTR(poDS->paiTiles + i);
1620
    }
1621
#else
1622
    if (poDS->bBigEndian)
200✔
1623
    {
1624
        for (GUInt32 i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32);
30✔
1625
             i++)
1626
            CPL_SWAP32PTR(poDS->paiTiles + i);
24✔
1627
    }
1628
#endif
1629

1630
#ifdef DEBUG
1631
    CPLDebug("RMF", "List of block offsets/sizes:");
200✔
1632

1633
    for (GUInt32 i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32);
646✔
1634
         i += 2)
446✔
1635
    {
1636
        CPLDebug("RMF", "    %u / %u", poDS->paiTiles[i],
446✔
1637
                 poDS->paiTiles[i + 1]);
446✔
1638
    }
1639
#endif
1640

1641
    /* -------------------------------------------------------------------- */
1642
    /*  Set up essential image parameters.                                  */
1643
    /* -------------------------------------------------------------------- */
1644
    GDALDataType eType = GDT_Byte;
200✔
1645

1646
    poDS->nRasterXSize = poDS->sHeader.nWidth;
200✔
1647
    poDS->nRasterYSize = poDS->sHeader.nHeight;
200✔
1648

1649
    if (poDS->eRMFType == RMFT_RSW)
200✔
1650
    {
1651
        switch (poDS->sHeader.nBitDepth)
126✔
1652
        {
1653
            case 32:
58✔
1654
            case 24:
1655
            case 16:
1656
                poDS->nBands = 3;
58✔
1657
                break;
58✔
1658
            case 1:
68✔
1659
            case 4:
1660
            case 8:
1661
                if (poParentDS != nullptr &&
68✔
1662
                    poParentDS->poColorTable != nullptr)
26✔
1663
                {
1664
                    poDS->poColorTable = poParentDS->poColorTable->Clone();
26✔
1665
                }
1666
                else
1667
                {
1668
                    // Allocate memory for colour table and read it
1669
                    poDS->nColorTableSize = 1 << poDS->sHeader.nBitDepth;
42✔
1670
                    GUInt32 nExpectedColorTableBytes =
42✔
1671
                        poDS->nColorTableSize * 4;
42✔
1672
                    if (nExpectedColorTableBytes > poDS->sHeader.nClrTblSize)
42✔
1673
                    {
1674
                        // We could probably test for strict equality in
1675
                        // the above test ???
1676
                        CPLDebug("RMF",
×
1677
                                 "Wrong color table size. "
1678
                                 "Expected %u, got %u.",
1679
                                 nExpectedColorTableBytes,
1680
                                 poDS->sHeader.nClrTblSize);
1681
                        delete poDS;
×
1682
                        return nullptr;
×
1683
                    }
1684
                    poDS->pabyColorTable = reinterpret_cast<GByte *>(
42✔
1685
                        VSIMalloc(nExpectedColorTableBytes));
42✔
1686
                    if (poDS->pabyColorTable == nullptr)
42✔
1687
                    {
1688
                        CPLDebug("RMF", "Can't allocate color table.");
×
1689
                        delete poDS;
×
1690
                        return nullptr;
×
1691
                    }
1692
                    if (VSIFSeekL(
42✔
1693
                            poDS->fp,
1694
                            poDS->GetFileOffset(poDS->sHeader.nClrTblOffset),
1695
                            SEEK_SET) < 0)
42✔
1696
                    {
1697
                        CPLDebug("RMF", "Can't seek to color table location.");
×
1698
                        delete poDS;
×
1699
                        return nullptr;
×
1700
                    }
1701
                    if (VSIFReadL(poDS->pabyColorTable, 1,
42✔
1702
                                  nExpectedColorTableBytes,
1703
                                  poDS->fp) < nExpectedColorTableBytes)
42✔
1704
                    {
1705
                        CPLDebug("RMF", "Can't read color table.");
×
1706
                        delete poDS;
×
1707
                        return nullptr;
×
1708
                    }
1709

1710
                    poDS->poColorTable = new GDALColorTable();
42✔
1711
                    for (GUInt32 i = 0; i < poDS->nColorTableSize; i++)
9,326✔
1712
                    {
1713
                        const GDALColorEntry oEntry = {
9,284✔
1714
                            poDS->pabyColorTable[i * 4],      // Red
9,284✔
1715
                            poDS->pabyColorTable[i * 4 + 1],  // Green
9,284✔
1716
                            poDS->pabyColorTable[i * 4 + 2],  // Blue
9,284✔
1717
                            255                               // Alpha
1718
                        };
9,284✔
1719

1720
                        poDS->poColorTable->SetColorEntry(i, &oEntry);
9,284✔
1721
                    }
1722
                }
1723
                poDS->nBands = 1;
68✔
1724
                break;
68✔
1725
            default:
×
1726
                CPLError(CE_Warning, CPLE_IllegalArg,
×
1727
                         "Invalid RSW bit depth %lu.",
1728
                         static_cast<unsigned long>(poDS->sHeader.nBitDepth));
×
1729
                delete poDS;
×
1730
                return nullptr;
×
1731
        }
1732
        eType = GDT_Byte;
126✔
1733
    }
1734
    else
1735
    {
1736
        poDS->nBands = 1;
74✔
1737
        if (poDS->sHeader.nBitDepth == 8)
74✔
1738
        {
1739
            eType = GDT_Byte;
×
1740
        }
1741
        else if (poDS->sHeader.nBitDepth == 16)
74✔
1742
        {
1743
            eType = GDT_Int16;
×
1744
        }
1745
        else if (poDS->sHeader.nBitDepth == 32)
74✔
1746
        {
1747
            eType = GDT_Int32;
9✔
1748
        }
1749
        else if (poDS->sHeader.nBitDepth == 64)
65✔
1750
        {
1751
            eType = GDT_Float64;
65✔
1752
        }
1753
        else
1754
        {
1755
            CPLError(CE_Warning, CPLE_IllegalArg, "Invalid MTW bit depth %lu.",
×
1756
                     static_cast<unsigned long>(poDS->sHeader.nBitDepth));
×
1757
            delete poDS;
×
1758
            return nullptr;
×
1759
        }
1760
    }
1761

1762
    if (poDS->sHeader.nTileWidth == 0 || poDS->sHeader.nTileWidth > INT_MAX ||
200✔
1763
        poDS->sHeader.nTileHeight == 0 || poDS->sHeader.nTileHeight > INT_MAX)
200✔
1764
    {
1765
        CPLDebug("RMF", "Invalid tile dimension : %u x %u",
×
1766
                 poDS->sHeader.nTileWidth, poDS->sHeader.nTileHeight);
1767
        delete poDS;
×
1768
        return nullptr;
×
1769
    }
1770

1771
    const int nDataSize = GDALGetDataTypeSizeBytes(eType);
200✔
1772
    const int nBlockXSize = static_cast<int>(poDS->sHeader.nTileWidth);
200✔
1773
    const int nBlockYSize = static_cast<int>(poDS->sHeader.nTileHeight);
200✔
1774
    if (nDataSize == 0 || nBlockXSize > INT_MAX / nBlockYSize ||
200✔
1775
        nBlockYSize > INT_MAX / nDataSize ||
200✔
1776
        nBlockXSize > INT_MAX / (nBlockYSize * nDataSize))
200✔
1777
    {
1778
        CPLDebug("RMF", "Too big raster / tile dimension");
×
1779
        delete poDS;
×
1780
        return nullptr;
×
1781
    }
1782

1783
    poDS->nXTiles = DIV_ROUND_UP(poDS->nRasterXSize, nBlockXSize);
200✔
1784
    poDS->nYTiles = DIV_ROUND_UP(poDS->nRasterYSize, nBlockYSize);
200✔
1785

1786
#ifdef DEBUG
1787
    CPLDebug("RMF", "Image is %d tiles wide, %d tiles long", poDS->nXTiles,
200✔
1788
             poDS->nYTiles);
1789
#endif
1790

1791
    /* -------------------------------------------------------------------- */
1792
    /*  Choose compression scheme.                                          */
1793
    /* -------------------------------------------------------------------- */
1794
    if (CE_None != poDS->SetupCompression(eType, poOpenInfo->pszFilename))
200✔
1795
    {
1796
        delete poDS;
×
1797
        return nullptr;
×
1798
    }
1799

1800
    if (poOpenInfo->eAccess == GA_Update)
200✔
1801
    {
1802
        if (poParentDS == nullptr)
20✔
1803
        {
1804
            if (CE_None !=
12✔
1805
                poDS->InitCompressorData(poOpenInfo->papszOpenOptions))
12✔
1806
            {
1807
                delete poDS;
×
1808
                return nullptr;
×
1809
            }
1810
        }
1811
        else
1812
        {
1813
            poDS->poCompressData = poParentDS->poCompressData;
8✔
1814
        }
1815
    }
1816
    /* -------------------------------------------------------------------- */
1817
    /*  Create band information objects.                                    */
1818
    /* -------------------------------------------------------------------- */
1819
    for (int iBand = 1; iBand <= poDS->nBands; iBand++)
516✔
1820
        poDS->SetBand(iBand, new RMFRasterBand(poDS, iBand, eType));
316✔
1821

1822
    poDS->SetupNBits();
200✔
1823

1824
    if (poDS->nBands > 1)
200✔
1825
    {
1826
        poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
58✔
1827
    }
1828
    /* -------------------------------------------------------------------- */
1829
    /*  Set up projection.                                                  */
1830
    /*                                                                      */
1831
    /*  XXX: If projection value is not specified, but image still have     */
1832
    /*  georeferencing information, assume Gauss-Kruger projection.         */
1833
    /* -------------------------------------------------------------------- */
1834
    if (poDS->sHeader.iEPSGCode > RMF_EPSG_MIN_CODE ||
200✔
1835
        poDS->sHeader.iProjection > 0 ||
186✔
1836
        (poDS->sHeader.dfPixelSize != 0.0 && poDS->sHeader.dfLLX != 0.0 &&
117✔
1837
         poDS->sHeader.dfLLY != 0.0))
15✔
1838
    {
1839
        GInt32 nProj =
98✔
1840
            (poDS->sHeader.iProjection) ? poDS->sHeader.iProjection : 1;
98✔
1841
        double padfPrjParams[8] = {poDS->sHeader.dfStdP1,
98✔
1842
                                   poDS->sHeader.dfStdP2,
98✔
1843
                                   poDS->sHeader.dfCenterLat,
98✔
1844
                                   poDS->sHeader.dfCenterLong,
98✔
1845
                                   1.0,
1846
                                   0.0,
1847
                                   0.0,
1848
                                   0.0};
98✔
1849

1850
        // XXX: Compute zone number for Gauss-Kruger (Transverse Mercator)
1851
        // projection if it is not specified.
1852
        if (nProj == 1L && poDS->sHeader.dfCenterLong == 0.0)
98✔
1853
        {
1854
            if (poDS->sExtHeader.nZone == 0)
6✔
1855
            {
1856
                double centerXCoord =
×
1857
                    poDS->sHeader.dfLLX +
×
1858
                    (poDS->nRasterXSize * poDS->sHeader.dfPixelSize / 2.0);
×
1859
                padfPrjParams[7] = floor((centerXCoord - 500000.0) / 1000000.0);
×
1860
            }
1861
            else
1862
            {
1863
                padfPrjParams[7] = poDS->sExtHeader.nZone;
6✔
1864
            }
1865
        }
1866

1867
        OGRErr res = OGRERR_FAILURE;
98✔
1868
        if (nProj >= 0 &&
98✔
1869
            (poDS->sExtHeader.nDatum >= 0 || poDS->sExtHeader.nEllipsoid >= 0))
88✔
1870
        {
1871
            res = poDS->m_oSRS.importFromPanorama(
88✔
1872
                nProj, poDS->sExtHeader.nDatum, poDS->sExtHeader.nEllipsoid,
88✔
1873
                padfPrjParams);
1874
        }
1875

1876
        if (poDS->sHeader.iEPSGCode > RMF_EPSG_MIN_CODE &&
111✔
1877
            (OGRERR_NONE != res || poDS->m_oSRS.IsLocal()))
13✔
1878
        {
1879
            res = poDS->m_oSRS.importFromEPSG(poDS->sHeader.iEPSGCode);
1✔
1880
        }
1881

1882
        const char *pszSetVertCS =
1883
            CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "RMF_SET_VERTCS",
98✔
1884
                                 CPLGetConfigOption("RMF_SET_VERTCS", "NO"));
1885
        if (CPLTestBool(pszSetVertCS) && res == OGRERR_NONE &&
98✔
1886
            poDS->sExtHeader.nVertDatum > 0)
×
1887
        {
1888
            poDS->m_oSRS.importVertCSFromPanorama(poDS->sExtHeader.nVertDatum);
×
1889
        }
1890
    }
1891

1892
    /* -------------------------------------------------------------------- */
1893
    /*  Set up georeferencing.                                              */
1894
    /* -------------------------------------------------------------------- */
1895
    if ((poDS->eRMFType == RMFT_RSW && poDS->sHeader.iGeorefFlag) ||
200✔
1896
        (poDS->eRMFType == RMFT_MTW && poDS->sHeader.dfPixelSize != 0.0))
128✔
1897
    {
1898
        poDS->m_gt[0] = poDS->sHeader.dfLLX;
146✔
1899
        poDS->m_gt[3] = poDS->sHeader.dfLLY +
292✔
1900
                        poDS->nRasterYSize * poDS->sHeader.dfPixelSize;
146✔
1901
        poDS->m_gt[1] = poDS->sHeader.dfPixelSize;
146✔
1902
        poDS->m_gt[5] = -poDS->sHeader.dfPixelSize;
146✔
1903
        poDS->m_gt[2] = 0.0;
146✔
1904
        poDS->m_gt[4] = 0.0;
146✔
1905
    }
1906

1907
    /* -------------------------------------------------------------------- */
1908
    /*  Set units.                                                          */
1909
    /* -------------------------------------------------------------------- */
1910

1911
    if (poDS->eRMFType == RMFT_MTW)
200✔
1912
    {
1913
        CPLFree(poDS->pszUnitType);
74✔
1914
        poDS->pszUnitType = RMFUnitTypeToStr(poDS->sHeader.iElevationUnit);
74✔
1915
    }
1916

1917
    /* -------------------------------------------------------------------- */
1918
    /*  Report some other dataset related information.                      */
1919
    /* -------------------------------------------------------------------- */
1920

1921
    if (poDS->eRMFType == RMFT_MTW)
200✔
1922
    {
1923
        char szTemp[256] = {};
74✔
1924

1925
        snprintf(szTemp, sizeof(szTemp), "%g", poDS->sHeader.adfElevMinMax[0]);
74✔
1926
        poDS->SetMetadataItem("ELEVATION_MINIMUM", szTemp);
74✔
1927

1928
        snprintf(szTemp, sizeof(szTemp), "%g", poDS->sHeader.adfElevMinMax[1]);
74✔
1929
        poDS->SetMetadataItem("ELEVATION_MAXIMUM", szTemp);
74✔
1930

1931
        poDS->SetMetadataItem("ELEVATION_UNITS", poDS->pszUnitType);
74✔
1932

1933
        snprintf(szTemp, sizeof(szTemp), "%d", poDS->sHeader.iElevationType);
74✔
1934
        poDS->SetMetadataItem("ELEVATION_TYPE", szTemp);
74✔
1935
    }
1936

1937
    /* -------------------------------------------------------------------- */
1938
    /*      Check for overviews.                                            */
1939
    /* -------------------------------------------------------------------- */
1940
    if (nNextHeaderOffset == 0 && poParentDS == nullptr)
200✔
1941
    {
1942
        poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
119✔
1943
    }
1944

1945
    /* Set frame */
1946
    if (poDS->sHeader.nROIOffset &&
200✔
1947
        poDS->sHeader.nROISize >= sizeof(RSWFrame) &&
107✔
1948
        poDS->sHeader.nROISize <= ROI_MAX_SIZE_TO_AVOID_EXCESSIVE_RAM_USAGE)
12✔
1949
    {
1950
        GByte *pabyROI = reinterpret_cast<GByte *>(
1951
            VSI_MALLOC_VERBOSE(poDS->sHeader.nROISize));
12✔
1952
        if (pabyROI == nullptr)
12✔
1953
        {
1954
            delete poDS;
×
1955
            return nullptr;
×
1956
        }
1957

1958
        VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nROIOffset),
12✔
1959
                  SEEK_SET);
1960
        if (VSIFReadL(pabyROI, poDS->sHeader.nROISize, 1, poDS->fp) != 1)
12✔
1961
        {
1962
            CPLError(CE_Failure, CPLE_FileIO, "Cannot read ROI");
×
1963
            CPLFree(pabyROI);
×
1964
            delete poDS;
×
1965
            return nullptr;
×
1966
        }
1967

1968
        GInt32 nFrameType;
1969
        RMF_READ_LONG(pabyROI, nFrameType, 0);
12✔
1970
        if (nFrameType == nPolygonType)
12✔
1971
        {
1972
            CPLString osWKT = "POLYGON((";
24✔
1973
            bool bFirst = true;
12✔
1974

1975
            CPLDebug("RMF", "ROI coordinates:");
12✔
1976
            /* coverity[tainted_data] */
1977
            for (GUInt32 i = sizeof(RSWFrame);
12✔
1978
                 i + sizeof(RSWFrameCoord) <= poDS->sHeader.nROISize;
1,734✔
1979
                 i += sizeof(RSWFrameCoord))
1,722✔
1980
            {
1981
                GInt32 nX, nY;
1982
                RMF_READ_LONG(pabyROI, nX, i);
1,722✔
1983
                RMF_READ_LONG(pabyROI, nY, i + 4);
1,722✔
1984

1985
                CPLDebug("RMF", "X: %d, Y: %d", nX, nY);
1,722✔
1986

1987
                double dfX =
1988
                    poDS->m_gt[0] + nX * poDS->m_gt[1] + nY * poDS->m_gt[2];
1,722✔
1989
                double dfY =
1990
                    poDS->m_gt[3] + nX * poDS->m_gt[4] + nY * poDS->m_gt[5];
1,722✔
1991

1992
                if (bFirst)
1,722✔
1993
                {
1994
                    osWKT += CPLSPrintf("%f %f", dfX, dfY);
12✔
1995
                    bFirst = false;
12✔
1996
                }
1997
                else
1998
                {
1999
                    osWKT += CPLSPrintf(", %f %f", dfX, dfY);
1,710✔
2000
                }
2001
            }
2002
            osWKT += "))";
12✔
2003
            CPLDebug("RMF", "Frame WKT: %s", osWKT.c_str());
12✔
2004
            poDS->SetMetadataItem(MD_FRAME_KEY, osWKT);
12✔
2005
        }
2006
        CPLFree(pabyROI);
12✔
2007
    }
2008

2009
#undef RMF_READ_DOUBLE
2010
#undef RMF_READ_LONG
2011
#undef RMF_READ_ULONG
2012

2013
    if (poDS->sHeader.nFlagsTblOffset && poDS->sHeader.nFlagsTblSize)
200✔
2014
    {
2015
        VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nFlagsTblOffset),
107✔
2016
                  SEEK_SET);
2017
        CPLDebug("RMF", "Blocks flags:");
107✔
2018
        /* coverity[tainted_data] */
2019
        for (GUInt32 i = 0; i < poDS->sHeader.nFlagsTblSize; i += sizeof(GByte))
416✔
2020
        {
2021
            GByte nValue;
2022
            if (VSIFReadL(&nValue, 1, sizeof(nValue), poDS->fp) !=
309✔
2023
                sizeof(nValue))
2024
            {
2025
                CPLDebug("RMF", "Cannot read Block flag at index %u", i);
×
2026
                break;
×
2027
            }
2028
            CPLDebug("RMF", "Block %u -- flag %d", i, nValue);
309✔
2029
        }
2030
    }
2031
    return poDS;
200✔
2032
}
2033

2034
/************************************************************************/
2035
/*                               Create()                               */
2036
/************************************************************************/
2037
GDALDataset *RMFDataset::Create(const char *pszFilename, int nXSize, int nYSize,
89✔
2038
                                int nBandsIn, GDALDataType eType,
2039
                                char **papszParamList)
2040
{
2041
    return Create(pszFilename, nXSize, nYSize, nBandsIn, eType, papszParamList,
89✔
2042
                  nullptr, 1.0);
89✔
2043
}
2044

2045
GDALDataset *RMFDataset::Create(const char *pszFilename, int nXSize, int nYSize,
123✔
2046
                                int nBandsIn, GDALDataType eType,
2047
                                char **papszParamList, RMFDataset *poParentDS,
2048
                                double dfOvFactor)
2049

2050
{
2051
    if (nBandsIn != 1 && nBandsIn != 3)
123✔
2052
    {
2053
        CPLError(CE_Failure, CPLE_NotSupported,
7✔
2054
                 "RMF driver doesn't support %d bands. Must be 1 or 3.",
2055
                 nBandsIn);
2056

2057
        return nullptr;
7✔
2058
    }
2059

2060
    if (nBandsIn == 1 && eType != GDT_Byte && eType != GDT_Int16 &&
116✔
2061
        eType != GDT_Int32 && eType != GDT_Float64)
52✔
2062
    {
2063
        CPLError(
17✔
2064
            CE_Failure, CPLE_AppDefined,
2065
            "Attempt to create RMF dataset with an illegal data type (%s), "
2066
            "only Byte, Int16, Int32 and Float64 types supported "
2067
            "by the format for single-band images.",
2068
            GDALGetDataTypeName(eType));
2069

2070
        return nullptr;
17✔
2071
    }
2072

2073
    if (nBandsIn == 3 && eType != GDT_Byte)
99✔
2074
    {
2075
        CPLError(
13✔
2076
            CE_Failure, CPLE_AppDefined,
2077
            "Attempt to create RMF dataset with an illegal data type (%s), "
2078
            "only Byte type supported by the format for three-band images.",
2079
            GDALGetDataTypeName(eType));
2080

2081
        return nullptr;
13✔
2082
    }
2083

2084
    /* -------------------------------------------------------------------- */
2085
    /*  Create the dataset.                                                 */
2086
    /* -------------------------------------------------------------------- */
2087
    RMFDataset *poDS = new RMFDataset();
86✔
2088

2089
    GUInt32 nBlockXSize =
86✔
2090
        (nXSize < RMF_DEFAULT_BLOCKXSIZE) ? nXSize : RMF_DEFAULT_BLOCKXSIZE;
86✔
2091
    GUInt32 nBlockYSize =
86✔
2092
        (nYSize < RMF_DEFAULT_BLOCKYSIZE) ? nYSize : RMF_DEFAULT_BLOCKYSIZE;
86✔
2093
    double dfScale;
2094
    double dfResolution;
2095
    double dfPixelSize;
2096
    if (poParentDS == nullptr)
86✔
2097
    {
2098
        poDS->fp = VSIFOpenL(pszFilename, "w+b");
52✔
2099
        if (poDS->fp == nullptr)
52✔
2100
        {
2101
            CPLError(CE_Failure, CPLE_OpenFailed, "Unable to create file %s.",
3✔
2102
                     pszFilename);
2103
            delete poDS;
3✔
2104
            return nullptr;
3✔
2105
        }
2106

2107
        const char *pszScaleValue =
2108
            CSLFetchNameValue(papszParamList, MD_SCALE_KEY);
49✔
2109
        if (pszScaleValue != nullptr && CPLStrnlen(pszScaleValue, 10) > 4)
49✔
2110
        {
2111
            dfScale = atof(pszScaleValue + 4);
×
2112
        }
2113
        else
2114
        {
2115
            dfScale = RMF_DEFAULT_SCALE;
49✔
2116
        }
2117
        dfResolution = RMF_DEFAULT_RESOLUTION;
49✔
2118
        dfPixelSize = 1;
49✔
2119

2120
        if (CPLFetchBool(papszParamList, "MTW", false))
49✔
2121
            poDS->eRMFType = RMFT_MTW;
12✔
2122
        else
2123
            poDS->eRMFType = RMFT_RSW;
37✔
2124

2125
        GUInt32 iVersion = RMF_VERSION;
49✔
2126
        const char *pszRMFHUGE = CSLFetchNameValue(papszParamList, "RMFHUGE");
49✔
2127

2128
        if (pszRMFHUGE == nullptr)
49✔
2129
            pszRMFHUGE = "NO";  // Keep old behavior by default
35✔
2130

2131
        if (EQUAL(pszRMFHUGE, "NO"))
49✔
2132
        {
2133
            iVersion = RMF_VERSION;
42✔
2134
        }
2135
        else if (EQUAL(pszRMFHUGE, "YES"))
7✔
2136
        {
2137
            iVersion = RMF_VERSION_HUGE;
7✔
2138
        }
2139
        else if (EQUAL(pszRMFHUGE, "IF_SAFER"))
×
2140
        {
2141
            const double dfImageSize =
2142
                static_cast<double>(nXSize) * static_cast<double>(nYSize) *
×
2143
                static_cast<double>(nBandsIn) *
×
2144
                static_cast<double>(GDALGetDataTypeSizeBytes(eType));
×
2145
            if (dfImageSize > 3.0 * 1024.0 * 1024.0 * 1024.0)
×
2146
            {
2147
                iVersion = RMF_VERSION_HUGE;
×
2148
            }
2149
            else
2150
            {
2151
                iVersion = RMF_VERSION;
×
2152
            }
2153
        }
2154

2155
        const char *pszValue = CSLFetchNameValue(papszParamList, "BLOCKXSIZE");
49✔
2156
        if (pszValue != nullptr)
49✔
2157
            nBlockXSize = atoi(pszValue);
1✔
2158
        if (static_cast<int>(nBlockXSize) <= 0)
49✔
2159
            nBlockXSize = RMF_DEFAULT_BLOCKXSIZE;
×
2160

2161
        pszValue = CSLFetchNameValue(papszParamList, "BLOCKYSIZE");
49✔
2162
        if (pszValue != nullptr)
49✔
2163
            nBlockYSize = atoi(pszValue);
1✔
2164
        if (static_cast<int>(nBlockYSize) <= 0)
49✔
2165
            nBlockYSize = RMF_DEFAULT_BLOCKXSIZE;
×
2166

2167
        if (poDS->eRMFType == RMFT_MTW)
49✔
2168
            memcpy(poDS->sHeader.bySignature, RMF_SigMTW, RMF_SIGNATURE_SIZE);
12✔
2169
        else
2170
            memcpy(poDS->sHeader.bySignature, RMF_SigRSW, RMF_SIGNATURE_SIZE);
37✔
2171
        poDS->sHeader.iVersion = iVersion;
49✔
2172
        poDS->sHeader.nOvrOffset = 0x00;
49✔
2173
    }
2174
    else
2175
    {
2176
        poDS->fp = poParentDS->fp;
34✔
2177
        memcpy(poDS->sHeader.bySignature, poParentDS->sHeader.bySignature,
34✔
2178
               RMF_SIGNATURE_SIZE);
2179
        poDS->sHeader.iVersion = poParentDS->sHeader.iVersion;
34✔
2180
        poDS->eRMFType = poParentDS->eRMFType;
34✔
2181
        nBlockXSize = poParentDS->sHeader.nTileWidth;
34✔
2182
        nBlockYSize = poParentDS->sHeader.nTileHeight;
34✔
2183
        dfScale = poParentDS->sHeader.dfScale;
34✔
2184
        dfResolution = poParentDS->sHeader.dfResolution / dfOvFactor;
34✔
2185
        dfPixelSize = poParentDS->sHeader.dfPixelSize * dfOvFactor;
34✔
2186

2187
        poDS->nHeaderOffset = poParentDS->GetLastOffset();
34✔
2188
        poParentDS->sHeader.nOvrOffset =
34✔
2189
            poDS->GetRMFOffset(poDS->nHeaderOffset, &poDS->nHeaderOffset);
34✔
2190
        poParentDS->bHeaderDirty = true;
34✔
2191
        VSIFSeekL(poDS->fp, poDS->nHeaderOffset, SEEK_SET);
34✔
2192
        poDS->poParentDS = poParentDS;
34✔
2193
        CPLDebug("RMF",
34✔
2194
                 "Create overview subfile at " CPL_FRMT_GUIB
2195
                 " with size %dx%d, parent overview offset %d",
2196
                 poDS->nHeaderOffset, nXSize, nYSize,
2197
                 poParentDS->sHeader.nOvrOffset);
2198
    }
2199
    /* -------------------------------------------------------------------- */
2200
    /*  Fill the RMFHeader                                                  */
2201
    /* -------------------------------------------------------------------- */
2202
    CPLDebug("RMF", "Version %d", poDS->sHeader.iVersion);
83✔
2203

2204
    poDS->sHeader.iUserID = 0x00;
83✔
2205
    memset(poDS->sHeader.byName, 0, sizeof(poDS->sHeader.byName));
83✔
2206
    poDS->sHeader.nBitDepth = GDALGetDataTypeSizeBits(eType) * nBandsIn;
83✔
2207
    poDS->sHeader.nHeight = nYSize;
83✔
2208
    poDS->sHeader.nWidth = nXSize;
83✔
2209
    poDS->sHeader.nTileWidth = nBlockXSize;
83✔
2210
    poDS->sHeader.nTileHeight = nBlockYSize;
83✔
2211

2212
    poDS->nXTiles = poDS->sHeader.nXTiles =
83✔
2213
        DIV_ROUND_UP(nXSize, poDS->sHeader.nTileWidth);
83✔
2214
    poDS->nYTiles = poDS->sHeader.nYTiles =
83✔
2215
        DIV_ROUND_UP(nYSize, poDS->sHeader.nTileHeight);
83✔
2216
    poDS->sHeader.nLastTileHeight = nYSize % poDS->sHeader.nTileHeight;
83✔
2217
    if (!poDS->sHeader.nLastTileHeight)
83✔
2218
        poDS->sHeader.nLastTileHeight = poDS->sHeader.nTileHeight;
44✔
2219
    poDS->sHeader.nLastTileWidth = nXSize % poDS->sHeader.nTileWidth;
83✔
2220
    if (!poDS->sHeader.nLastTileWidth)
83✔
2221
        poDS->sHeader.nLastTileWidth = poDS->sHeader.nTileWidth;
40✔
2222

2223
    // poDS->sHeader.nROIOffset = 0x00;
2224
    // poDS->sHeader.nROISize = 0x00;
2225

2226
    vsi_l_offset nCurPtr = poDS->nHeaderOffset + RMF_HEADER_SIZE;
83✔
2227

2228
    // Extended header
2229
    poDS->sHeader.nExtHdrOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
83✔
2230
    poDS->sHeader.nExtHdrSize = RMF_EXT_HEADER_SIZE;
83✔
2231
    nCurPtr += poDS->sHeader.nExtHdrSize;
83✔
2232

2233
    // Color table
2234
    if (poDS->eRMFType == RMFT_RSW && nBandsIn == 1)
83✔
2235
    {
2236
        if (poDS->sHeader.nBitDepth > 8)
34✔
2237
        {
2238
            CPLError(CE_Failure, CPLE_AppDefined,
6✔
2239
                     "Cannot create color table of RSW with nBitDepth = %d. "
2240
                     "Retry with MTW ?",
2241
                     poDS->sHeader.nBitDepth);
2242
            delete poDS;
6✔
2243
            return nullptr;
6✔
2244
        }
2245

2246
        poDS->sHeader.nClrTblOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
28✔
2247
        poDS->nColorTableSize = 1 << poDS->sHeader.nBitDepth;
28✔
2248
        poDS->sHeader.nClrTblSize = poDS->nColorTableSize * 4;
28✔
2249
        poDS->pabyColorTable =
28✔
2250
            static_cast<GByte *>(VSI_MALLOC_VERBOSE(poDS->sHeader.nClrTblSize));
28✔
2251
        if (poDS->pabyColorTable == nullptr)
28✔
2252
        {
2253
            delete poDS;
×
2254
            return nullptr;
×
2255
        }
2256
        for (GUInt32 i = 0; i < poDS->nColorTableSize; i++)
7,196✔
2257
        {
2258
            poDS->pabyColorTable[i * 4 + 0] = static_cast<GByte>(i);
7,168✔
2259
            poDS->pabyColorTable[i * 4 + 1] = static_cast<GByte>(i);
7,168✔
2260
            poDS->pabyColorTable[i * 4 + 2] = static_cast<GByte>(i);
7,168✔
2261
            poDS->pabyColorTable[i * 4 + 3] = 0;
7,168✔
2262
        }
2263
        nCurPtr += poDS->sHeader.nClrTblSize;
28✔
2264
    }
2265
    else
2266
    {
2267
        poDS->sHeader.nClrTblOffset = 0x00;
49✔
2268
        poDS->sHeader.nClrTblSize = 0x00;
49✔
2269
    }
2270

2271
    // Add room for ROI (frame)
2272
    poDS->sHeader.nROIOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
77✔
2273
    poDS->sHeader.nROISize = 0x00;
77✔
2274
    nCurPtr +=
77✔
2275
        sizeof(RSWFrame) +
2276
        sizeof(RSWFrameCoord) *
2277
            nMaxFramePointCount;  // Allocate nMaxFramePointCount coordinates for frame
2278

2279
    // Add blocks flags
2280
    poDS->sHeader.nFlagsTblOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
77✔
2281
    poDS->sHeader.nFlagsTblSize =
77✔
2282
        sizeof(GByte) * poDS->sHeader.nXTiles * poDS->sHeader.nYTiles;
77✔
2283
    nCurPtr += poDS->sHeader.nFlagsTblSize;
77✔
2284

2285
    // Blocks table
2286
    poDS->sHeader.nTileTblOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
77✔
2287
    poDS->sHeader.nTileTblSize =
77✔
2288
        2 * sizeof(GUInt32) * poDS->sHeader.nXTiles * poDS->sHeader.nYTiles;
77✔
2289
    poDS->paiTiles =
77✔
2290
        reinterpret_cast<GUInt32 *>(CPLCalloc(poDS->sHeader.nTileTblSize, 1));
77✔
2291
    // nCurPtr += poDS->sHeader.nTileTblSize;
2292
    const GUInt32 nTileSize = poDS->sHeader.nTileWidth *
77✔
2293
                              poDS->sHeader.nTileHeight *
77✔
2294
                              GDALGetDataTypeSizeBytes(eType);
77✔
2295
    poDS->sHeader.nSize =
77✔
2296
        poDS->paiTiles[poDS->sHeader.nTileTblSize / 4 - 2] + nTileSize;
77✔
2297

2298
    // Elevation units
2299
    poDS->sHeader.iElevationUnit = RMFStrToUnitType(poDS->pszUnitType);
77✔
2300

2301
    poDS->sHeader.iMapType = -1;
77✔
2302
    poDS->sHeader.iProjection = -1;
77✔
2303
    poDS->sHeader.iEPSGCode = -1;
77✔
2304
    poDS->sHeader.dfScale = dfScale;
77✔
2305
    poDS->sHeader.dfResolution = dfResolution;
77✔
2306
    poDS->sHeader.dfPixelSize = dfPixelSize;
77✔
2307
    poDS->sHeader.iMaskType = 0;
77✔
2308
    poDS->sHeader.iMaskStep = 0;
77✔
2309
    poDS->sHeader.iFrameFlag = 1;  // 1 - Frame not using
77✔
2310
    // poDS->sHeader.nFlagsTblOffset = 0x00;
2311
    // poDS->sHeader.nFlagsTblSize = 0x00;
2312
    poDS->sHeader.nFileSize0 = 0x00;
77✔
2313
    poDS->sHeader.nFileSize1 = 0x00;
77✔
2314
    poDS->sHeader.iUnknown = 0;
77✔
2315
    poDS->sHeader.iGeorefFlag = 0;
77✔
2316
    poDS->sHeader.iInverse = 0;
77✔
2317
    poDS->sHeader.iJpegQuality = 0;
77✔
2318
    memset(poDS->sHeader.abyInvisibleColors, 0,
77✔
2319
           sizeof(poDS->sHeader.abyInvisibleColors));
2320
    poDS->sHeader.iElevationType = 0;
77✔
2321

2322
    poDS->nRasterXSize = nXSize;
77✔
2323
    poDS->nRasterYSize = nYSize;
77✔
2324
    poDS->eAccess = GA_Update;
77✔
2325
    poDS->nBands = nBandsIn;
77✔
2326

2327
    if (poParentDS == nullptr)
77✔
2328
    {
2329
        poDS->sHeader.adfElevMinMax[0] = 0.0;
43✔
2330
        poDS->sHeader.adfElevMinMax[1] = 0.0;
43✔
2331
        poDS->sHeader.dfNoData = 0.0;
43✔
2332
        poDS->sHeader.iCompression =
43✔
2333
            GetCompressionType(CSLFetchNameValue(papszParamList, "COMPRESS"));
43✔
2334
        if (CE_None != poDS->InitCompressorData(papszParamList))
43✔
2335
        {
2336
            delete poDS;
×
2337
            return nullptr;
×
2338
        }
2339

2340
        if (poDS->sHeader.iCompression == RMF_COMPRESSION_JPEG)
43✔
2341
        {
2342
            const char *pszJpegQuality =
2343
                CSLFetchNameValue(papszParamList, "JPEG_QUALITY");
1✔
2344
            if (pszJpegQuality == nullptr)
1✔
2345
            {
2346
                poDS->sHeader.iJpegQuality = 75;
1✔
2347
            }
2348
            else
2349
            {
2350
                int iJpegQuality = atoi(pszJpegQuality);
×
2351
                if (iJpegQuality < 10 || iJpegQuality > 100)
×
2352
                {
2353
                    CPLError(CE_Failure, CPLE_IllegalArg,
×
2354
                             "JPEG_QUALITY=%s is not a legal value in the "
2355
                             "range 10-100.\n"
2356
                             "Defaulting to 75",
2357
                             pszJpegQuality);
2358
                    iJpegQuality = 75;
×
2359
                }
2360
                poDS->sHeader.iJpegQuality = static_cast<GByte>(iJpegQuality);
×
2361
            }
2362
        }
2363

2364
        if (CE_None != poDS->SetupCompression(eType, pszFilename))
43✔
2365
        {
2366
            delete poDS;
×
2367
            return nullptr;
×
2368
        }
2369
    }
2370
    else
2371
    {
2372
        poDS->sHeader.adfElevMinMax[0] = poParentDS->sHeader.adfElevMinMax[0];
34✔
2373
        poDS->sHeader.adfElevMinMax[1] = poParentDS->sHeader.adfElevMinMax[1];
34✔
2374
        poDS->sHeader.dfNoData = poParentDS->sHeader.dfNoData;
34✔
2375
        poDS->sHeader.iCompression = poParentDS->sHeader.iCompression;
34✔
2376
        poDS->sHeader.iJpegQuality = poParentDS->sHeader.iJpegQuality;
34✔
2377
        poDS->Decompress = poParentDS->Decompress;
34✔
2378
        poDS->Compress = poParentDS->Compress;
34✔
2379
        poDS->poCompressData = poParentDS->poCompressData;
34✔
2380
    }
2381

2382
    if (nBandsIn > 1)
77✔
2383
    {
2384
        poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
13✔
2385
    }
2386

2387
    poDS->WriteHeader();
77✔
2388

2389
    /* -------------------------------------------------------------------- */
2390
    /*      Create band information objects.                                */
2391
    /* -------------------------------------------------------------------- */
2392
    for (int iBand = 1; iBand <= poDS->nBands; iBand++)
180✔
2393
        poDS->SetBand(iBand, new RMFRasterBand(poDS, iBand, eType));
103✔
2394

2395
    poDS->SetupNBits();
77✔
2396

2397
    return GDALDataset::FromHandle(poDS);
77✔
2398
}
2399

2400
// GIS Panorama 11 was introduced new format for huge files (greater than 3 Gb)
2401
vsi_l_offset RMFDataset::GetFileOffset(GUInt32 iRMFOffset) const
3,859✔
2402
{
2403
    if (sHeader.iVersion >= RMF_VERSION_HUGE)
3,859✔
2404
    {
2405
        return (static_cast<vsi_l_offset>(iRMFOffset)) * RMF_HUGE_OFFSET_FACTOR;
1,142✔
2406
    }
2407

2408
    return static_cast<vsi_l_offset>(iRMFOffset);
2,717✔
2409
}
2410

2411
GUInt32 RMFDataset::GetRMFOffset(vsi_l_offset nFileOffset,
966✔
2412
                                 vsi_l_offset *pnNewFileOffset) const
2413
{
2414
    if (sHeader.iVersion >= RMF_VERSION_HUGE)
966✔
2415
    {
2416
        // Round offset to next RMF_HUGE_OFFSET_FACTOR
2417
        const GUInt32 iRMFOffset =
272✔
2418
            static_cast<GUInt32>((nFileOffset + (RMF_HUGE_OFFSET_FACTOR - 1)) /
272✔
2419
                                 RMF_HUGE_OFFSET_FACTOR);
2420
        if (pnNewFileOffset != nullptr)
272✔
2421
        {
2422
            *pnNewFileOffset = GetFileOffset(iRMFOffset);
205✔
2423
        }
2424
        return iRMFOffset;
272✔
2425
    }
2426

2427
    if (pnNewFileOffset != nullptr)
694✔
2428
    {
2429
        *pnNewFileOffset = nFileOffset;
561✔
2430
    }
2431
    return static_cast<GUInt32>(nFileOffset);
694✔
2432
}
2433

2434
RMFDataset *RMFDataset::OpenOverview(RMFDataset *poParent,
200✔
2435
                                     GDALOpenInfo *poOpenInfo)
2436
{
2437
    if (sHeader.nOvrOffset == 0)
200✔
2438
    {
2439
        return nullptr;
111✔
2440
    }
2441

2442
    if (poParent == nullptr)
89✔
2443
    {
2444
        return nullptr;
×
2445
    }
2446

2447
    vsi_l_offset nSubOffset = GetFileOffset(sHeader.nOvrOffset);
89✔
2448

2449
    CPLDebug("RMF",
89✔
2450
             "Try to open overview subfile at " CPL_FRMT_GUIB " for '%s'",
2451
             nSubOffset, poOpenInfo->pszFilename);
2452

2453
    if (!poParent->poOvrDatasets.empty())
89✔
2454
    {
2455
        if (poParent->GetFileOffset(poParent->sHeader.nOvrOffset) == nSubOffset)
49✔
2456
        {
2457
            CPLError(CE_Warning, CPLE_IllegalArg,
2✔
2458
                     "Recursive subdataset list is detected. "
2459
                     "Overview open failed.");
2460
            return nullptr;
2✔
2461
        }
2462

2463
        for (size_t n = 0; n != poParent->poOvrDatasets.size() - 1; ++n)
58✔
2464
        {
2465
            RMFDataset *poOvr(poParent->poOvrDatasets[n]);
13✔
2466

2467
            if (poOvr == nullptr)
13✔
2468
                continue;
×
2469
            if (poOvr->GetFileOffset(poOvr->sHeader.nOvrOffset) == nSubOffset)
13✔
2470
            {
2471
                CPLError(CE_Warning, CPLE_IllegalArg,
2✔
2472
                         "Recursive subdataset list is detected. "
2473
                         "Overview open failed.");
2474
                return nullptr;
2✔
2475
            }
2476
        }
2477
    }
2478

2479
    size_t nHeaderSize(RMF_HEADER_SIZE);
85✔
2480
    GByte *pabyNewHeader;
2481
    pabyNewHeader = static_cast<GByte *>(
2482
        CPLRealloc(poOpenInfo->pabyHeader, nHeaderSize + 1));
85✔
2483
    if (pabyNewHeader == nullptr)
85✔
2484
    {
2485
        CPLError(CE_Warning, CPLE_OutOfMemory,
×
2486
                 "Can't allocate buffer for overview header");
2487
        return nullptr;
×
2488
    }
2489

2490
    poOpenInfo->pabyHeader = pabyNewHeader;
85✔
2491
    memset(poOpenInfo->pabyHeader, 0, nHeaderSize + 1);
85✔
2492
    VSIFSeekL(fp, nSubOffset, SEEK_SET);
85✔
2493
    poOpenInfo->nHeaderBytes =
85✔
2494
        static_cast<int>(VSIFReadL(poOpenInfo->pabyHeader, 1, nHeaderSize, fp));
85✔
2495

2496
    return Open(poOpenInfo, poParent, nSubOffset);
85✔
2497
}
2498

2499
CPLErr RMFDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
17✔
2500
                                   const int *panOverviewList, int nBandsIn,
2501
                                   const int *panBandList,
2502
                                   GDALProgressFunc pfnProgress,
2503
                                   void *pProgressData,
2504
                                   CSLConstList papszOptions)
2505
{
2506
    bool bUseGenericHandling = false;
17✔
2507

2508
    if (GetAccess() != GA_Update)
17✔
2509
    {
2510
        CPLDebug("RMF", "File open for read-only accessing, "
×
2511
                        "creating overviews externally.");
2512

2513
        bUseGenericHandling = true;
×
2514
    }
2515

2516
    if (bUseGenericHandling)
17✔
2517
    {
2518
        if (!poOvrDatasets.empty())
×
2519
        {
2520
            CPLError(CE_Failure, CPLE_NotSupported,
×
2521
                     "Cannot add external overviews when there are already "
2522
                     "internal overviews");
2523
            return CE_Failure;
×
2524
        }
2525

2526
        return GDALDataset::IBuildOverviews(
×
2527
            pszResampling, nOverviews, panOverviewList, nBandsIn, panBandList,
2528
            pfnProgress, pProgressData, papszOptions);
×
2529
    }
2530

2531
    if (nBandsIn != GetRasterCount())
17✔
2532
    {
2533
        CPLError(CE_Failure, CPLE_NotSupported,
×
2534
                 "Generation of overviews in RMF is only "
2535
                 "supported when operating on all bands.  "
2536
                 "Operation failed.");
2537
        return CE_Failure;
×
2538
    }
2539

2540
    if (nOverviews == 0)
17✔
2541
    {
2542
        if (poOvrDatasets.empty())
×
2543
        {
2544
            return GDALDataset::IBuildOverviews(
×
2545
                pszResampling, nOverviews, panOverviewList, nBandsIn,
2546
                panBandList, pfnProgress, pProgressData, papszOptions);
×
2547
        }
2548
        return CleanOverviews();
×
2549
    }
2550

2551
    // First destroy old overviews
2552
    if (CE_None != CleanOverviews())
17✔
2553
    {
2554
        return CE_Failure;
×
2555
    }
2556

2557
    CPLDebug("RMF", "Build overviews on dataset %d x %d size", GetRasterXSize(),
17✔
2558
             GetRasterYSize());
2559

2560
    GDALDataType eMainType = GetRasterBand(1)->GetRasterDataType();
17✔
2561
    RMFDataset *poParent = this;
17✔
2562
    double prevOvLevel = 1.0;
17✔
2563
    for (int n = 0; n != nOverviews; ++n)
51✔
2564
    {
2565
        int nOvLevel = panOverviewList[n];
34✔
2566
        const int nOXSize = DIV_ROUND_UP(GetRasterXSize(), nOvLevel);
34✔
2567
        const int nOYSize = DIV_ROUND_UP(GetRasterYSize(), nOvLevel);
34✔
2568
        CPLDebug("RMF", "\tCreate overview #%d size %d x %d", nOvLevel, nOXSize,
34✔
2569
                 nOYSize);
2570

2571
        RMFDataset *poOvrDataset;
2572
        poOvrDataset = static_cast<RMFDataset *>(RMFDataset::Create(
34✔
2573
            nullptr, nOXSize, nOYSize, GetRasterCount(), eMainType, nullptr,
2574
            poParent, nOvLevel / prevOvLevel));
2575

2576
        if (poOvrDataset == nullptr)
34✔
2577
        {
2578
            CPLError(CE_Failure, CPLE_AppDefined,
×
2579
                     "Can't create overview dataset #%d size %d x %d", nOvLevel,
2580
                     nOXSize, nOYSize);
2581
            return CE_Failure;
×
2582
        }
2583

2584
        prevOvLevel = nOvLevel;
34✔
2585
        poParent = poOvrDataset;
34✔
2586
        poOvrDatasets.push_back(poOvrDataset);
34✔
2587
    }
2588

2589
    GDALRasterBand ***papapoOverviewBands =
2590
        static_cast<GDALRasterBand ***>(CPLCalloc(sizeof(void *), nBandsIn));
17✔
2591
    GDALRasterBand **papoBandList =
2592
        static_cast<GDALRasterBand **>(CPLCalloc(sizeof(void *), nBandsIn));
17✔
2593

2594
    for (int iBand = 0; iBand < nBandsIn; ++iBand)
36✔
2595
    {
2596
        GDALRasterBand *poBand = GetRasterBand(panBandList[iBand]);
19✔
2597

2598
        papoBandList[iBand] = poBand;
19✔
2599
        papapoOverviewBands[iBand] = static_cast<GDALRasterBand **>(
38✔
2600
            CPLCalloc(sizeof(void *), poBand->GetOverviewCount()));
19✔
2601

2602
        for (int i = 0; i < nOverviews; ++i)
57✔
2603
        {
2604
            papapoOverviewBands[iBand][i] = poBand->GetOverview(i);
38✔
2605
        }
2606
    }
2607
#ifdef DEBUG
2608
    for (int iBand = 0; iBand < nBandsIn; ++iBand)
36✔
2609
    {
2610
        CPLDebug("RMF", "Try to create overview for #%d size %d x %d",
19✔
2611
                 iBand + 1, papoBandList[iBand]->GetXSize(),
19✔
2612
                 papoBandList[iBand]->GetYSize());
19✔
2613
        for (int i = 0; i < nOverviews; ++i)
57✔
2614
        {
2615
            CPLDebug("RMF", "\t%d x %d",
38✔
2616
                     papapoOverviewBands[iBand][i]->GetXSize(),
38✔
2617
                     papapoOverviewBands[iBand][i]->GetYSize());
38✔
2618
        }
2619
    }
2620
#endif  // DEBUG
2621
    CPLErr res;
2622
    res = GDALRegenerateOverviewsMultiBand(
17✔
2623
        nBandsIn, papoBandList, nOverviews, papapoOverviewBands, pszResampling,
2624
        pfnProgress, pProgressData, papszOptions);
2625

2626
    for (int iBand = 0; iBand < nBandsIn; ++iBand)
36✔
2627
    {
2628
        CPLFree(papapoOverviewBands[iBand]);
19✔
2629
    }
2630

2631
    CPLFree(papapoOverviewBands);
17✔
2632
    CPLFree(papoBandList);
17✔
2633

2634
    return res;
17✔
2635
}
2636

2637
CPLErr RMFDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
68✔
2638
                             int nXSize, int nYSize, void *pData, int nBufXSize,
2639
                             int nBufYSize, GDALDataType eBufType,
2640
                             int nBandCount, BANDMAP_TYPE panBandMap,
2641
                             GSpacing nPixelSpace, GSpacing nLineSpace,
2642
                             GSpacing nBandSpace,
2643
                             GDALRasterIOExtraArg *psExtraArg)
2644
{
2645
#ifdef DEBUG
2646
    CPLDebug("RMF", "Dataset %p, %s %d %d %d %d, %d %d", this,
68✔
2647
             (eRWFlag == GF_Read ? "Read" : "Write"), nXOff, nYOff, nXSize,
2648
             nYSize, nBufXSize, nBufYSize);
2649
#endif  // DEBUG
2650
    if (eRWFlag == GF_Read && poCompressData != nullptr &&
68✔
2651
        poCompressData->oThreadPool.GetThreadCount() > 0)
×
2652
    {
2653
        poCompressData->oThreadPool.WaitCompletion();
×
2654
    }
2655

2656
    return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
68✔
2657
                                  nBufXSize, nBufYSize, eBufType, nBandCount,
2658
                                  panBandMap, nPixelSpace, nLineSpace,
2659
                                  nBandSpace, psExtraArg);
68✔
2660
}
2661

2662
vsi_l_offset RMFDataset::GetLastOffset() const
238✔
2663
{
2664
    vsi_l_offset nLastTileOff = 0;
238✔
2665
    GUInt32 nTiles(sHeader.nTileTblSize / sizeof(GUInt32));
238✔
2666

2667
    for (GUInt32 n = 0; n < nTiles; n += 2)
768✔
2668
    {
2669
        vsi_l_offset nTileOffset = GetFileOffset(paiTiles[n]);
530✔
2670
        GUInt32 nTileBytes = paiTiles[n + 1];
530✔
2671
        nLastTileOff = std::max(nLastTileOff, nTileOffset + nTileBytes);
530✔
2672
    }
2673

2674
    nLastTileOff = std::max(nLastTileOff, GetFileOffset(sHeader.nROIOffset) +
238✔
2675
                                              sHeader.nROISize);
238✔
2676
    nLastTileOff = std::max(nLastTileOff, GetFileOffset(sHeader.nClrTblOffset) +
238✔
2677
                                              sHeader.nClrTblSize);
238✔
2678
    nLastTileOff =
238✔
2679
        std::max(nLastTileOff,
238✔
2680
                 GetFileOffset(sHeader.nTileTblOffset) + sHeader.nTileTblSize);
238✔
2681
    nLastTileOff =
238✔
2682
        std::max(nLastTileOff, GetFileOffset(sHeader.nFlagsTblOffset) +
238✔
2683
                                   sHeader.nFlagsTblSize);
238✔
2684
    nLastTileOff = std::max(nLastTileOff, GetFileOffset(sHeader.nExtHdrOffset) +
238✔
2685
                                              sHeader.nExtHdrSize);
238✔
2686
    return nLastTileOff;
238✔
2687
}
2688

2689
CPLErr RMFDataset::CleanOverviews()
17✔
2690
{
2691
    if (sHeader.nOvrOffset == 0)
17✔
2692
    {
2693
        return CE_None;
13✔
2694
    }
2695

2696
    if (GetAccess() != GA_Update)
4✔
2697
    {
2698
        CPLError(CE_Failure, CPLE_NotSupported,
×
2699
                 "File open for read-only accessing, "
2700
                 "overviews cleanup failed.");
2701
        return CE_Failure;
×
2702
    }
2703

2704
    if (poParentDS != nullptr)
4✔
2705
    {
2706
        CPLError(CE_Failure, CPLE_NotSupported,
×
2707
                 "Overviews cleanup for non-root dataset is not possible.");
2708
        return CE_Failure;
×
2709
    }
2710

2711
    for (size_t n = 0; n != poOvrDatasets.size(); ++n)
12✔
2712
    {
2713
        GDALClose(poOvrDatasets[n]);
8✔
2714
    }
2715
    poOvrDatasets.clear();
4✔
2716

2717
    vsi_l_offset nLastTileOff = GetLastOffset();
4✔
2718

2719
    if (0 != VSIFSeekL(fp, 0, SEEK_END))
4✔
2720
    {
2721
        CPLError(CE_Failure, CPLE_FileIO,
×
2722
                 "Failed to seek to end of file, "
2723
                 "overviews cleanup failed.");
2724
    }
2725

2726
    vsi_l_offset nFileSize = VSIFTellL(fp);
4✔
2727
    if (nFileSize < nLastTileOff)
4✔
2728
    {
2729
        CPLError(CE_Failure, CPLE_FileIO,
×
2730
                 "Invalid file offset, "
2731
                 "overviews cleanup failed.");
2732
        return CE_Failure;
×
2733
    }
2734

2735
    CPLDebug("RMF", "Truncate to " CPL_FRMT_GUIB, nLastTileOff);
4✔
2736
    CPLDebug("RMF", "File size:  " CPL_FRMT_GUIB, nFileSize);
4✔
2737

2738
    if (0 != VSIFTruncateL(fp, nLastTileOff))
4✔
2739
    {
2740
        CPLError(CE_Failure, CPLE_FileIO,
×
2741
                 "Failed to truncate file, "
2742
                 "overviews cleanup failed.");
2743
        return CE_Failure;
×
2744
    }
2745

2746
    sHeader.nOvrOffset = 0;
4✔
2747
    bHeaderDirty = true;
4✔
2748

2749
    return CE_None;
4✔
2750
}
2751

2752
/************************************************************************/
2753
/*                         GetCompressionType()                         */
2754
/************************************************************************/
2755

2756
GByte RMFDataset::GetCompressionType(const char *pszCompressName)
43✔
2757
{
2758
    if (pszCompressName == nullptr || EQUAL(pszCompressName, "NONE"))
43✔
2759
    {
2760
        return RMF_COMPRESSION_NONE;
35✔
2761
    }
2762
    else if (EQUAL(pszCompressName, "LZW"))
8✔
2763
    {
2764
        return RMF_COMPRESSION_LZW;
5✔
2765
    }
2766
    else if (EQUAL(pszCompressName, "JPEG"))
3✔
2767
    {
2768
        return RMF_COMPRESSION_JPEG;
1✔
2769
    }
2770
    else if (EQUAL(pszCompressName, "RMF_DEM"))
2✔
2771
    {
2772
        return RMF_COMPRESSION_DEM;
2✔
2773
    }
2774

2775
    CPLError(CE_Failure, CPLE_AppDefined,
×
2776
             "RMF: Unknown compression scheme <%s>.\n"
2777
             "Defaults to NONE compression.",
2778
             pszCompressName);
2779
    return RMF_COMPRESSION_NONE;
×
2780
}
2781

2782
/************************************************************************/
2783
/*                        SetupCompression()                            */
2784
/************************************************************************/
2785

2786
int RMFDataset::SetupCompression(GDALDataType eType, const char *pszFilename)
243✔
2787
{
2788
    /* -------------------------------------------------------------------- */
2789
    /*  XXX: The DEM compression method seems to be only applicable         */
2790
    /*  to Int32 data.                                                      */
2791
    /* -------------------------------------------------------------------- */
2792
    if (sHeader.iCompression == RMF_COMPRESSION_NONE)
243✔
2793
    {
2794
        Decompress = nullptr;
173✔
2795
        Compress = nullptr;
173✔
2796
    }
2797
    else if (sHeader.iCompression == RMF_COMPRESSION_LZW)
70✔
2798
    {
2799
        Decompress = &LZWDecompress;
57✔
2800
        Compress = &LZWCompress;
57✔
2801
        SetMetadataItem("COMPRESSION", "LZW", "IMAGE_STRUCTURE");
57✔
2802
    }
2803
    else if (sHeader.iCompression == RMF_COMPRESSION_JPEG)
13✔
2804
    {
2805
        if (eType != GDT_Byte || nBands != RMF_JPEG_BAND_COUNT ||
3✔
2806
            sHeader.nBitDepth != 24)
3✔
2807
        {
2808
            CPLError(CE_Failure, CPLE_AppDefined,
×
2809
                     "RMF support only 24 bpp JPEG compressed files.");
2810
            return CE_Failure;
×
2811
        }
2812
#ifdef HAVE_LIBJPEG
2813
        CPLString oBuf;
6✔
2814
        oBuf.Printf("%d", sHeader.iJpegQuality);
3✔
2815
        Decompress = &JPEGDecompress;
3✔
2816
        Compress = &JPEGCompress;
3✔
2817
        SetMetadataItem("JPEG_QUALITY", oBuf.c_str(), "IMAGE_STRUCTURE");
3✔
2818
        SetMetadataItem("COMPRESSION", "JPEG", "IMAGE_STRUCTURE");
3✔
2819
#else   // HAVE_LIBJPEG
2820
        CPLError(CE_Failure, CPLE_AppDefined,
2821
                 "JPEG codec is needed to open <%s>.\n"
2822
                 "Please rebuild GDAL with libjpeg support.",
2823
                 pszFilename);
2824
        return CE_Failure;
2825
#endif  // HAVE_LIBJPEG
2826
    }
2827
    else if (sHeader.iCompression == RMF_COMPRESSION_DEM &&
10✔
2828
             eType == GDT_Int32 && nBands == RMF_DEM_BAND_COUNT)
10✔
2829
    {
2830
        Decompress = &DEMDecompress;
10✔
2831
        Compress = &DEMCompress;
10✔
2832
        SetMetadataItem("COMPRESSION", "RMF_DEM", "IMAGE_STRUCTURE");
10✔
2833
    }
2834
    else
2835
    {
2836
        CPLError(CE_Failure, CPLE_AppDefined,
×
2837
                 "Unknown compression #%d at file <%s>.", sHeader.iCompression,
×
2838
                 pszFilename);
2839
        return CE_Failure;
×
2840
    }
2841

2842
    return CE_None;
243✔
2843
}
2844

2845
void RMFDataset::WriteTileJobFunc(void *pData)
190✔
2846
{
2847
    RMFCompressionJob *psJob = static_cast<RMFCompressionJob *>(pData);
190✔
2848
    RMFDataset *poDS = psJob->poDS;
190✔
2849

2850
    GByte *pabyTileData;
2851
    size_t nTileSize;
2852

2853
    if (poDS->Compress)
190✔
2854
    {
2855
        // RMF doesn't store compressed tiles with size greater than 80% of
2856
        // uncompressed size
2857
        GUInt32 nMaxCompressedTileSize =
131✔
2858
            static_cast<GUInt32>((psJob->nUncompressedBytes * 8) / 10);
131✔
2859
        size_t nCompressedBytes =
2860
            poDS->Compress(psJob->pabyUncompressedData,
262✔
2861
                           static_cast<GUInt32>(psJob->nUncompressedBytes),
131✔
2862
                           psJob->pabyCompressedData, nMaxCompressedTileSize,
2863
                           psJob->nXSize, psJob->nYSize, poDS);
2864
        if (nCompressedBytes == 0)
131✔
2865
        {
2866
            pabyTileData = psJob->pabyUncompressedData;
28✔
2867
            nTileSize = psJob->nUncompressedBytes;
28✔
2868
        }
2869
        else
2870
        {
2871
            pabyTileData = psJob->pabyCompressedData;
103✔
2872
            nTileSize = nCompressedBytes;
103✔
2873
        }
2874
    }
2875
    else
2876
    {
2877
        pabyTileData = psJob->pabyUncompressedData;
59✔
2878
        nTileSize = psJob->nUncompressedBytes;
59✔
2879
    }
2880

2881
    {
2882
        CPLMutexHolder oHolder(poDS->poCompressData->hWriteTileMutex);
190✔
2883
        psJob->eResult = poDS->WriteRawTile(
190✔
2884
            psJob->nBlockXOff, psJob->nBlockYOff, pabyTileData, nTileSize);
2885
    }
2886
    if (poDS->poCompressData->oThreadPool.GetThreadCount() > 0)
190✔
2887
    {
2888
        CPLMutexHolder oHolder(poDS->poCompressData->hReadyJobMutex);
188✔
2889
        poDS->poCompressData->asReadyJobs.push_back(psJob);
94✔
2890
    }
2891
}
190✔
2892

2893
CPLErr RMFDataset::InitCompressorData(char **papszParamList)
55✔
2894
{
2895
    const char *pszNumThreads =
2896
        CSLFetchNameValue(papszParamList, "NUM_THREADS");
55✔
2897
    if (pszNumThreads == nullptr)
55✔
2898
        pszNumThreads = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
51✔
2899

2900
    int nThreads = 0;
55✔
2901
    if (pszNumThreads != nullptr)
55✔
2902
    {
2903
        nThreads = EQUAL(pszNumThreads, "ALL_CPUS") ? CPLGetNumCPUs()
8✔
2904
                                                    : atoi(pszNumThreads);
4✔
2905
    }
2906

2907
    if (nThreads < 0)
55✔
2908
    {
2909
        nThreads = 0;
×
2910
    }
2911
    if (nThreads > 1024)
55✔
2912
    {
2913
        nThreads = 1024;
×
2914
    }
2915

2916
    poCompressData = std::make_shared<RMFCompressData>();
55✔
2917
    if (nThreads > 0)
55✔
2918
    {
2919
        if (!poCompressData->oThreadPool.Setup(nThreads, nullptr, nullptr))
3✔
2920
        {
2921
            CPLError(CE_Failure, CPLE_AppDefined,
×
2922
                     "Can't setup %d compressor threads", nThreads);
2923
            return CE_Failure;
×
2924
        }
2925
    }
2926

2927
    poCompressData->asJobs.resize(nThreads + 1);
55✔
2928

2929
    size_t nMaxTileBytes =
55✔
2930
        sHeader.nTileWidth * sHeader.nTileHeight * sHeader.nBitDepth / 8;
55✔
2931
    size_t nCompressBufferSize =
2932
        2 * nMaxTileBytes * poCompressData->asJobs.size();
55✔
2933
    poCompressData->pabyBuffers =
110✔
2934
        static_cast<GByte *>(VSIMalloc(nCompressBufferSize));
55✔
2935

2936
    CPLDebug("RMF", "Setup %d compressor threads and allocate %lu bytes buffer",
55✔
2937
             nThreads, static_cast<unsigned long>(nCompressBufferSize));
2938
    if (poCompressData->pabyBuffers == nullptr)
55✔
2939
    {
2940
        CPLError(CE_Failure, CPLE_OutOfMemory,
×
2941
                 "Can't allocate compress buffer of size %lu.",
2942
                 static_cast<unsigned long>(nCompressBufferSize));
2943
        return CE_Failure;
×
2944
    }
2945

2946
    for (size_t i = 0; i != poCompressData->asJobs.size(); ++i)
122✔
2947
    {
2948
        RMFCompressionJob &sJob(poCompressData->asJobs[i]);
67✔
2949
        sJob.pabyCompressedData =
67✔
2950
            poCompressData->pabyBuffers + 2 * i * nMaxTileBytes;
67✔
2951
        sJob.pabyUncompressedData = sJob.pabyCompressedData + nMaxTileBytes;
67✔
2952
        poCompressData->asReadyJobs.push_back(&sJob);
67✔
2953
    }
2954

2955
    if (nThreads > 0)
55✔
2956
    {
2957
        poCompressData->hReadyJobMutex = CPLCreateMutex();
3✔
2958
        CPLReleaseMutex(poCompressData->hReadyJobMutex);
3✔
2959
        poCompressData->hWriteTileMutex = CPLCreateMutex();
3✔
2960
        CPLReleaseMutex(poCompressData->hWriteTileMutex);
3✔
2961
    }
2962

2963
    return CE_None;
55✔
2964
}
2965

2966
CPLErr RMFDataset::WriteTile(int nBlockXOff, int nBlockYOff, GByte *pabyData,
190✔
2967
                             size_t nBytes, GUInt32 nRawXSize,
2968
                             GUInt32 nRawYSize)
2969
{
2970
    RMFCompressionJob *poJob = nullptr;
190✔
2971
    if (poCompressData == nullptr)
190✔
2972
    {
2973
        CPLError(CE_Failure, CPLE_AppDefined, "RMF: Compress data is null");
×
2974
        return CE_Failure;
×
2975
    }
2976

2977
    if (poCompressData->oThreadPool.GetThreadCount() > 0)
190✔
2978
    {
2979
        size_t nJobs(poCompressData->asJobs.size());
94✔
2980

2981
        poCompressData->oThreadPool.WaitCompletion(static_cast<int>(nJobs - 1));
94✔
2982

2983
        CPLMutexHolder oHolder(poCompressData->hReadyJobMutex);
188✔
2984
        CPLAssert(!poCompressData->asReadyJobs.empty());
94✔
2985
        poJob = poCompressData->asReadyJobs.front();
94✔
2986
        poCompressData->asReadyJobs.pop_front();
94✔
2987
    }
2988
    else
2989
    {
2990
        poJob = poCompressData->asReadyJobs.front();
96✔
2991
    }
2992

2993
    if (poJob->eResult != CE_None)
190✔
2994
    {
2995
        // One of the previous jobs is not done.
2996
        // Detailed debug message is already emitted from WriteRawTile
2997
        return poJob->eResult;
×
2998
    }
2999
    poJob->poDS = this;
190✔
3000
    poJob->eResult = CE_Failure;
190✔
3001
    poJob->nBlockXOff = nBlockXOff;
190✔
3002
    poJob->nBlockYOff = nBlockYOff;
190✔
3003
    poJob->nUncompressedBytes = nBytes;
190✔
3004
    poJob->nXSize = nRawXSize;
190✔
3005
    poJob->nYSize = nRawYSize;
190✔
3006

3007
    memcpy(poJob->pabyUncompressedData, pabyData, nBytes);
190✔
3008

3009
    if (poCompressData->oThreadPool.GetThreadCount() > 0)
190✔
3010
    {
3011
        if (!poCompressData->oThreadPool.SubmitJob(WriteTileJobFunc, poJob))
94✔
3012
        {
3013
            CPLError(CE_Failure, CPLE_NotSupported,
×
3014
                     "Can't submit job to thread pool.");
3015
            return CE_Failure;
×
3016
        }
3017
    }
3018
    else
3019
    {
3020
        WriteTileJobFunc(poJob);
96✔
3021
        if (poJob->eResult != CE_None)
96✔
3022
        {
3023
            return poJob->eResult;
×
3024
        }
3025
    }
3026

3027
    return CE_None;
190✔
3028
}
3029

3030
CPLErr RMFDataset::WriteRawTile(int nBlockXOff, int nBlockYOff, GByte *pabyData,
190✔
3031
                                size_t nTileBytes)
3032
{
3033
    CPLAssert(nBlockXOff >= 0 && nBlockYOff >= 0 && pabyData != nullptr &&
190✔
3034
              nTileBytes > 0);
3035

3036
    const GUInt32 nTile = nBlockYOff * nXTiles + nBlockXOff;
190✔
3037

3038
    vsi_l_offset nTileOffset = GetFileOffset(paiTiles[2 * nTile]);
190✔
3039
    size_t nTileSize = static_cast<size_t>(paiTiles[2 * nTile + 1]);
190✔
3040

3041
    if (nTileOffset && nTileSize <= nTileBytes)
190✔
3042
    {
3043
        if (VSIFSeekL(fp, nTileOffset, SEEK_SET) < 0)
×
3044
        {
3045
            CPLError(
×
3046
                CE_Failure, CPLE_FileIO,
3047
                "Can't seek to offset %ld in output file to write data.\n%s",
3048
                static_cast<long>(nTileOffset), VSIStrerror(errno));
×
3049
            return CE_Failure;
×
3050
        }
3051
    }
3052
    else
3053
    {
3054
        if (VSIFSeekL(fp, 0, SEEK_END) < 0)
190✔
3055
        {
3056
            CPLError(
×
3057
                CE_Failure, CPLE_FileIO,
3058
                "Can't seek to offset %ld in output file to write data.\n%s",
3059
                static_cast<long>(nTileOffset), VSIStrerror(errno));
×
3060
            return CE_Failure;
×
3061
        }
3062
        nTileOffset = VSIFTellL(fp);
190✔
3063
        vsi_l_offset nNewTileOffset = 0;
190✔
3064
        paiTiles[2 * nTile] = GetRMFOffset(nTileOffset, &nNewTileOffset);
190✔
3065

3066
        if (nTileOffset != nNewTileOffset)
190✔
3067
        {
3068
            if (VSIFSeekL(fp, nNewTileOffset, SEEK_SET) < 0)
23✔
3069
            {
3070
                CPLError(CE_Failure, CPLE_FileIO,
×
3071
                         "Can't seek to offset %ld in output file to "
3072
                         "write data.\n%s",
3073
                         static_cast<long>(nNewTileOffset), VSIStrerror(errno));
×
3074
                return CE_Failure;
×
3075
            }
3076
        }
3077
        bHeaderDirty = true;
190✔
3078
    }
3079

3080
#ifdef CPL_MSB
3081
    // Compressed tiles are already with proper byte order
3082
    if (eRMFType == RMFT_MTW && sHeader.iCompression == RMF_COMPRESSION_NONE)
3083
    {
3084
        // Byte swap can be done in place
3085
        if (sHeader.nBitDepth == 16)
3086
        {
3087
            for (size_t i = 0; i < nTileBytes; i += 2)
3088
                CPL_SWAP16PTR(pabyData + i);
3089
        }
3090
        else if (sHeader.nBitDepth == 32)
3091
        {
3092
            for (size_t i = 0; i < nTileBytes; i += 4)
3093
                CPL_SWAP32PTR(pabyData + i);
3094
        }
3095
        else if (sHeader.nBitDepth == 64)
3096
        {
3097
            for (size_t i = 0; i < nTileBytes; i += 8)
3098
                CPL_SWAPDOUBLE(pabyData + i);
3099
        }
3100
    }
3101
#endif
3102

3103
    bool bOk = (VSIFWriteL(pabyData, 1, nTileBytes, fp) == nTileBytes);
190✔
3104

3105
    if (!bOk)
190✔
3106
    {
3107
        CPLError(CE_Failure, CPLE_FileIO,
×
3108
                 "Can't write tile with X offset %d and Y offset %d.\n%s",
3109
                 nBlockXOff, nBlockYOff, VSIStrerror(errno));
×
3110
        return CE_Failure;
×
3111
    }
3112

3113
    paiTiles[2 * nTile + 1] = static_cast<GUInt32>(nTileBytes);
190✔
3114
    bHeaderDirty = true;
190✔
3115

3116
    return CE_None;
190✔
3117
}
3118

3119
CPLErr RMFDataset::ReadTile(int nBlockXOff, int nBlockYOff, GByte *pabyData,
352✔
3120
                            size_t nRawBytes, GUInt32 nRawXSize,
3121
                            GUInt32 nRawYSize, bool &bNullTile)
3122
{
3123
    bNullTile = false;
352✔
3124

3125
    const GUInt32 nTile = nBlockYOff * nXTiles + nBlockXOff;
352✔
3126
    if (2 * nTile + 1 >= sHeader.nTileTblSize / sizeof(GUInt32))
352✔
3127
    {
3128
        return CE_Failure;
×
3129
    }
3130
    vsi_l_offset nTileOffset = GetFileOffset(paiTiles[2 * nTile]);
352✔
3131
    GUInt32 nTileBytes = paiTiles[2 * nTile + 1];
352✔
3132
    // RMF doesn't store compressed tiles with size greater than 80% of
3133
    // uncompressed size. But just in case, select twice as many.
3134
    GUInt32 nMaxTileBytes =
352✔
3135
        2 * sHeader.nTileWidth * sHeader.nTileHeight * sHeader.nBitDepth / 8;
352✔
3136

3137
    if (nTileBytes >= nMaxTileBytes)
352✔
3138
    {
3139
        CPLError(CE_Failure, CPLE_AppDefined,
×
3140
                 "Invalid tile size %lu at offset %ld. Must be less than %lu",
3141
                 static_cast<unsigned long>(nTileBytes),
3142
                 static_cast<long>(nTileOffset),
3143
                 static_cast<unsigned long>(nMaxTileBytes));
3144
        return CE_Failure;
×
3145
    }
3146

3147
    if (nTileOffset == 0)
352✔
3148
    {
3149
        bNullTile = true;
1✔
3150
        return CE_None;
1✔
3151
    }
3152

3153
#ifdef DEBUG
3154
    CPLDebug("RMF", "Read RawSize [%d, %d], nTileBytes %d, nRawBytes %d",
351✔
3155
             nRawXSize, nRawYSize, static_cast<int>(nTileBytes),
3156
             static_cast<int>(nRawBytes));
3157
#endif  // DEBUG
3158

3159
    if (VSIFSeekL(fp, nTileOffset, SEEK_SET) < 0)
351✔
3160
    {
3161
        // XXX: We will not report error here, because file just may be
3162
        // in update state and data for this block will be available later
3163
        if (eAccess == GA_Update)
×
3164
            return CE_None;
×
3165

3166
        CPLError(CE_Failure, CPLE_FileIO,
×
3167
                 "Can't seek to offset %ld in input file to read data.\n%s",
3168
                 static_cast<long>(nTileOffset), VSIStrerror(errno));
×
3169
        return CE_Failure;
×
3170
    }
3171

3172
    if (Decompress == nullptr || nTileBytes == nRawBytes)
351✔
3173
    {
3174
        if (nTileBytes != nRawBytes)
166✔
3175
        {
3176
            CPLError(CE_Failure, CPLE_AppDefined,
×
3177
                     "RMF: Invalid tile size %lu, expected %lu",
3178
                     static_cast<unsigned long>(nTileBytes),
3179
                     static_cast<unsigned long>(nRawBytes));
3180
            return CE_Failure;
×
3181
        }
3182

3183
        if (VSIFReadL(pabyData, 1, nRawBytes, fp) < nRawBytes)
166✔
3184
        {
3185
            CPLError(CE_Failure, CPLE_FileIO,
×
3186
                     "RMF: Can't read at offset %lu from input file.\n%s",
3187
                     static_cast<unsigned long>(nTileOffset),
3188
                     VSIStrerror(errno));
×
3189
            return CE_Failure;
×
3190
        }
3191

3192
#ifdef CPL_MSB
3193
        if (eRMFType == RMFT_MTW)
3194
        {
3195
            if (sHeader.nBitDepth == 16)
3196
            {
3197
                for (GUInt32 i = 0; i < nRawBytes; i += 2)
3198
                    CPL_SWAP16PTR(pabyData + i);
3199
            }
3200
            else if (sHeader.nBitDepth == 32)
3201
            {
3202
                for (GUInt32 i = 0; i < nRawBytes; i += 4)
3203
                    CPL_SWAP32PTR(pabyData + i);
3204
            }
3205
            else if (sHeader.nBitDepth == 64)
3206
            {
3207
                for (GUInt32 i = 0; i < nRawBytes; i += 8)
3208
                    CPL_SWAPDOUBLE(pabyData + i);
3209
            }
3210
        }
3211
#endif
3212
        return CE_None;
166✔
3213
    }
3214

3215
    if (pabyDecompressBuffer == nullptr)
185✔
3216
    {
3217
        pabyDecompressBuffer =
21✔
3218
            static_cast<GByte *>(VSIMalloc(std::max(1U, nMaxTileBytes)));
21✔
3219
        if (!pabyDecompressBuffer)
21✔
3220
        {
3221
            CPLError(CE_Failure, CPLE_OutOfMemory,
×
3222
                     "Can't allocate decompress buffer of size %lu.\n%s",
3223
                     static_cast<unsigned long>(nMaxTileBytes),
3224
                     VSIStrerror(errno));
×
3225
            return CE_Failure;
×
3226
        }
3227
    }
3228

3229
    if (VSIFReadL(pabyDecompressBuffer, 1, nTileBytes, fp) < nTileBytes)
185✔
3230
    {
3231
        CPLError(CE_Failure, CPLE_FileIO,
×
3232
                 "RMF: Can't read at offset %lu from input file.\n%s",
3233
                 static_cast<unsigned long>(nTileOffset), VSIStrerror(errno));
×
3234
        return CE_Failure;
×
3235
    }
3236

3237
    size_t nDecompressedSize =
3238
        Decompress(pabyDecompressBuffer, nTileBytes, pabyData,
185✔
3239
                   static_cast<GUInt32>(nRawBytes), nRawXSize, nRawYSize);
3240

3241
    if (nDecompressedSize != static_cast<size_t>(nRawBytes))
185✔
3242
    {
3243
        CPLError(CE_Failure, CPLE_FileIO,
×
3244
                 "Can't decompress tile xOff %d yOff %d. "
3245
                 "Raw tile size is %lu but decompressed is %lu. "
3246
                 "Compressed tile size is %lu",
3247
                 nBlockXOff, nBlockYOff, static_cast<unsigned long>(nRawBytes),
3248
                 static_cast<unsigned long>(nDecompressedSize),
3249
                 static_cast<unsigned long>(nTileBytes));
3250
        return CE_Failure;
×
3251
    }
3252
    // We don't need to swap bytes here,
3253
    // because decompressed data is in proper byte order
3254
    return CE_None;
185✔
3255
}
3256

3257
void RMFDataset::SetupNBits()
277✔
3258
{
3259
    int nBitDepth = 0;
277✔
3260
    if (sHeader.nBitDepth < 8 && nBands == 1)
277✔
3261
    {
3262
        nBitDepth = static_cast<int>(sHeader.nBitDepth);
6✔
3263
    }
3264
    else if (sHeader.nBitDepth == 16 && nBands == 3 && eRMFType == RMFT_RSW)
271✔
3265
    {
3266
        nBitDepth = 5;
×
3267
    }
3268

3269
    if (nBitDepth > 0)
277✔
3270
    {
3271
        char szNBits[32] = {};
6✔
3272
        snprintf(szNBits, sizeof(szNBits), "%d", nBitDepth);
6✔
3273
        for (int iBand = 1; iBand <= nBands; iBand++)
12✔
3274
        {
3275
            GetRasterBand(iBand)->SetMetadataItem("NBITS", szNBits,
6✔
3276
                                                  "IMAGE_STRUCTURE");
6✔
3277
        }
3278
    }
3279
}
277✔
3280

3281
/************************************************************************/
3282
/*                        GDALRegister_RMF()                            */
3283
/************************************************************************/
3284

3285
void GDALRegister_RMF()
1,911✔
3286

3287
{
3288
    if (GDALGetDriverByName("RMF") != nullptr)
1,911✔
3289
        return;
282✔
3290

3291
    GDALDriver *poDriver = new GDALDriver();
1,629✔
3292

3293
    poDriver->SetDescription("RMF");
1,629✔
3294
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1,629✔
3295
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Raster Matrix Format");
1,629✔
3296
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/rmf.html");
1,629✔
3297
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "rsw");
1,629✔
3298
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
1,629✔
3299
                              "Byte Int16 Int32 Float64");
1,629✔
3300
    poDriver->SetMetadataItem(
1,629✔
3301
        GDAL_DMD_CREATIONOPTIONLIST,
3302
        "<CreationOptionList>"
3303
        "   <Option name='MTW' type='boolean' description='Create MTW DEM "
3304
        "matrix'/>"
3305
        "   <Option name='BLOCKXSIZE' type='int' description='Tile Width'/>"
3306
        "   <Option name='BLOCKYSIZE' type='int' description='Tile Height'/>"
3307
        "   <Option name='RMFHUGE' type='string-select' description='Creation "
3308
        "of huge RMF file (Supported by GIS Panorama since v11)'>"
3309
        "     <Value>NO</Value>"
3310
        "     <Value>YES</Value>"
3311
        "     <Value>IF_SAFER</Value>"
3312
        "   </Option>"
3313
        "   <Option name='COMPRESS' type='string-select' default='NONE'>"
3314
        "     <Value>NONE</Value>"
3315
        "     <Value>LZW</Value>"
3316
        "     <Value>JPEG</Value>"
3317
        "     <Value>RMF_DEM</Value>"
3318
        "   </Option>"
3319
        "   <Option name='JPEG_QUALITY' type='int' description='JPEG quality "
3320
        "1-100' default='75'/>"
3321
        "   <Option name='NUM_THREADS' type='string' description='Number of "
3322
        "worker threads for compression. Can be set to ALL_CPUS' default='1'/>"
3323
        "</CreationOptionList>");
1,629✔
3324
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1,629✔
3325

3326
    poDriver->pfnIdentify = RMFDataset::Identify;
1,629✔
3327
    poDriver->pfnOpen = RMFDataset::Open;
1,629✔
3328
    poDriver->pfnCreate = RMFDataset::Create;
1,629✔
3329
    poDriver->SetMetadataItem(
1,629✔
3330
        GDAL_DMD_OPENOPTIONLIST,
3331
        "<OpenOptionList>"
3332
        "  <Option name='RMF_SET_VERTCS' type='string' description='Layers "
3333
        "spatial reference will include vertical coordinate system description "
3334
        "if exist' default='NO'/>"
3335
        "</OpenOptionList>");
1,629✔
3336

3337
    GetGDALDriverManager()->RegisterDriver(poDriver);
1,629✔
3338
}
3339

3340
/************************************************************************/
3341
/*                            RMFCompressData                           */
3342
/************************************************************************/
3343

3344
RMFCompressData::RMFCompressData() : pabyBuffers(nullptr)
55✔
3345
{
3346
}
55✔
3347

3348
RMFCompressData::~RMFCompressData()
55✔
3349
{
3350
    if (pabyBuffers != nullptr)
55✔
3351
    {
3352
        VSIFree(pabyBuffers);
55✔
3353
    }
3354

3355
    if (hWriteTileMutex != nullptr)
55✔
3356
    {
3357
        CPLDestroyMutex(hWriteTileMutex);
3✔
3358
    }
3359

3360
    if (hReadyJobMutex != nullptr)
55✔
3361
    {
3362
        CPLDestroyMutex(hReadyJobMutex);
3✔
3363
    }
3364
}
55✔
3365

3366
GDALSuggestedBlockAccessPattern
3367
RMFRasterBand::GetSuggestedBlockAccessPattern() const
21✔
3368
{
3369
    return GSBAP_RANDOM;
21✔
3370
}
3371

3372
CPLErr RMFDataset::SetMetadataItem(const char *pszName, const char *pszValue,
1,507✔
3373
                                   const char *pszDomain)
3374
{
3375
    if (GetAccess() == GA_Update)
1,507✔
3376
    {
3377
        CPLDebug("RMF", "SetMetadataItem: %s=%s", pszName, pszValue);
190✔
3378
        if (EQUAL(pszName, MD_NAME_KEY))
190✔
3379
        {
3380
            memcpy(sHeader.byName, pszValue,
20✔
3381
                   CPLStrnlen(pszValue, RMF_NAME_SIZE));
3382
            bHeaderDirty = true;
20✔
3383
        }
3384
        else if (EQUAL(pszName, MD_SCALE_KEY) && CPLStrnlen(pszValue, 10) > 4)
170✔
3385
        {
3386
            sHeader.dfScale = atof(pszValue + 4);
20✔
3387
            sHeader.dfResolution = sHeader.dfScale / sHeader.dfPixelSize;
20✔
3388
            bHeaderDirty = true;
20✔
3389
        }
3390
        else if (EQUAL(pszName, MD_FRAME_KEY))
150✔
3391
        {
3392
            bHeaderDirty = true;
×
3393
        }
3394
    }
3395
    return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1,507✔
3396
}
3397

3398
CPLErr RMFDataset::SetMetadata(char **papszMetadata, const char *pszDomain)
39✔
3399
{
3400
    if (GetAccess() == GA_Update)
39✔
3401
    {
3402
        auto pszName = CSLFetchNameValue(papszMetadata, MD_NAME_KEY);
39✔
3403
        if (pszName != nullptr)
39✔
3404
        {
3405
            memcpy(sHeader.byName, pszName, CPLStrnlen(pszName, RMF_NAME_SIZE));
21✔
3406
            bHeaderDirty = true;
21✔
3407

3408
            CPLDebug("RMF", "SetMetadata: %s", pszName);
21✔
3409
        }
3410
        auto pszScale = CSLFetchNameValue(papszMetadata, MD_SCALE_KEY);
39✔
3411
        if (pszScale != nullptr && CPLStrnlen(pszScale, 10) > 4)
39✔
3412
        {
3413
            sHeader.dfScale = atof(pszScale + 4);
21✔
3414
            sHeader.dfResolution = sHeader.dfScale / sHeader.dfPixelSize;
21✔
3415
            bHeaderDirty = true;
21✔
3416

3417
            CPLDebug("RMF", "SetMetadata: %s", pszScale);
21✔
3418
        }
3419
        auto pszFrame = CSLFetchNameValue(papszMetadata, MD_FRAME_KEY);
39✔
3420
        if (pszFrame != nullptr)
39✔
3421
        {
3422
            bHeaderDirty = true;
2✔
3423

3424
            CPLDebug("RMF", "SetMetadata: %s", pszFrame);
2✔
3425
        }
3426
    }
3427
    return GDALDataset::SetMetadata(papszMetadata, pszDomain);
39✔
3428
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc