Coverage Report

Created: 2025-06-09 08:44

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