LCOV - code coverage report
Current view: top level - frmts/ozi - ozidataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 20 296 6.8 %
Date: 2024-11-21 22:18:42 Functions: 2 19 10.5 %

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

Generated by: LCOV version 1.14