Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: RIK Reader
4 : * Purpose: All code for RIK Reader
5 : * Author: Daniel Wallner, daniel.wallner@bredband.net
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2005, Daniel Wallner <daniel.wallner@bredband.net>
9 : * Copyright (c) 2008-2011, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include <cfloat>
15 : #include <zlib.h>
16 : #include "gdal_frmts.h"
17 : #include "gdal_pam.h"
18 :
19 : #include <cmath>
20 :
21 : #define RIK_HEADER_DEBUG 0
22 : #define RIK_CLEAR_DEBUG 0
23 : #define RIK_PIXEL_DEBUG 0
24 :
25 : // #define RIK_SINGLE_BLOCK 0
26 :
27 : #define RIK_ALLOW_BLOCK_ERRORS 1
28 :
29 : //
30 : // The RIK file format information was extracted from the trikpanel project:
31 : // http://sourceforge.net/projects/trikpanel/
32 : //
33 : // A RIK file consists of the following elements:
34 : //
35 : // +--------------------+
36 : // | Magic "RIK3" | (Only in RIK version 3)
37 : // +--------------------+
38 : // | Map name | (The first two bytes is the string length)
39 : // +--------------------+
40 : // | Header | (Three different formats exists)
41 : // +--------------------+
42 : // | Color palette |
43 : // +--------------------+
44 : // | Block offset array | (Only in compressed formats)
45 : // +--------------------+
46 : // | Image blocks |
47 : // +--------------------+
48 : //
49 : // All numbers are stored in little endian.
50 : //
51 : // There are four different image block formats:
52 : //
53 : // 1. Uncompressed image block
54 : //
55 : // A stream of palette indexes.
56 : //
57 : // 2. RLE image block
58 : //
59 : // The RLE image block is a stream of byte pairs:
60 : // | Run length - 1 (byte) | Pixel value (byte) | Run length - 1 ...
61 : //
62 : // 3. LZW image block
63 : //
64 : // The LZW image block uses the same LZW encoding as a GIF file
65 : // except that there is no EOF code and maximum code length is 13 bits.
66 : // These blocks are upside down compared to GDAL.
67 : //
68 : // 4. ZLIB image block
69 : //
70 : // These blocks are upside down compared to GDAL.
71 : //
72 :
73 : typedef struct
74 : {
75 : GUInt16 iUnknown;
76 : double fSouth; // Map bounds
77 : double fWest;
78 : double fNorth;
79 : double fEast;
80 : GUInt32 iScale; // Source map scale
81 : float iMPPNum; // Meters per pixel numerator
82 : GUInt32 iMPPDen; // Meters per pixel denominator
83 : // Only used if fSouth < 4000000
84 : GUInt32 iBlockWidth;
85 : GUInt32 iBlockHeight;
86 : GUInt32 iHorBlocks; // Number of horizontal blocks
87 : GUInt32 iVertBlocks; // Number of vertical blocks
88 : // Only used if fSouth >= 4000000
89 : GByte iBitsPerPixel;
90 : GByte iOptions;
91 : } RIKHeader;
92 :
93 : /************************************************************************/
94 : /* ==================================================================== */
95 : /* RIKDataset */
96 : /* ==================================================================== */
97 : /************************************************************************/
98 :
99 : class RIKRasterBand;
100 :
101 : class RIKDataset final : public GDALPamDataset
102 : {
103 : friend class RIKRasterBand;
104 :
105 : VSILFILE *fp;
106 :
107 : OGRSpatialReference m_oSRS{};
108 : GDALGeoTransform m_gt{};
109 :
110 : GUInt32 nBlockXSize;
111 : GUInt32 nBlockYSize;
112 : GUInt32 nHorBlocks;
113 : GUInt32 nVertBlocks;
114 : GUInt32 nFileSize;
115 : GUInt32 *pOffsets;
116 : GByte options;
117 :
118 : GDALColorTable *poColorTable;
119 :
120 : public:
121 : RIKDataset();
122 : ~RIKDataset();
123 :
124 : static GDALDataset *Open(GDALOpenInfo *);
125 : static int Identify(GDALOpenInfo *);
126 :
127 : CPLErr GetGeoTransform(GDALGeoTransform >) const override;
128 : const OGRSpatialReference *GetSpatialRef() const override;
129 : };
130 :
131 : /************************************************************************/
132 : /* ==================================================================== */
133 : /* RIKRasterBand */
134 : /* ==================================================================== */
135 : /************************************************************************/
136 :
137 : class RIKRasterBand final : public GDALPamRasterBand
138 : {
139 : friend class RIKDataset;
140 :
141 : public:
142 : RIKRasterBand(RIKDataset *, int);
143 :
144 : virtual CPLErr IReadBlock(int, int, void *) override;
145 : virtual GDALColorInterp GetColorInterpretation() override;
146 : virtual GDALColorTable *GetColorTable() override;
147 : };
148 :
149 : /************************************************************************/
150 : /* RIKRasterBand() */
151 : /************************************************************************/
152 :
153 0 : RIKRasterBand::RIKRasterBand(RIKDataset *poDSIn, int nBandIn)
154 :
155 : {
156 0 : poDS = poDSIn;
157 0 : nBand = nBandIn;
158 :
159 0 : eDataType = GDT_Byte;
160 :
161 0 : nBlockXSize = poDSIn->nBlockXSize;
162 0 : nBlockYSize = poDSIn->nBlockYSize;
163 0 : }
164 :
165 : /************************************************************************/
166 : /* GetNextLZWCode() */
167 : /************************************************************************/
168 :
169 0 : static int GetNextLZWCode(int codeBits, const GByte *blockData,
170 : const GUInt32 blockSize, GUInt32 &filePos,
171 : GUInt32 &fileAlign, int &bitsTaken)
172 :
173 : {
174 0 : if (filePos == fileAlign)
175 : {
176 0 : fileAlign += codeBits;
177 : }
178 :
179 0 : const int BitMask[] = {0x0000, 0x0001, 0x0003, 0x0007,
180 : 0x000f, 0x001f, 0x003f, 0x007f};
181 :
182 0 : int ret = 0;
183 0 : int bitsLeftToGo = codeBits;
184 :
185 0 : while (bitsLeftToGo > 0)
186 : {
187 0 : if (filePos >= blockSize)
188 0 : return -1;
189 :
190 0 : int tmp = blockData[filePos];
191 0 : tmp = tmp >> bitsTaken;
192 :
193 0 : if (bitsLeftToGo < 8)
194 0 : tmp &= BitMask[bitsLeftToGo];
195 :
196 0 : tmp = tmp << (codeBits - bitsLeftToGo);
197 :
198 0 : ret |= tmp;
199 :
200 0 : bitsLeftToGo -= (8 - bitsTaken);
201 0 : bitsTaken = 0;
202 :
203 0 : if (bitsLeftToGo < 0)
204 0 : bitsTaken = 8 + bitsLeftToGo;
205 :
206 0 : if (bitsTaken == 0)
207 0 : filePos++;
208 : }
209 :
210 : #if RIK_PIXEL_DEBUG
211 : printf("c%03X\n", ret); /*ok*/
212 : #endif
213 :
214 0 : return ret;
215 : }
216 :
217 : /************************************************************************/
218 : /* OutputPixel() */
219 : /************************************************************************/
220 :
221 0 : static void OutputPixel(GByte pixel, void *image, GUInt32 imageWidth,
222 : GUInt32 lineBreak, int &imageLine, GUInt32 &imagePos)
223 :
224 : {
225 0 : if (imagePos < imageWidth && imageLine >= 0)
226 0 : reinterpret_cast<GByte *>(image)[imagePos + imageLine * imageWidth] =
227 : pixel;
228 :
229 0 : imagePos++;
230 :
231 : #if RIK_PIXEL_DEBUG
232 : printf("_%02X %d\n", pixel, imagePos); /*ok*/
233 : #endif
234 :
235 : // Check if we need to change line
236 :
237 0 : if (imagePos == lineBreak)
238 : {
239 : #if RIK_PIXEL_DEBUG
240 : printf("\n%d\n", imageLine); /*ok*/
241 : #endif
242 :
243 0 : imagePos = 0;
244 :
245 0 : imageLine--;
246 : }
247 0 : }
248 :
249 : /************************************************************************/
250 : /* IReadBlock() */
251 : /************************************************************************/
252 :
253 0 : CPLErr RIKRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
254 :
255 : {
256 0 : RIKDataset *poRDS = cpl::down_cast<RIKDataset *>(poDS);
257 :
258 0 : const GUInt32 blocks = poRDS->nHorBlocks * poRDS->nVertBlocks;
259 0 : const GUInt32 nBlockIndex = nBlockXOff + nBlockYOff * poRDS->nHorBlocks;
260 0 : const GUInt32 nBlockOffset = poRDS->pOffsets[nBlockIndex];
261 :
262 0 : GUInt32 nBlockSize = poRDS->nFileSize;
263 0 : for (GUInt32 bi = nBlockIndex + 1; bi < blocks; bi++)
264 : {
265 0 : if (poRDS->pOffsets[bi])
266 : {
267 0 : nBlockSize = poRDS->pOffsets[bi];
268 0 : break;
269 : }
270 : }
271 0 : nBlockSize -= nBlockOffset;
272 :
273 0 : const GUInt32 pixels = poRDS->nBlockXSize * poRDS->nBlockYSize;
274 :
275 0 : if (!nBlockOffset || !nBlockSize
276 : #ifdef RIK_SINGLE_BLOCK
277 : || nBlockIndex != RIK_SINGLE_BLOCK
278 : #endif
279 : )
280 : {
281 0 : memset(pImage, 0, pixels);
282 0 : return CE_None;
283 : }
284 :
285 0 : VSIFSeekL(poRDS->fp, nBlockOffset, SEEK_SET);
286 :
287 : /* -------------------------------------------------------------------- */
288 : /* Read uncompressed block. */
289 : /* -------------------------------------------------------------------- */
290 :
291 0 : if (poRDS->options == 0x00 || poRDS->options == 0x40)
292 : {
293 0 : VSIFReadL(pImage, 1, pixels, poRDS->fp);
294 0 : return CE_None;
295 : }
296 :
297 : // Read block to memory
298 0 : GByte *blockData = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nBlockSize));
299 0 : if (blockData == nullptr)
300 0 : return CE_Failure;
301 0 : if (VSIFReadL(blockData, 1, nBlockSize, poRDS->fp) != nBlockSize)
302 : {
303 0 : VSIFree(blockData);
304 0 : return CE_Failure;
305 : }
306 0 : memset(pImage, 0, pixels);
307 :
308 : /* -------------------------------------------------------------------- */
309 : /* Read RLE block. */
310 : /* -------------------------------------------------------------------- */
311 0 : GUInt32 filePos = 0;
312 0 : GUInt32 imagePos = 0;
313 :
314 0 : if (poRDS->options == 0x01 || poRDS->options == 0x41)
315 : {
316 0 : while (filePos + 1 < nBlockSize && imagePos < pixels)
317 : {
318 0 : GByte count = blockData[filePos++];
319 0 : GByte color = blockData[filePos++];
320 :
321 0 : for (GByte i = 0; imagePos < pixels && i <= count; i++)
322 : {
323 0 : reinterpret_cast<GByte *>(pImage)[imagePos++] = color;
324 : }
325 0 : }
326 : }
327 :
328 : /* -------------------------------------------------------------------- */
329 : /* Read LZW block. */
330 : /* -------------------------------------------------------------------- */
331 :
332 0 : else if (poRDS->options == 0x0b)
333 : {
334 : try
335 : {
336 0 : if (nBlockSize < 5)
337 : {
338 0 : throw "Not enough bytes";
339 : }
340 :
341 0 : const bool LZW_HAS_CLEAR_CODE = !!(blockData[4] & 0x80);
342 0 : const int LZW_MAX_BITS = blockData[4] & 0x1f; // Max 13
343 0 : if (LZW_MAX_BITS > 13)
344 : {
345 0 : throw "Invalid LZW_MAX_BITS";
346 : }
347 0 : const int LZW_BITS_PER_PIXEL = 8;
348 0 : const int LZW_OFFSET = 5;
349 :
350 0 : const int LZW_CLEAR = 1 << LZW_BITS_PER_PIXEL;
351 0 : const int LZW_CODES = 1 << LZW_MAX_BITS;
352 0 : const int LZW_NO_SUCH_CODE = LZW_CODES + 1;
353 :
354 0 : int lastAdded = LZW_HAS_CLEAR_CODE ? LZW_CLEAR : LZW_CLEAR - 1;
355 0 : int codeBits = LZW_BITS_PER_PIXEL + 1;
356 :
357 : int code;
358 : int lastCode;
359 : GByte lastOutput;
360 0 : int bitsTaken = 0;
361 :
362 : int prefix[8192]; // only need LZW_CODES for size.
363 : GByte character[8192]; // only need LZW_CODES for size.
364 :
365 0 : for (int i = 0; i < LZW_CLEAR; i++)
366 0 : character[i] = static_cast<GByte>(i);
367 0 : for (int i = 0; i < LZW_CODES; i++)
368 0 : prefix[i] = LZW_NO_SUCH_CODE;
369 :
370 0 : filePos = LZW_OFFSET;
371 0 : GUInt32 fileAlign = LZW_OFFSET;
372 0 : int imageLine = poRDS->nBlockYSize - 1;
373 :
374 0 : GUInt32 lineBreak = poRDS->nBlockXSize;
375 :
376 : // 32 bit alignment
377 0 : lineBreak += 3;
378 0 : lineBreak &= 0xfffffffc;
379 :
380 0 : code = GetNextLZWCode(codeBits, blockData, nBlockSize, filePos,
381 : fileAlign, bitsTaken);
382 0 : if (code < 0)
383 : {
384 0 : throw "Not enough bytes";
385 : }
386 :
387 0 : OutputPixel(static_cast<GByte>(code), pImage, poRDS->nBlockXSize,
388 : lineBreak, imageLine, imagePos);
389 0 : lastOutput = static_cast<GByte>(code);
390 :
391 0 : while (imageLine >= 0 &&
392 0 : (imageLine || imagePos < poRDS->nBlockXSize) &&
393 0 : filePos < nBlockSize)
394 : {
395 0 : lastCode = code;
396 0 : code = GetNextLZWCode(codeBits, blockData, nBlockSize, filePos,
397 : fileAlign, bitsTaken);
398 0 : if (code < 0)
399 : {
400 0 : throw "Not enough bytes";
401 : }
402 :
403 0 : if (LZW_HAS_CLEAR_CODE && code == LZW_CLEAR)
404 : {
405 : #if RIK_CLEAR_DEBUG
406 : CPLDebug("RIK",
407 : "Clearing block %d\n"
408 : " x=%d y=%d\n"
409 : " pos=%d size=%d\n",
410 : nBlockIndex, imagePos, imageLine, filePos,
411 : nBlockSize);
412 : #endif
413 :
414 : // Clear prefix table
415 0 : for (int i = LZW_CLEAR; i < LZW_CODES; i++)
416 0 : prefix[i] = LZW_NO_SUCH_CODE;
417 0 : lastAdded = LZW_CLEAR;
418 0 : codeBits = LZW_BITS_PER_PIXEL + 1;
419 :
420 0 : filePos = fileAlign;
421 0 : bitsTaken = 0;
422 :
423 0 : code = GetNextLZWCode(codeBits, blockData, nBlockSize,
424 : filePos, fileAlign, bitsTaken);
425 0 : if (code < 0)
426 : {
427 0 : throw "Not enough bytes";
428 : }
429 :
430 0 : if (code > lastAdded)
431 : {
432 0 : throw "Clear Error";
433 : }
434 :
435 0 : OutputPixel((GByte)code, pImage, poRDS->nBlockXSize,
436 : lineBreak, imageLine, imagePos);
437 0 : lastOutput = (GByte)code;
438 : }
439 : else
440 : {
441 : // Set-up decoding
442 :
443 : GByte stack[8192]; // only need LZW_CODES for size.
444 :
445 0 : int stackPtr = 0;
446 0 : int decodeCode = code;
447 :
448 0 : if (code == lastAdded + 1)
449 : {
450 : // Handle special case
451 0 : *stack = lastOutput;
452 0 : stackPtr = 1;
453 0 : decodeCode = lastCode;
454 : }
455 0 : else if (code > lastAdded + 1)
456 : {
457 0 : throw "Too high code";
458 : }
459 :
460 : // Decode
461 :
462 0 : int i = 0;
463 0 : while (++i < LZW_CODES && decodeCode >= LZW_CLEAR &&
464 : decodeCode < LZW_NO_SUCH_CODE)
465 : {
466 0 : stack[stackPtr++] = character[decodeCode];
467 0 : decodeCode = prefix[decodeCode];
468 : }
469 0 : stack[stackPtr++] = static_cast<GByte>(decodeCode);
470 :
471 0 : if (i == LZW_CODES || decodeCode >= LZW_NO_SUCH_CODE)
472 : {
473 0 : throw "Decode error";
474 : }
475 :
476 : // Output stack
477 :
478 0 : lastOutput = stack[stackPtr - 1];
479 :
480 0 : while (stackPtr != 0 && imagePos < pixels)
481 : {
482 0 : OutputPixel(stack[--stackPtr], pImage,
483 : poRDS->nBlockXSize, lineBreak, imageLine,
484 : imagePos);
485 : }
486 :
487 : // Add code to string table
488 :
489 0 : if (lastCode != LZW_NO_SUCH_CODE &&
490 0 : lastAdded != LZW_CODES - 1)
491 : {
492 0 : ++lastAdded;
493 0 : if (lastAdded >= 8192)
494 : {
495 0 : throw "Decode error";
496 : }
497 0 : prefix[lastAdded] = lastCode;
498 0 : character[lastAdded] = lastOutput;
499 : }
500 :
501 : // Check if we need to use more bits
502 :
503 0 : if (lastAdded == (1 << codeBits) - 1 &&
504 : codeBits != LZW_MAX_BITS)
505 : {
506 0 : codeBits++;
507 :
508 0 : filePos = fileAlign;
509 0 : bitsTaken = 0;
510 : }
511 : }
512 : }
513 : }
514 0 : catch (const char *errStr)
515 : {
516 : #if RIK_ALLOW_BLOCK_ERRORS
517 0 : CPLDebug("RIK",
518 : "LZW Decompress Failed: %s\n"
519 : " blocks: %d\n"
520 : " blockindex: %d\n"
521 : " blockoffset: %X\n"
522 : " blocksize: %d\n",
523 : errStr, blocks, nBlockIndex, nBlockOffset, nBlockSize);
524 : #else
525 : CPLFree(blockData);
526 : CPLError(CE_Failure, CPLE_AppDefined,
527 : "RIK decompression failed: %s", errStr);
528 : return CE_Failure;
529 : #endif
530 : }
531 : }
532 :
533 : /* -------------------------------------------------------------------- */
534 : /* Read ZLIB block. */
535 : /* -------------------------------------------------------------------- */
536 :
537 0 : else if (poRDS->options == 0x0d)
538 : {
539 0 : uLong destLen = pixels;
540 0 : Byte *upsideDown = static_cast<Byte *>(CPLMalloc(pixels));
541 :
542 0 : if (uncompress(upsideDown, &destLen, blockData, nBlockSize) != Z_OK)
543 : {
544 0 : CPLDebug("RIK", "Deflate compression failed on block %u",
545 : nBlockIndex);
546 : }
547 :
548 0 : for (GUInt32 i = 0; i < poRDS->nBlockYSize; i++)
549 : {
550 0 : memcpy(reinterpret_cast<Byte *>(pImage) + poRDS->nBlockXSize * i,
551 0 : upsideDown +
552 0 : poRDS->nBlockXSize * (poRDS->nBlockYSize - i - 1),
553 0 : poRDS->nBlockXSize);
554 : }
555 :
556 0 : CPLFree(upsideDown);
557 : }
558 :
559 0 : CPLFree(blockData);
560 :
561 0 : return CE_None;
562 : }
563 :
564 : /************************************************************************/
565 : /* GetColorInterpretation() */
566 : /************************************************************************/
567 :
568 0 : GDALColorInterp RIKRasterBand::GetColorInterpretation()
569 :
570 : {
571 0 : return GCI_PaletteIndex;
572 : }
573 :
574 : /************************************************************************/
575 : /* GetColorTable() */
576 : /************************************************************************/
577 :
578 0 : GDALColorTable *RIKRasterBand::GetColorTable()
579 :
580 : {
581 0 : RIKDataset *poRDS = cpl::down_cast<RIKDataset *>(poDS);
582 :
583 0 : return poRDS->poColorTable;
584 : }
585 :
586 : /************************************************************************/
587 : /* ==================================================================== */
588 : /* RIKDataset */
589 : /* ==================================================================== */
590 : /************************************************************************/
591 :
592 : /************************************************************************/
593 : /* RIKDataset() */
594 : /************************************************************************/
595 :
596 0 : RIKDataset::RIKDataset()
597 : : fp(nullptr), nBlockXSize(0), nBlockYSize(0), nHorBlocks(0),
598 : nVertBlocks(0), nFileSize(0), pOffsets(nullptr), options(0),
599 0 : poColorTable(nullptr)
600 :
601 : {
602 0 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
603 0 : m_oSRS.importFromWkt(
604 : "PROJCS[\"RT90 2.5 gon "
605 : "V\",GEOGCS[\"RT90\",DATUM[\"Rikets_koordinatsystem_1990\",SPHEROID["
606 : "\"Bessel "
607 : "1841\",6377397.155,299.1528128,AUTHORITY[\"EPSG\",\"7004\"]],TOWGS84["
608 : "414.1055246174,41.3265500042,603.0582474221,-0.8551163377,2."
609 : "1413174055,-7.0227298286,0],AUTHORITY[\"EPSG\",\"6124\"]],PRIMEM["
610 : "\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0."
611 : "0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\","
612 : "\"4124\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_"
613 : "of_origin\",0],PARAMETER[\"central_meridian\",15.80827777777778],"
614 : "PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",1500000],"
615 : "PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\","
616 : "\"9001\"]],AUTHORITY[\"EPSG\",\"3021\"]]");
617 0 : }
618 :
619 : /************************************************************************/
620 : /* ~RIKDataset() */
621 : /************************************************************************/
622 :
623 0 : RIKDataset::~RIKDataset()
624 :
625 : {
626 0 : FlushCache(true);
627 0 : CPLFree(pOffsets);
628 0 : if (fp != nullptr)
629 0 : VSIFCloseL(fp);
630 0 : delete poColorTable;
631 0 : }
632 :
633 : /************************************************************************/
634 : /* GetGeoTransform() */
635 : /************************************************************************/
636 :
637 0 : CPLErr RIKDataset::GetGeoTransform(GDALGeoTransform >) const
638 :
639 : {
640 0 : gt = m_gt;
641 0 : return CE_None;
642 : }
643 :
644 : /************************************************************************/
645 : /* GetSpatialRef() */
646 : /************************************************************************/
647 :
648 0 : const OGRSpatialReference *RIKDataset::GetSpatialRef() const
649 :
650 : {
651 0 : return &m_oSRS;
652 : }
653 :
654 : /************************************************************************/
655 : /* GetRikString() */
656 : /************************************************************************/
657 :
658 785 : static GUInt16 GetRikString(VSILFILE *fp, char *str, GUInt16 strLength)
659 :
660 : {
661 : GUInt16 actLength;
662 :
663 785 : VSIFReadL(&actLength, 1, sizeof(actLength), fp);
664 : #ifdef CPL_MSB
665 : CPL_SWAP16PTR(&actLength);
666 : #endif
667 :
668 785 : if (actLength + 2 > strLength)
669 : {
670 0 : return actLength;
671 : }
672 :
673 785 : VSIFReadL(str, 1, actLength, fp);
674 :
675 785 : str[actLength] = '\0';
676 :
677 785 : return actLength;
678 : }
679 :
680 : /************************************************************************/
681 : /* Identify() */
682 : /************************************************************************/
683 :
684 58769 : int RIKDataset::Identify(GDALOpenInfo *poOpenInfo)
685 :
686 : {
687 58769 : if (poOpenInfo->fpL == nullptr || poOpenInfo->nHeaderBytes < 50)
688 54592 : return FALSE;
689 :
690 4177 : if (STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "RIK3"))
691 : {
692 0 : return TRUE;
693 : }
694 : else
695 : {
696 : GUInt16 actLength;
697 4177 : memcpy(&actLength, poOpenInfo->pabyHeader, 2);
698 : #ifdef CPL_MSB
699 : CPL_SWAP16PTR(&actLength);
700 : #endif
701 4177 : if (actLength + 2 > 1024)
702 : {
703 2495 : return FALSE;
704 : }
705 1682 : if (actLength == 0)
706 1619 : return -1;
707 :
708 2544 : for (int i = 0; i < actLength; i++)
709 : {
710 2538 : if (poOpenInfo->pabyHeader[2 + i] == 0)
711 57 : return FALSE;
712 : }
713 :
714 6 : if (poOpenInfo->IsExtensionEqualToCI("rik"))
715 0 : return TRUE;
716 :
717 : // We really need Open to be able to conclude
718 6 : return -1;
719 : }
720 : }
721 :
722 : /************************************************************************/
723 : /* Open() */
724 : /************************************************************************/
725 :
726 785 : GDALDataset *RIKDataset::Open(GDALOpenInfo *poOpenInfo)
727 :
728 : {
729 785 : if (Identify(poOpenInfo) == FALSE)
730 0 : return nullptr;
731 :
732 785 : bool rik3header = false;
733 :
734 785 : if (STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "RIK3"))
735 : {
736 0 : rik3header = true;
737 0 : VSIFSeekL(poOpenInfo->fpL, 4, SEEK_SET);
738 : }
739 : else
740 785 : VSIFSeekL(poOpenInfo->fpL, 0, SEEK_SET);
741 :
742 : /* -------------------------------------------------------------------- */
743 : /* Read the map name. */
744 : /* -------------------------------------------------------------------- */
745 :
746 : char name[1024];
747 :
748 785 : GUInt16 nameLength = GetRikString(poOpenInfo->fpL, name, sizeof(name));
749 :
750 785 : if (nameLength > sizeof(name) - 1)
751 : {
752 0 : return nullptr;
753 : }
754 :
755 785 : if (!rik3header)
756 : {
757 785 : if (nameLength == 0 || nameLength != strlen(name))
758 782 : return nullptr;
759 : }
760 :
761 : /* -------------------------------------------------------------------- */
762 : /* Read the header. */
763 : /* -------------------------------------------------------------------- */
764 :
765 : RIKHeader header;
766 : double metersPerPixel;
767 :
768 3 : const char *headerType = "RIK3";
769 :
770 3 : if (rik3header)
771 : {
772 : /* --------------------------------------------------------------------
773 : */
774 : /* RIK3 header. */
775 : /* --------------------------------------------------------------------
776 : */
777 :
778 : // Read projection name
779 :
780 : char projection[1024];
781 :
782 : GUInt16 projLength =
783 0 : GetRikString(poOpenInfo->fpL, projection, sizeof(projection));
784 :
785 0 : if (projLength > sizeof(projection) - 1)
786 : {
787 : // Unreasonable string length, assume wrong format
788 0 : return nullptr;
789 : }
790 :
791 : // Read unknown string
792 :
793 0 : /*projLength =*/GetRikString(poOpenInfo->fpL, projection,
794 : sizeof(projection));
795 :
796 : // Read map north edge
797 :
798 : char tmpStr[16];
799 :
800 : GUInt16 tmpLength =
801 0 : GetRikString(poOpenInfo->fpL, tmpStr, sizeof(tmpStr));
802 :
803 0 : if (tmpLength > sizeof(tmpStr) - 1)
804 : {
805 : // Unreasonable string length, assume wrong format
806 0 : return nullptr;
807 : }
808 :
809 0 : header.fNorth = CPLAtof(tmpStr);
810 :
811 : // Read map west edge
812 :
813 0 : tmpLength = GetRikString(poOpenInfo->fpL, tmpStr, sizeof(tmpStr));
814 :
815 0 : if (tmpLength > sizeof(tmpStr) - 1)
816 : {
817 : // Unreasonable string length, assume wrong format
818 0 : return nullptr;
819 : }
820 :
821 0 : header.fWest = CPLAtof(tmpStr);
822 :
823 : // Read binary values
824 :
825 0 : VSIFReadL(&header.iScale, 1, sizeof(header.iScale), poOpenInfo->fpL);
826 0 : VSIFReadL(&header.iMPPNum, 1, sizeof(header.iMPPNum), poOpenInfo->fpL);
827 0 : VSIFReadL(&header.iBlockWidth, 1, sizeof(header.iBlockWidth),
828 : poOpenInfo->fpL);
829 0 : VSIFReadL(&header.iBlockHeight, 1, sizeof(header.iBlockHeight),
830 : poOpenInfo->fpL);
831 0 : VSIFReadL(&header.iHorBlocks, 1, sizeof(header.iHorBlocks),
832 : poOpenInfo->fpL);
833 0 : VSIFReadL(&header.iVertBlocks, 1, sizeof(header.iVertBlocks),
834 : poOpenInfo->fpL);
835 : #ifdef CPL_MSB
836 : CPL_SWAP32PTR(&header.iScale);
837 : CPL_SWAP32PTR(&header.iMPPNum);
838 : CPL_SWAP32PTR(&header.iBlockWidth);
839 : CPL_SWAP32PTR(&header.iBlockHeight);
840 : CPL_SWAP32PTR(&header.iHorBlocks);
841 : CPL_SWAP32PTR(&header.iVertBlocks);
842 : #endif
843 0 : if (header.iMPPNum == 0)
844 0 : return nullptr;
845 :
846 0 : VSIFReadL(&header.iBitsPerPixel, 1, sizeof(header.iBitsPerPixel),
847 : poOpenInfo->fpL);
848 0 : VSIFReadL(&header.iOptions, 1, sizeof(header.iOptions),
849 : poOpenInfo->fpL);
850 0 : header.iUnknown = header.iOptions;
851 0 : VSIFReadL(&header.iOptions, 1, sizeof(header.iOptions),
852 : poOpenInfo->fpL);
853 :
854 0 : header.fSouth =
855 0 : header.fNorth - static_cast<double>(header.iVertBlocks) *
856 0 : header.iBlockHeight * header.iMPPNum;
857 0 : header.fEast = header.fWest + static_cast<double>(header.iHorBlocks) *
858 0 : header.iBlockWidth * header.iMPPNum;
859 :
860 0 : metersPerPixel = header.iMPPNum;
861 : }
862 : else
863 : {
864 : /* --------------------------------------------------------------------
865 : */
866 : /* Old RIK header. */
867 : /* --------------------------------------------------------------------
868 : */
869 :
870 3 : VSIFReadL(&header.iUnknown, 1, sizeof(header.iUnknown),
871 : poOpenInfo->fpL);
872 3 : VSIFReadL(&header.fSouth, 1, sizeof(header.fSouth), poOpenInfo->fpL);
873 3 : VSIFReadL(&header.fWest, 1, sizeof(header.fWest), poOpenInfo->fpL);
874 3 : VSIFReadL(&header.fNorth, 1, sizeof(header.fNorth), poOpenInfo->fpL);
875 3 : VSIFReadL(&header.fEast, 1, sizeof(header.fEast), poOpenInfo->fpL);
876 3 : VSIFReadL(&header.iScale, 1, sizeof(header.iScale), poOpenInfo->fpL);
877 3 : VSIFReadL(&header.iMPPNum, 1, sizeof(header.iMPPNum), poOpenInfo->fpL);
878 : #ifdef CPL_MSB
879 : CPL_SWAP64PTR(&header.fSouth);
880 : CPL_SWAP64PTR(&header.fWest);
881 : CPL_SWAP64PTR(&header.fNorth);
882 : CPL_SWAP64PTR(&header.fEast);
883 : CPL_SWAP32PTR(&header.iScale);
884 : CPL_SWAP32PTR(&header.iMPPNum);
885 : #endif
886 :
887 6 : if (!std::isfinite(header.fSouth) || !std::isfinite(header.fWest) ||
888 9 : !std::isfinite(header.fNorth) || !std::isfinite(header.fEast) ||
889 3 : header.iMPPNum == 0)
890 : {
891 0 : return nullptr;
892 : }
893 :
894 3 : const bool offsetBounds = header.fSouth < 4000000;
895 :
896 3 : header.iMPPDen = 1;
897 :
898 3 : if (offsetBounds)
899 : {
900 3 : header.fSouth += 4002995;
901 3 : header.fNorth += 5004000;
902 3 : header.fWest += 201000;
903 3 : header.fEast += 302005;
904 :
905 3 : VSIFReadL(&header.iMPPDen, 1, sizeof(header.iMPPDen),
906 : poOpenInfo->fpL);
907 : #ifdef CPL_MSB
908 : CPL_SWAP32PTR(&header.iMPPDen);
909 : #endif
910 3 : if (header.iMPPDen == 0)
911 0 : return nullptr;
912 :
913 3 : headerType = "RIK1";
914 : }
915 : else
916 : {
917 0 : headerType = "RIK2";
918 : }
919 :
920 3 : metersPerPixel = header.iMPPNum / static_cast<double>(header.iMPPDen);
921 :
922 3 : VSIFReadL(&header.iBlockWidth, 1, sizeof(header.iBlockWidth),
923 : poOpenInfo->fpL);
924 3 : VSIFReadL(&header.iBlockHeight, 1, sizeof(header.iBlockHeight),
925 : poOpenInfo->fpL);
926 3 : VSIFReadL(&header.iHorBlocks, 1, sizeof(header.iHorBlocks),
927 : poOpenInfo->fpL);
928 : #ifdef CPL_MSB
929 : CPL_SWAP32PTR(&header.iBlockWidth);
930 : CPL_SWAP32PTR(&header.iBlockHeight);
931 : CPL_SWAP32PTR(&header.iHorBlocks);
932 : #endif
933 :
934 3 : if ((header.iBlockWidth > 2000) || (header.iBlockWidth < 10) ||
935 0 : (header.iBlockHeight > 2000) || (header.iBlockHeight < 10))
936 3 : return nullptr;
937 :
938 0 : if (!offsetBounds)
939 : {
940 0 : VSIFReadL(&header.iVertBlocks, 1, sizeof(header.iVertBlocks),
941 : poOpenInfo->fpL);
942 : #ifdef CPL_MSB
943 : CPL_SWAP32PTR(&header.iVertBlocks);
944 : #endif
945 : }
946 :
947 0 : if (offsetBounds || !header.iVertBlocks)
948 : {
949 0 : double dfVertBlocks = ceil((header.fNorth - header.fSouth) /
950 0 : (header.iBlockHeight * metersPerPixel));
951 0 : if (dfVertBlocks < 1 || dfVertBlocks > INT_MAX)
952 0 : return nullptr;
953 0 : header.iVertBlocks = static_cast<GUInt32>(dfVertBlocks);
954 : }
955 :
956 : #if RIK_HEADER_DEBUG
957 : CPLDebug("RIK", "Original vertical blocks %d\n", header.iVertBlocks);
958 : #endif
959 :
960 0 : VSIFReadL(&header.iBitsPerPixel, 1, sizeof(header.iBitsPerPixel),
961 : poOpenInfo->fpL);
962 :
963 0 : if (header.iBitsPerPixel != 8)
964 : {
965 0 : CPLError(CE_Failure, CPLE_OpenFailed,
966 : "File %s has unsupported number of bits per pixel.\n",
967 : poOpenInfo->pszFilename);
968 0 : return nullptr;
969 : }
970 :
971 0 : VSIFReadL(&header.iOptions, 1, sizeof(header.iOptions),
972 : poOpenInfo->fpL);
973 :
974 0 : if (header.iOptions != 0x00 && // Uncompressed
975 0 : header.iOptions != 0x40 && // Uncompressed
976 0 : header.iOptions != 0x01 && // RLE
977 0 : header.iOptions != 0x41 && // RLE
978 0 : header.iOptions != 0x0B && // LZW
979 0 : header.iOptions != 0x0D) // ZLIB
980 : {
981 0 : CPLError(CE_Failure, CPLE_OpenFailed,
982 : "File %s. Unknown map options.\n",
983 : poOpenInfo->pszFilename);
984 0 : return nullptr;
985 : }
986 : }
987 :
988 0 : if (header.iBlockWidth == 0 || header.iHorBlocks == 0 ||
989 0 : header.iBlockWidth >= INT_MAX / header.iHorBlocks ||
990 0 : header.iBlockHeight == 0 || header.iVertBlocks == 0 ||
991 0 : header.iBlockHeight >= INT_MAX / header.iVertBlocks ||
992 0 : header.iBlockHeight >= INT_MAX / header.iBlockWidth ||
993 0 : header.iVertBlocks >= INT_MAX / (int)sizeof(GUInt32) ||
994 0 : header.iHorBlocks >=
995 0 : INT_MAX / (header.iVertBlocks * (int)sizeof(GUInt32)))
996 : {
997 0 : return nullptr;
998 : }
999 :
1000 : /* -------------------------------------------------------------------- */
1001 : /* Read the palette. */
1002 : /* -------------------------------------------------------------------- */
1003 :
1004 : GByte palette[768];
1005 :
1006 0 : for (GUInt16 i = 0; i < 256; i++)
1007 : {
1008 0 : VSIFReadL(&palette[i * 3 + 2], 1, 1, poOpenInfo->fpL);
1009 0 : VSIFReadL(&palette[i * 3 + 1], 1, 1, poOpenInfo->fpL);
1010 0 : VSIFReadL(&palette[i * 3 + 0], 1, 1, poOpenInfo->fpL);
1011 : }
1012 :
1013 : /* -------------------------------------------------------------------- */
1014 : /* Find block offsets. */
1015 : /* -------------------------------------------------------------------- */
1016 :
1017 0 : GUInt32 blocks = header.iHorBlocks * header.iVertBlocks;
1018 : GUInt32 *offsets =
1019 0 : reinterpret_cast<GUInt32 *>(VSIMalloc(blocks * sizeof(GUInt32)));
1020 :
1021 0 : if (!offsets)
1022 : {
1023 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1024 : "File %s. Unable to allocate offset table.\n",
1025 : poOpenInfo->pszFilename);
1026 0 : return nullptr;
1027 : }
1028 :
1029 0 : if (header.iOptions == 0x00)
1030 : {
1031 0 : offsets[0] = static_cast<GUInt32>(VSIFTellL(poOpenInfo->fpL));
1032 :
1033 0 : if (VSIFEofL(poOpenInfo->fpL))
1034 : {
1035 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1036 : "File %s. Read past end of file.\n",
1037 : poOpenInfo->pszFilename);
1038 0 : CPLFree(offsets);
1039 0 : return nullptr;
1040 : }
1041 :
1042 0 : VSIFSeekL(poOpenInfo->fpL, 0, SEEK_END);
1043 0 : vsi_l_offset nBigFileSize = VSIFTellL(poOpenInfo->fpL);
1044 0 : if (nBigFileSize > UINT_MAX)
1045 0 : nBigFileSize = UINT_MAX;
1046 0 : GUInt32 fileSize = static_cast<GUInt32>(nBigFileSize);
1047 :
1048 0 : GUInt32 nBlocksFromFileSize =
1049 0 : (fileSize - offsets[0]) /
1050 0 : (header.iBlockWidth * header.iBlockHeight);
1051 0 : if (nBlocksFromFileSize < blocks)
1052 : {
1053 0 : blocks = nBlocksFromFileSize;
1054 0 : header.iVertBlocks = blocks / header.iHorBlocks;
1055 : }
1056 :
1057 0 : if (header.iVertBlocks == 0)
1058 : {
1059 0 : CPLError(CE_Failure, CPLE_OpenFailed, "File %s too short.\n",
1060 : poOpenInfo->pszFilename);
1061 0 : CPLFree(offsets);
1062 0 : return nullptr;
1063 : }
1064 :
1065 0 : for (GUInt32 i = 1; i < blocks; i++)
1066 : {
1067 0 : offsets[i] =
1068 0 : offsets[i - 1] + header.iBlockWidth * header.iBlockHeight;
1069 : }
1070 : }
1071 : else
1072 : {
1073 0 : for (GUInt32 i = 0; i < blocks; i++)
1074 : {
1075 0 : if (VSIFReadL(&offsets[i], sizeof(offsets[i]), 1,
1076 0 : poOpenInfo->fpL) != 1)
1077 0 : break;
1078 : #ifdef CPL_MSB
1079 : CPL_SWAP32PTR(&offsets[i]);
1080 : #endif
1081 0 : if (rik3header)
1082 : {
1083 : GUInt32 blockSize;
1084 0 : if (VSIFReadL(&blockSize, sizeof(blockSize), 1,
1085 0 : poOpenInfo->fpL) != 1)
1086 0 : break;
1087 : #ifdef CPL_MSB
1088 : CPL_SWAP32PTR(&blockSize);
1089 : #endif
1090 : }
1091 : }
1092 : }
1093 :
1094 : /* -------------------------------------------------------------------- */
1095 : /* Final checks. */
1096 : /* -------------------------------------------------------------------- */
1097 :
1098 : // File size
1099 :
1100 0 : if (VSIFEofL(poOpenInfo->fpL))
1101 : {
1102 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1103 : "File %s. Read past end of file.\n", poOpenInfo->pszFilename);
1104 0 : CPLFree(offsets);
1105 0 : return nullptr;
1106 : }
1107 :
1108 0 : VSIFSeekL(poOpenInfo->fpL, 0, SEEK_END);
1109 0 : GUInt32 fileSize = static_cast<GUInt32>(VSIFTellL(poOpenInfo->fpL));
1110 :
1111 : #if RIK_HEADER_DEBUG
1112 : CPLDebug("RIK", "File size %d\n", fileSize);
1113 : #endif
1114 :
1115 : // Make sure the offset table is valid
1116 :
1117 0 : GUInt32 lastoffset = 0;
1118 :
1119 0 : for (GUInt32 y = 0; y < header.iVertBlocks; y++)
1120 : {
1121 0 : for (GUInt32 x = 0; x < header.iHorBlocks; x++)
1122 : {
1123 0 : if (!offsets[x + y * header.iHorBlocks])
1124 : {
1125 0 : continue;
1126 : }
1127 :
1128 0 : if (offsets[x + y * header.iHorBlocks] >= fileSize)
1129 : {
1130 0 : if (!y)
1131 : {
1132 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1133 : "File %s too short.\n", poOpenInfo->pszFilename);
1134 0 : CPLFree(offsets);
1135 0 : return nullptr;
1136 : }
1137 0 : header.iVertBlocks = y;
1138 0 : break;
1139 : }
1140 :
1141 0 : if (offsets[x + y * header.iHorBlocks] < lastoffset)
1142 : {
1143 0 : if (!y)
1144 : {
1145 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1146 : "File %s. Corrupt offset table.\n",
1147 : poOpenInfo->pszFilename);
1148 0 : CPLFree(offsets);
1149 0 : return nullptr;
1150 : }
1151 0 : header.iVertBlocks = y;
1152 0 : break;
1153 : }
1154 :
1155 0 : lastoffset = offsets[x + y * header.iHorBlocks];
1156 : }
1157 : }
1158 :
1159 : #if RIK_HEADER_DEBUG
1160 : CPLDebug("RIK",
1161 : "first offset %d\n"
1162 : "last offset %d\n",
1163 : offsets[0], lastoffset);
1164 : #endif
1165 :
1166 0 : const char *compression = "RLE";
1167 :
1168 0 : if (header.iOptions == 0x00 || header.iOptions == 0x40)
1169 0 : compression = "Uncompressed";
1170 0 : if (header.iOptions == 0x0b)
1171 0 : compression = "LZW";
1172 0 : if (header.iOptions == 0x0d)
1173 0 : compression = "ZLIB";
1174 :
1175 0 : CPLDebug("RIK",
1176 : "RIK file parameters:\n"
1177 : " name: %s\n"
1178 : " header: %s\n"
1179 : " unknown: 0x%X\n"
1180 : " south: %f\n"
1181 : " west: %f\n"
1182 : " north: %f\n"
1183 : " east: %f\n"
1184 : " original scale: %d\n"
1185 : " meters per pixel: %f\n"
1186 : " block width: %d\n"
1187 : " block height: %d\n"
1188 : " horizontal blocks: %d\n"
1189 : " vertical blocks: %d\n"
1190 : " bits per pixel: %d\n"
1191 : " options: 0x%X\n"
1192 : " compression: %s\n",
1193 0 : name, headerType, header.iUnknown, header.fSouth, header.fWest,
1194 : header.fNorth, header.fEast, header.iScale, metersPerPixel,
1195 : header.iBlockWidth, header.iBlockHeight, header.iHorBlocks,
1196 0 : header.iVertBlocks, header.iBitsPerPixel, header.iOptions,
1197 : compression);
1198 :
1199 : /* -------------------------------------------------------------------- */
1200 : /* Create a corresponding GDALDataset. */
1201 : /* -------------------------------------------------------------------- */
1202 :
1203 0 : RIKDataset *poDS = new RIKDataset();
1204 :
1205 0 : poDS->fp = poOpenInfo->fpL;
1206 0 : poOpenInfo->fpL = nullptr;
1207 :
1208 0 : poDS->m_gt[0] = header.fWest - metersPerPixel / 2.0;
1209 0 : poDS->m_gt[1] = metersPerPixel;
1210 0 : poDS->m_gt[2] = 0.0;
1211 0 : poDS->m_gt[3] = header.fNorth + metersPerPixel / 2.0;
1212 0 : poDS->m_gt[4] = 0.0;
1213 0 : poDS->m_gt[5] = -metersPerPixel;
1214 :
1215 0 : poDS->nBlockXSize = header.iBlockWidth;
1216 0 : poDS->nBlockYSize = header.iBlockHeight;
1217 0 : poDS->nHorBlocks = header.iHorBlocks;
1218 0 : poDS->nVertBlocks = header.iVertBlocks;
1219 0 : poDS->pOffsets = offsets;
1220 0 : poDS->options = header.iOptions;
1221 0 : poDS->nFileSize = fileSize;
1222 :
1223 0 : poDS->nRasterXSize = header.iBlockWidth * header.iHorBlocks;
1224 0 : poDS->nRasterYSize = header.iBlockHeight * header.iVertBlocks;
1225 :
1226 0 : poDS->nBands = 1;
1227 :
1228 : GDALColorEntry oEntry;
1229 0 : poDS->poColorTable = new GDALColorTable();
1230 0 : for (GUInt16 i = 0; i < 256; i++)
1231 : {
1232 0 : oEntry.c1 = palette[i * 3 + 2]; // Red
1233 0 : oEntry.c2 = palette[i * 3 + 1]; // Green
1234 0 : oEntry.c3 = palette[i * 3]; // Blue
1235 0 : oEntry.c4 = 255;
1236 :
1237 0 : poDS->poColorTable->SetColorEntry(i, &oEntry);
1238 : }
1239 :
1240 : /* -------------------------------------------------------------------- */
1241 : /* Create band information objects. */
1242 : /* -------------------------------------------------------------------- */
1243 :
1244 0 : poDS->SetBand(1, new RIKRasterBand(poDS, 1));
1245 :
1246 : /* -------------------------------------------------------------------- */
1247 : /* Initialize any PAM information. */
1248 : /* -------------------------------------------------------------------- */
1249 :
1250 0 : poDS->SetDescription(poOpenInfo->pszFilename);
1251 0 : poDS->TryLoadXML();
1252 :
1253 : /* -------------------------------------------------------------------- */
1254 : /* Check for external overviews. */
1255 : /* -------------------------------------------------------------------- */
1256 0 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
1257 0 : poOpenInfo->GetSiblingFiles());
1258 :
1259 : /* -------------------------------------------------------------------- */
1260 : /* Confirm the requested access is supported. */
1261 : /* -------------------------------------------------------------------- */
1262 0 : if (poOpenInfo->eAccess == GA_Update)
1263 : {
1264 0 : delete poDS;
1265 0 : ReportUpdateNotSupportedByDriver("RIK");
1266 0 : return nullptr;
1267 : }
1268 :
1269 0 : return poDS;
1270 : }
1271 :
1272 : /************************************************************************/
1273 : /* GDALRegister_RIK() */
1274 : /************************************************************************/
1275 :
1276 1961 : void GDALRegister_RIK()
1277 :
1278 : {
1279 1961 : if (GDALGetDriverByName("RIK") != nullptr)
1280 283 : return;
1281 :
1282 1678 : GDALDriver *poDriver = new GDALDriver();
1283 :
1284 1678 : poDriver->SetDescription("RIK");
1285 1678 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1286 1678 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Swedish Grid RIK (.rik)");
1287 1678 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/rik.html");
1288 1678 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "rik");
1289 1678 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1290 :
1291 1678 : poDriver->pfnOpen = RIKDataset::Open;
1292 1678 : poDriver->pfnIdentify = RIKDataset::Identify;
1293 :
1294 1678 : GetGDALDriverManager()->RegisterDriver(poDriver);
1295 : }
|