Coverage Report

Created: 2026-08-14 09:29

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