Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OZF2 and OZFx3 binary files driver
4 : * Purpose: GDALDataset driver for OZF2 and OZFx3 binary files.
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2010-2012, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "gdal_frmts.h"
30 : #include "gdal_pam.h"
31 : #include "zlib.h"
32 :
33 : /* g++ -fPIC -g -Wall frmts/ozi/ozidataset.cpp -shared -o gdal_OZI.so -Iport
34 : * -Igcore -Iogr -L. -lgdal */
35 :
36 : /************************************************************************/
37 : /* ==================================================================== */
38 : /* OZIDataset */
39 : /* ==================================================================== */
40 : /************************************************************************/
41 :
42 : class OZIRasterBand;
43 :
44 : class OZIDataset final : public GDALPamDataset
45 : {
46 : friend class OZIRasterBand;
47 :
48 : VSILFILE *fp;
49 : int nZoomLevelCount;
50 : int *panZoomLevelOffsets;
51 : OZIRasterBand **papoOvrBands;
52 : vsi_l_offset nFileSize;
53 :
54 : int bOzi3;
55 : GByte nKeyInit;
56 :
57 : public:
58 : OZIDataset();
59 : virtual ~OZIDataset();
60 :
61 : static GDALDataset *Open(GDALOpenInfo *);
62 : static int Identify(GDALOpenInfo *);
63 : };
64 :
65 : /************************************************************************/
66 : /* ==================================================================== */
67 : /* OZIRasterBand */
68 : /* ==================================================================== */
69 : /************************************************************************/
70 :
71 : class OZIRasterBand final : public GDALPamRasterBand
72 : {
73 : friend class OZIDataset;
74 :
75 : int nXBlocks;
76 : int nZoomLevel;
77 : std::unique_ptr<GDALColorTable> poColorTable{};
78 : GByte *pabyTranslationTable;
79 :
80 : public:
81 : OZIRasterBand(OZIDataset *, int nZoomLevel, int nRasterXSize,
82 : int nRasterYSize, int nXBlocks,
83 : std::unique_ptr<GDALColorTable> &&poColorTableIn);
84 : virtual ~OZIRasterBand();
85 :
86 : virtual CPLErr IReadBlock(int, int, void *) override;
87 : virtual GDALColorInterp GetColorInterpretation() override;
88 : virtual GDALColorTable *GetColorTable() override;
89 :
90 : virtual int GetOverviewCount() override;
91 : virtual GDALRasterBand *GetOverview(int nLevel) override;
92 : };
93 :
94 : /************************************************************************/
95 : /* I/O functions */
96 : /************************************************************************/
97 :
98 : constexpr GByte abyKey[] = {0x2D, 0x4A, 0x43, 0xF1, 0x27, 0x9B, 0x69,
99 : 0x4F, 0x36, 0x52, 0x87, 0xEC, 0x5F, 0x42,
100 : 0x53, 0x22, 0x9E, 0x8B, 0x2D, 0x83, 0x3D,
101 : 0xD2, 0x84, 0xBA, 0xD8, 0x5B};
102 :
103 0 : static void OZIDecrypt(void *pabyVal, int n, GByte nKeyInit)
104 : {
105 0 : for (int i = 0; i < n; i++)
106 : {
107 0 : reinterpret_cast<GByte *>(pabyVal)[i] ^=
108 0 : abyKey[i % sizeof(abyKey)] + nKeyInit;
109 : }
110 0 : }
111 :
112 0 : static int ReadInt(GByte **pptr)
113 : {
114 : int nVal;
115 0 : memcpy(&nVal, *pptr, 4);
116 0 : *pptr += 4;
117 0 : CPL_LSBPTR32(&nVal);
118 0 : return nVal;
119 : }
120 :
121 0 : static short ReadShort(GByte **pptr)
122 : {
123 : short nVal;
124 0 : memcpy(&nVal, *pptr, 2);
125 0 : *pptr += 2;
126 0 : CPL_LSBPTR16(&nVal);
127 0 : return nVal;
128 : }
129 :
130 0 : static int ReadInt(VSILFILE *fp, int bOzi3 = FALSE, int nKeyInit = 0)
131 : {
132 : int nVal;
133 0 : VSIFReadL(&nVal, 1, 4, fp);
134 0 : if (bOzi3)
135 : {
136 : GByte abyVal[4];
137 0 : memcpy(&abyVal[0], &nVal, 4);
138 0 : OZIDecrypt(&abyVal[0], 4, static_cast<GByte>(nKeyInit));
139 0 : memcpy(&nVal, &abyVal[0], 4);
140 : }
141 0 : CPL_LSBPTR32(&nVal);
142 0 : return nVal;
143 : }
144 :
145 0 : static short ReadShort(VSILFILE *fp, int bOzi3 = FALSE, int nKeyInit = 0)
146 : {
147 : short nVal;
148 0 : VSIFReadL(&nVal, 1, 2, fp);
149 0 : if (bOzi3)
150 : {
151 : GByte abyVal[2];
152 0 : memcpy(&abyVal[0], &nVal, 2);
153 0 : OZIDecrypt(&abyVal[0], 2, static_cast<GByte>(nKeyInit));
154 0 : memcpy(&nVal, &abyVal[0], 2);
155 : }
156 0 : CPL_LSBPTR16(&nVal);
157 0 : return nVal;
158 : }
159 :
160 : /************************************************************************/
161 : /* OZIRasterBand() */
162 : /************************************************************************/
163 :
164 0 : OZIRasterBand::OZIRasterBand(OZIDataset *poDSIn, int nZoomLevelIn,
165 : int nRasterXSizeIn, int nRasterYSizeIn,
166 : int nXBlocksIn,
167 0 : std::unique_ptr<GDALColorTable> &&poColorTableIn)
168 : : nXBlocks(nXBlocksIn), nZoomLevel(nZoomLevelIn),
169 0 : poColorTable(std::move(poColorTableIn)), pabyTranslationTable(nullptr)
170 : {
171 0 : poDS = poDSIn;
172 0 : nBand = 1;
173 :
174 0 : eDataType = GDT_Byte;
175 :
176 0 : nBlockXSize = 64;
177 0 : nBlockYSize = 64;
178 :
179 0 : nRasterXSize = nRasterXSizeIn;
180 0 : nRasterYSize = nRasterYSizeIn;
181 0 : }
182 :
183 : /************************************************************************/
184 : /* ~OZIRasterBand() */
185 : /************************************************************************/
186 :
187 0 : OZIRasterBand::~OZIRasterBand()
188 : {
189 0 : CPLFree(pabyTranslationTable);
190 0 : }
191 :
192 : /************************************************************************/
193 : /* GetColorInterpretation() */
194 : /************************************************************************/
195 :
196 0 : GDALColorInterp OZIRasterBand::GetColorInterpretation()
197 : {
198 0 : return GCI_PaletteIndex;
199 : }
200 :
201 : /************************************************************************/
202 : /* GetColorTable() */
203 : /************************************************************************/
204 :
205 0 : GDALColorTable *OZIRasterBand::GetColorTable()
206 : {
207 0 : return poColorTable.get();
208 : }
209 :
210 : /************************************************************************/
211 : /* IReadBlock() */
212 : /************************************************************************/
213 :
214 0 : CPLErr OZIRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
215 :
216 : {
217 0 : OZIDataset *poGDS = reinterpret_cast<OZIDataset *>(poDS);
218 :
219 0 : const int nBlock = nBlockYOff * nXBlocks + nBlockXOff;
220 :
221 0 : VSIFSeekL(poGDS->fp,
222 0 : poGDS->panZoomLevelOffsets[nZoomLevel] + 12 + 1024 + 4 * nBlock,
223 : SEEK_SET);
224 0 : const int nPointer = ReadInt(poGDS->fp, poGDS->bOzi3, poGDS->nKeyInit);
225 0 : if (nPointer < 0 || (vsi_l_offset)nPointer >= poGDS->nFileSize)
226 : {
227 0 : CPLError(CE_Failure, CPLE_AppDefined,
228 : "Invalid offset for block (%d, %d) : %d", nBlockXOff,
229 : nBlockYOff, nPointer);
230 0 : return CE_Failure;
231 : }
232 0 : int nNextPointer = ReadInt(poGDS->fp, poGDS->bOzi3, poGDS->nKeyInit);
233 0 : if (nNextPointer <= nPointer + 16 ||
234 0 : (vsi_l_offset)nNextPointer >= poGDS->nFileSize ||
235 0 : nNextPointer - nPointer > 10 * 64 * 64)
236 : {
237 0 : CPLError(CE_Failure, CPLE_AppDefined,
238 : "Invalid next offset for block (%d, %d) : %d", nBlockXOff,
239 : nBlockYOff, nNextPointer);
240 0 : return CE_Failure;
241 : }
242 :
243 0 : VSIFSeekL(poGDS->fp, nPointer, SEEK_SET);
244 :
245 0 : const int nToRead = nNextPointer - nPointer;
246 0 : GByte *pabyZlibBuffer = reinterpret_cast<GByte *>(CPLMalloc(nToRead));
247 0 : if (VSIFReadL(pabyZlibBuffer, nToRead, 1, poGDS->fp) != 1)
248 : {
249 0 : CPLError(CE_Failure, CPLE_AppDefined,
250 : "Not enough byte read for block (%d, %d)", nBlockXOff,
251 : nBlockYOff);
252 0 : CPLFree(pabyZlibBuffer);
253 0 : return CE_Failure;
254 : }
255 :
256 0 : if (poGDS->bOzi3)
257 0 : OZIDecrypt(pabyZlibBuffer, 16, poGDS->nKeyInit);
258 :
259 0 : if (pabyZlibBuffer[0] != 0x78 || pabyZlibBuffer[1] != 0xDA)
260 : {
261 0 : CPLError(CE_Failure, CPLE_AppDefined,
262 : "Bad ZLIB signature for block (%d, %d) : 0x%02X 0x%02X",
263 0 : nBlockXOff, nBlockYOff, pabyZlibBuffer[0], pabyZlibBuffer[1]);
264 0 : CPLFree(pabyZlibBuffer);
265 0 : return CE_Failure;
266 : }
267 :
268 : z_stream stream;
269 0 : memset(&stream, 0, sizeof(stream));
270 0 : stream.zalloc = (alloc_func) nullptr;
271 0 : stream.zfree = (free_func) nullptr;
272 0 : stream.opaque = (voidpf) nullptr;
273 0 : stream.next_in = pabyZlibBuffer + 2;
274 0 : stream.avail_in = nToRead - 2;
275 :
276 0 : int err = inflateInit2(&(stream), -MAX_WBITS);
277 :
278 0 : for (int i = 0; i < 64 && err == Z_OK; i++)
279 : {
280 0 : stream.next_out = reinterpret_cast<Bytef *>(pImage) + (63 - i) * 64;
281 0 : stream.avail_out = 64;
282 0 : err = inflate(&(stream), Z_NO_FLUSH);
283 0 : if (err != Z_OK && err != Z_STREAM_END)
284 0 : break;
285 :
286 0 : if (pabyTranslationTable)
287 : {
288 0 : GByte *ptr = reinterpret_cast<GByte *>(pImage) + (63 - i) * 64;
289 0 : for (int j = 0; j < 64; j++)
290 : {
291 0 : *ptr = pabyTranslationTable[*ptr];
292 0 : ptr++;
293 : }
294 : }
295 : }
296 :
297 0 : inflateEnd(&stream);
298 :
299 0 : CPLFree(pabyZlibBuffer);
300 :
301 0 : return (err == Z_OK || err == Z_STREAM_END) ? CE_None : CE_Failure;
302 : }
303 :
304 : /************************************************************************/
305 : /* GetOverviewCount() */
306 : /************************************************************************/
307 :
308 0 : int OZIRasterBand::GetOverviewCount()
309 : {
310 0 : if (nZoomLevel != 0)
311 0 : return 0;
312 :
313 0 : OZIDataset *poGDS = reinterpret_cast<OZIDataset *>(poDS);
314 0 : return poGDS->nZoomLevelCount - 1;
315 : }
316 :
317 : /************************************************************************/
318 : /* GetOverview() */
319 : /************************************************************************/
320 :
321 0 : GDALRasterBand *OZIRasterBand::GetOverview(int nLevel)
322 : {
323 0 : if (nZoomLevel != 0)
324 0 : return nullptr;
325 :
326 0 : OZIDataset *poGDS = reinterpret_cast<OZIDataset *>(poDS);
327 0 : if (nLevel < 0 || nLevel >= poGDS->nZoomLevelCount - 1)
328 0 : return nullptr;
329 :
330 0 : return poGDS->papoOvrBands[nLevel + 1];
331 : }
332 :
333 : /************************************************************************/
334 : /* ~OZIDataset() */
335 : /************************************************************************/
336 :
337 0 : OZIDataset::OZIDataset()
338 : : fp(nullptr), nZoomLevelCount(0), panZoomLevelOffsets(nullptr),
339 0 : papoOvrBands(nullptr), nFileSize(0), bOzi3(FALSE), nKeyInit(0)
340 : {
341 0 : }
342 :
343 : /************************************************************************/
344 : /* ~OZIDataset() */
345 : /************************************************************************/
346 :
347 0 : OZIDataset::~OZIDataset()
348 : {
349 0 : if (fp)
350 0 : VSIFCloseL(fp);
351 0 : if (papoOvrBands != nullptr)
352 : {
353 : /* start at 1: do not destroy the base band ! */
354 0 : for (int i = 1; i < nZoomLevelCount; i++)
355 0 : delete papoOvrBands[i];
356 0 : CPLFree(papoOvrBands);
357 : }
358 0 : CPLFree(panZoomLevelOffsets);
359 0 : }
360 :
361 : /************************************************************************/
362 : /* Identify() */
363 : /************************************************************************/
364 :
365 47992 : int OZIDataset::Identify(GDALOpenInfo *poOpenInfo)
366 : {
367 47992 : if (poOpenInfo->nHeaderBytes < 14)
368 45326 : return FALSE;
369 :
370 2666 : if (poOpenInfo->pabyHeader[0] == 0x80 && poOpenInfo->pabyHeader[1] == 0x77)
371 0 : return TRUE;
372 :
373 2666 : return poOpenInfo->pabyHeader[0] == 0x78 &&
374 0 : poOpenInfo->pabyHeader[1] == 0x77 &&
375 0 : poOpenInfo->pabyHeader[6] == 0x40 &&
376 0 : poOpenInfo->pabyHeader[7] == 0x00 &&
377 0 : poOpenInfo->pabyHeader[8] == 0x01 &&
378 0 : poOpenInfo->pabyHeader[9] == 0x00 &&
379 0 : poOpenInfo->pabyHeader[10] == 0x36 &&
380 0 : poOpenInfo->pabyHeader[11] == 0x04 &&
381 2666 : poOpenInfo->pabyHeader[12] == 0x00 &&
382 2666 : poOpenInfo->pabyHeader[13] == 0x00;
383 : }
384 :
385 : /************************************************************************/
386 : /* Open() */
387 : /************************************************************************/
388 :
389 0 : GDALDataset *OZIDataset::Open(GDALOpenInfo *poOpenInfo)
390 :
391 : {
392 0 : if (!Identify(poOpenInfo))
393 0 : return nullptr;
394 :
395 : GByte abyHeader[14];
396 0 : memcpy(abyHeader, poOpenInfo->pabyHeader, 14);
397 :
398 0 : int bOzi3 = (abyHeader[0] == 0x80 && abyHeader[1] == 0x77);
399 :
400 0 : const CPLString osImgFilename = poOpenInfo->pszFilename;
401 0 : VSILFILE *fp = VSIFOpenL(osImgFilename.c_str(), "rb");
402 0 : if (fp == nullptr)
403 0 : return nullptr;
404 :
405 0 : OZIDataset *poDS = new OZIDataset();
406 0 : poDS->fp = fp;
407 :
408 0 : GByte nKeyInit = 0;
409 0 : if (bOzi3)
410 : {
411 0 : VSIFSeekL(fp, 14, SEEK_SET);
412 :
413 0 : GByte nRandomNumber = 0;
414 0 : VSIFReadL(&nRandomNumber, 1, 1, fp);
415 : // printf("nRandomNumber = %d\n", nRandomNumber);
416 0 : if (nRandomNumber < 0x94)
417 : {
418 0 : delete poDS;
419 0 : return nullptr;
420 : }
421 0 : VSIFSeekL(fp, 0x93, SEEK_CUR);
422 0 : VSIFReadL(&nKeyInit, 1, 1, fp);
423 :
424 0 : VSIFSeekL(fp, 0, SEEK_SET);
425 0 : VSIFReadL(abyHeader, 1, 14, fp);
426 0 : OZIDecrypt(abyHeader, 14, nKeyInit);
427 0 : if (!(abyHeader[6] == 0x40 && abyHeader[7] == 0x00 &&
428 0 : abyHeader[8] == 0x01 && abyHeader[9] == 0x00 &&
429 0 : abyHeader[10] == 0x36 && abyHeader[11] == 0x04 &&
430 0 : abyHeader[12] == 0x00 && abyHeader[13] == 0x00))
431 : {
432 0 : delete poDS;
433 0 : return nullptr;
434 : }
435 :
436 0 : VSIFSeekL(fp, 14 + 1 + nRandomNumber, SEEK_SET);
437 0 : const int nMagic = ReadInt(fp, bOzi3, nKeyInit);
438 0 : CPLDebug("OZI", "OZI version code : 0x%08X", nMagic);
439 :
440 0 : poDS->bOzi3 = bOzi3;
441 : }
442 : else
443 : {
444 0 : VSIFSeekL(fp, 14, SEEK_SET);
445 : }
446 :
447 : GByte abyHeader2[40], abyHeader2_Backup[40];
448 0 : VSIFReadL(abyHeader2, 40, 1, fp);
449 0 : memcpy(abyHeader2_Backup, abyHeader2, 40);
450 :
451 : /* There's apparently a relationship between the nMagic number */
452 : /* and the nKeyInit, but I'm too lazy to add switch/cases that might */
453 : /* be not exhaustive, so let's try the 'brute force' attack !!! */
454 : /* It is much so funny to be able to run one in a few microseconds :-) */
455 0 : for (int i = 0; i < 256; i++)
456 : {
457 0 : nKeyInit = static_cast<GByte>(i);
458 0 : GByte *pabyHeader2 = abyHeader2;
459 0 : if (bOzi3)
460 0 : OZIDecrypt(abyHeader2, 40, nKeyInit);
461 :
462 0 : const int nHeaderSize = ReadInt(&pabyHeader2); /* should be 40 */
463 0 : poDS->nRasterXSize = ReadInt(&pabyHeader2);
464 0 : poDS->nRasterYSize = ReadInt(&pabyHeader2);
465 0 : const int nDepth = ReadShort(&pabyHeader2); /* should be 1 */
466 0 : const int nBPP = ReadShort(&pabyHeader2); /* should be 8 */
467 0 : ReadInt(&pabyHeader2); /* reserved */
468 0 : ReadInt(&pabyHeader2); /* pixel number (height * width) : unused */
469 0 : ReadInt(&pabyHeader2); /* reserved */
470 0 : ReadInt(&pabyHeader2); /* reserved */
471 0 : ReadInt(&pabyHeader2); /* ?? 0x100 */
472 0 : ReadInt(&pabyHeader2); /* ?? 0x100 */
473 :
474 0 : if (nHeaderSize != 40 || nDepth != 1 || nBPP != 8)
475 : {
476 0 : if (bOzi3)
477 : {
478 0 : if (nKeyInit != 255)
479 : {
480 0 : memcpy(abyHeader2, abyHeader2_Backup, 40);
481 0 : continue;
482 : }
483 : else
484 : {
485 0 : CPLDebug("OZI", "Cannot decipher 2nd header. Sorry...");
486 0 : delete poDS;
487 0 : return nullptr;
488 : }
489 : }
490 : else
491 : {
492 0 : CPLDebug("OZI", "nHeaderSize = %d, nDepth = %d, nBPP = %d",
493 : nHeaderSize, nDepth, nBPP);
494 0 : delete poDS;
495 0 : return nullptr;
496 : }
497 : }
498 : else
499 : break;
500 : }
501 0 : poDS->nKeyInit = nKeyInit;
502 :
503 0 : int nSeparator = ReadInt(fp);
504 0 : if (!bOzi3 && nSeparator != 0x77777777)
505 : {
506 0 : CPLDebug("OZI", "didn't get end of header2 marker");
507 0 : delete poDS;
508 0 : return nullptr;
509 : }
510 :
511 0 : poDS->nZoomLevelCount = ReadShort(fp);
512 :
513 : #ifdef DEBUG_VERBOSE
514 : CPLDebug("OZI", "nZoomLevelCount = %d", poDS->nZoomLevelCount);
515 : #endif
516 :
517 0 : if (poDS->nZoomLevelCount < 0 || poDS->nZoomLevelCount >= 256)
518 : {
519 0 : CPLDebug("OZI", "nZoomLevelCount = %d", poDS->nZoomLevelCount);
520 0 : delete poDS;
521 0 : return nullptr;
522 : }
523 :
524 : /* Skip array of zoom level percentage. We don't need it for GDAL */
525 0 : VSIFSeekL(fp, sizeof(float) * poDS->nZoomLevelCount, SEEK_CUR);
526 :
527 0 : nSeparator = ReadInt(fp);
528 0 : if (!bOzi3 && nSeparator != 0x77777777)
529 : {
530 : /* Some files have 8 extra bytes before the marker. I'm not sure */
531 : /* what they are used for. So just skip them and hope that */
532 : /* we'll find the marker */
533 0 : CPL_IGNORE_RET_VAL(ReadInt(fp));
534 0 : nSeparator = ReadInt(fp);
535 0 : if (nSeparator != 0x77777777)
536 : {
537 0 : CPLDebug("OZI", "didn't get end of zoom levels marker");
538 0 : delete poDS;
539 0 : return nullptr;
540 : }
541 : }
542 :
543 0 : VSIFSeekL(fp, 0, SEEK_END);
544 0 : const vsi_l_offset nFileSize = VSIFTellL(fp);
545 0 : poDS->nFileSize = nFileSize;
546 0 : VSIFSeekL(fp, nFileSize - 4, SEEK_SET);
547 0 : const int nZoomLevelTableOffset = ReadInt(fp, bOzi3, nKeyInit);
548 0 : if (nZoomLevelTableOffset < 0 ||
549 0 : (vsi_l_offset)nZoomLevelTableOffset >= nFileSize)
550 : {
551 0 : CPLDebug("OZI", "nZoomLevelTableOffset = %d", nZoomLevelTableOffset);
552 0 : delete poDS;
553 0 : return nullptr;
554 : }
555 :
556 0 : VSIFSeekL(fp, nZoomLevelTableOffset, SEEK_SET);
557 :
558 0 : poDS->panZoomLevelOffsets =
559 0 : reinterpret_cast<int *>(CPLMalloc(sizeof(int) * poDS->nZoomLevelCount));
560 :
561 0 : for (int i = 0; i < poDS->nZoomLevelCount; i++)
562 : {
563 0 : poDS->panZoomLevelOffsets[i] = ReadInt(fp, bOzi3, nKeyInit);
564 0 : if (poDS->panZoomLevelOffsets[i] < 0 ||
565 0 : (vsi_l_offset)poDS->panZoomLevelOffsets[i] >= nFileSize)
566 : {
567 0 : CPLDebug("OZI", "panZoomLevelOffsets[%d] = %d", i,
568 0 : poDS->panZoomLevelOffsets[i]);
569 0 : delete poDS;
570 0 : return nullptr;
571 : }
572 : }
573 :
574 0 : poDS->papoOvrBands = reinterpret_cast<OZIRasterBand **>(
575 0 : CPLCalloc(sizeof(OZIRasterBand *), poDS->nZoomLevelCount));
576 :
577 0 : for (int i = 0; i < poDS->nZoomLevelCount; i++)
578 : {
579 0 : VSIFSeekL(fp, poDS->panZoomLevelOffsets[i], SEEK_SET);
580 0 : const int nW = ReadInt(fp, bOzi3, nKeyInit);
581 0 : const int nH = ReadInt(fp, bOzi3, nKeyInit);
582 0 : const short nTileX = ReadShort(fp, bOzi3, nKeyInit);
583 0 : const short nTileY = ReadShort(fp, bOzi3, nKeyInit);
584 0 : if (i == 0 && (nW != poDS->nRasterXSize || nH != poDS->nRasterYSize))
585 : {
586 0 : CPLDebug("OZI",
587 : "zoom[%d] inconsistent dimensions for zoom level 0 "
588 : ": nW=%d, nH=%d, nTileX=%d, nTileY=%d, nRasterXSize=%d, "
589 : "nRasterYSize=%d",
590 : i, nW, nH, nTileX, nTileY, poDS->nRasterXSize,
591 : poDS->nRasterYSize);
592 0 : delete poDS;
593 0 : return nullptr;
594 : }
595 : /* Note (#3895): some files such as world.ozf2 provided with OziExplorer
596 : */
597 : /* expose nTileY=33, but have nH=2048, so only require 32 tiles in
598 : * vertical dimension. */
599 : /* So there's apparently one extra and useless tile that will be ignored
600 : */
601 : /* without causing apparent issues */
602 : /* Some other files have more tile in horizontal direction than needed,
603 : * so let's */
604 : /* accept that. But in that case we really need to keep the nTileX value
605 : * for IReadBlock() */
606 : /* to work properly */
607 0 : if ((nW + 63) / 64 > nTileX || (nH + 63) / 64 > nTileY)
608 : {
609 0 : CPLDebug("OZI",
610 : "zoom[%d] unexpected number of tiles : nW=%d, "
611 : "nH=%d, nTileX=%d, nTileY=%d",
612 : i, nW, nH, nTileX, nTileY);
613 0 : delete poDS;
614 0 : return nullptr;
615 : }
616 :
617 0 : auto poColorTable = std::make_unique<GDALColorTable>();
618 : GByte abyColorTable[256 * 4];
619 0 : VSIFReadL(abyColorTable, 1, 1024, fp);
620 0 : if (bOzi3)
621 0 : OZIDecrypt(abyColorTable, 1024, nKeyInit);
622 0 : for (int j = 0; j < 256; j++)
623 : {
624 : GDALColorEntry sEntry;
625 0 : sEntry.c1 = abyColorTable[4 * j + 2];
626 0 : sEntry.c2 = abyColorTable[4 * j + 1];
627 0 : sEntry.c3 = abyColorTable[4 * j + 0];
628 0 : sEntry.c4 = 255;
629 0 : poColorTable->SetColorEntry(j, &sEntry);
630 : }
631 :
632 0 : poDS->papoOvrBands[i] =
633 0 : new OZIRasterBand(poDS, i, nW, nH, nTileX, std::move(poColorTable));
634 :
635 0 : if (i > 0)
636 : {
637 : GByte *pabyTranslationTable =
638 0 : poDS->papoOvrBands[i]->GetIndexColorTranslationTo(
639 0 : poDS->papoOvrBands[0], nullptr, nullptr);
640 :
641 0 : poDS->papoOvrBands[i]->poColorTable.reset(
642 0 : poDS->papoOvrBands[0]->poColorTable->Clone());
643 0 : poDS->papoOvrBands[i]->pabyTranslationTable = pabyTranslationTable;
644 : }
645 : }
646 :
647 0 : poDS->SetBand(1, poDS->papoOvrBands[0]);
648 :
649 : /* -------------------------------------------------------------------- */
650 : /* Initialize any PAM information. */
651 : /* -------------------------------------------------------------------- */
652 0 : poDS->SetDescription(poOpenInfo->pszFilename);
653 0 : poDS->TryLoadXML();
654 :
655 : /* -------------------------------------------------------------------- */
656 : /* Support overviews. */
657 : /* -------------------------------------------------------------------- */
658 0 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
659 0 : return poDS;
660 : }
661 :
662 : /************************************************************************/
663 : /* GDALRegister_OZI() */
664 : /************************************************************************/
665 :
666 1512 : void GDALRegister_OZI()
667 :
668 : {
669 1512 : if (!GDAL_CHECK_VERSION("OZI driver"))
670 0 : return;
671 :
672 1512 : if (GDALGetDriverByName("OZI") != nullptr)
673 295 : return;
674 :
675 1217 : GDALDriver *poDriver = new GDALDriver();
676 :
677 1217 : poDriver->SetDescription("OZI");
678 1217 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
679 1217 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "OziExplorer Image File");
680 1217 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/ozi.html");
681 1217 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
682 :
683 1217 : poDriver->pfnOpen = OZIDataset::Open;
684 1217 : poDriver->pfnIdentify = OZIDataset::Identify;
685 :
686 1217 : GetGDALDriverManager()->RegisterDriver(poDriver);
687 : }
|