EVOLUTION-MANAGER
Edit File: ozidataset.cpp
/****************************************************************************** * $Id: ozidataset.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: OZF2 and OZFx3 binary files driver * Purpose: GDALDataset driver for OZF2 and OZFx3 binary files. * Author: Even Rouault, <even dot rouault at mines dash paris dot org> * ****************************************************************************** * Copyright (c) 2010-2012, Even Rouault <even dot rouault at mines-paris dot org> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ #include "gdal_pam.h" #include "zlib.h" /* g++ -fPIC -g -Wall frmts/ozi/ozidataset.cpp -shared -o gdal_OZI.so -Iport -Igcore -Iogr -L. -lgdal */ CPL_CVSID("$Id: ozidataset.cpp 27044 2014-03-16 23:41:27Z rouault $"); CPL_C_START void GDALRegister_OZI(void); CPL_C_END /************************************************************************/ /* ==================================================================== */ /* OZIDataset */ /* ==================================================================== */ /************************************************************************/ class OZIRasterBand; class OZIDataset : public GDALPamDataset { friend class OZIRasterBand; VSILFILE* fp; int nZoomLevelCount; int* panZoomLevelOffsets; OZIRasterBand** papoOvrBands; vsi_l_offset nFileSize; int bOzi3; GByte nKeyInit; public: OZIDataset(); virtual ~OZIDataset(); static GDALDataset *Open( GDALOpenInfo * ); static int Identify( GDALOpenInfo * ); }; /************************************************************************/ /* ==================================================================== */ /* OZIRasterBand */ /* ==================================================================== */ /************************************************************************/ class OZIRasterBand : public GDALPamRasterBand { friend class OZIDataset; int nXBlocks; int nZoomLevel; GDALColorTable* poColorTable; GByte* pabyTranslationTable; public: OZIRasterBand( OZIDataset *, int nZoomLevel, int nRasterXSize, int nRasterYSize, int nXBlocks, GDALColorTable* poColorTable); virtual ~OZIRasterBand(); virtual CPLErr IReadBlock( int, int, void * ); virtual GDALColorInterp GetColorInterpretation(); virtual GDALColorTable *GetColorTable(); virtual int GetOverviewCount(); virtual GDALRasterBand* GetOverview(int nLevel); }; /************************************************************************/ /* I/O functions */ /************************************************************************/ static const GByte abyKey[] = { 0x2D, 0x4A, 0x43, 0xF1, 0x27, 0x9B, 0x69, 0x4F, 0x36, 0x52, 0x87, 0xEC, 0x5F, 0x42, 0x53, 0x22, 0x9E, 0x8B, 0x2D, 0x83, 0x3D, 0xD2, 0x84, 0xBA, 0xD8, 0x5B }; static void OZIDecrypt(void *pabyVal, int n, GByte nKeyInit) { int i; for(i = 0; i < n; i++) { ((GByte*)pabyVal)[i] ^= abyKey[i % sizeof(abyKey)] + nKeyInit; } } static int ReadInt(GByte**pptr) { int nVal; memcpy(&nVal, *pptr, 4); *pptr += 4; CPL_LSBPTR32(&nVal); return nVal; } static short ReadShort(GByte**pptr) { short nVal; memcpy(&nVal, *pptr, 2); *pptr += 2; CPL_LSBPTR16(&nVal); return nVal; } static int ReadInt(VSILFILE* fp, int bOzi3 = FALSE, int nKeyInit = 0) { int nVal; VSIFReadL(&nVal, 1, 4, fp); if (bOzi3) OZIDecrypt(&nVal, 4, (GByte) nKeyInit); CPL_LSBPTR32(&nVal); return nVal; } static short ReadShort(VSILFILE* fp, int bOzi3 = FALSE, int nKeyInit = 0) { short nVal; VSIFReadL(&nVal, 1, 2, fp); if (bOzi3) OZIDecrypt(&nVal, 2, (GByte) nKeyInit); CPL_LSBPTR16(&nVal); return nVal; } /************************************************************************/ /* OZIRasterBand() */ /************************************************************************/ OZIRasterBand::OZIRasterBand( OZIDataset *poDS, int nZoomLevel, int nRasterXSize, int nRasterYSize, int nXBlocks, GDALColorTable* poColorTable ) { this->poDS = poDS; this->nBand = 1; eDataType = GDT_Byte; nBlockXSize = 64; nBlockYSize = 64; this->nZoomLevel = nZoomLevel; this->nRasterXSize = nRasterXSize; this->nRasterYSize = nRasterYSize; this->poColorTable = poColorTable; this->nXBlocks = nXBlocks; pabyTranslationTable = NULL; } /************************************************************************/ /* ~OZIRasterBand() */ /************************************************************************/ OZIRasterBand::~OZIRasterBand() { delete poColorTable; CPLFree(pabyTranslationTable); } /************************************************************************/ /* GetColorInterpretation() */ /************************************************************************/ GDALColorInterp OZIRasterBand::GetColorInterpretation() { return GCI_PaletteIndex; } /************************************************************************/ /* GetColorTable() */ /************************************************************************/ GDALColorTable* OZIRasterBand::GetColorTable() { return poColorTable; } /************************************************************************/ /* IReadBlock() */ /************************************************************************/ CPLErr OZIRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, void * pImage ) { OZIDataset *poGDS = (OZIDataset *) poDS; int nBlock = nBlockYOff * nXBlocks + nBlockXOff; VSIFSeekL(poGDS->fp, poGDS->panZoomLevelOffsets[nZoomLevel] + 12 + 1024 + 4 * nBlock, SEEK_SET); int nPointer = ReadInt(poGDS->fp, poGDS->bOzi3, poGDS->nKeyInit); if (nPointer < 0 || (vsi_l_offset)nPointer >= poGDS->nFileSize) { CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset for block (%d, %d) : %d", nBlockXOff, nBlockYOff, nPointer); return CE_Failure; } int nNextPointer = ReadInt(poGDS->fp, poGDS->bOzi3, poGDS->nKeyInit); if (nNextPointer <= nPointer + 16 || (vsi_l_offset)nNextPointer >= poGDS->nFileSize || nNextPointer - nPointer > 10 * 64 * 64) { CPLError(CE_Failure, CPLE_AppDefined, "Invalid next offset for block (%d, %d) : %d", nBlockXOff, nBlockYOff, nNextPointer); return CE_Failure; } VSIFSeekL(poGDS->fp, nPointer, SEEK_SET); int nToRead = nNextPointer - nPointer; GByte* pabyZlibBuffer = (GByte*)CPLMalloc(nToRead); if (VSIFReadL(pabyZlibBuffer, nToRead, 1, poGDS->fp) != 1) { CPLError(CE_Failure, CPLE_AppDefined, "Not enough byte read for block (%d, %d)", nBlockXOff, nBlockYOff); CPLFree(pabyZlibBuffer); return CE_Failure; } if (poGDS->bOzi3) OZIDecrypt(pabyZlibBuffer, 16, poGDS->nKeyInit); if (pabyZlibBuffer[0] != 0x78 || pabyZlibBuffer[1] != 0xDA) { CPLError(CE_Failure, CPLE_AppDefined, "Bad ZLIB signature for block (%d, %d) : 0x%02X 0x%02X", nBlockXOff, nBlockYOff, pabyZlibBuffer[0], pabyZlibBuffer[1]); CPLFree(pabyZlibBuffer); return CE_Failure; } z_stream stream; stream.zalloc = (alloc_func)0; stream.zfree = (free_func)0; stream.opaque = (voidpf)0; stream.next_in = pabyZlibBuffer + 2; stream.avail_in = nToRead - 2; int err = inflateInit2(&(stream), -MAX_WBITS); int i; for(i=0;i<64 && err == Z_OK;i++) { stream.next_out = (Bytef*)pImage + (63 - i) * 64; stream.avail_out = 64; err = inflate(& (stream), Z_NO_FLUSH); if (err != Z_OK && err != Z_STREAM_END) break; if (pabyTranslationTable) { int j; GByte* ptr = ((GByte*)pImage) + (63 - i) * 64; for(j=0;j<64;j++) { *ptr = pabyTranslationTable[*ptr]; ptr ++; } } } inflateEnd(&stream); CPLFree(pabyZlibBuffer); return (err == Z_OK || err == Z_STREAM_END) ? CE_None : CE_Failure; } /************************************************************************/ /* GetOverviewCount() */ /************************************************************************/ int OZIRasterBand::GetOverviewCount() { if (nZoomLevel != 0) return 0; OZIDataset *poGDS = (OZIDataset *) poDS; return poGDS->nZoomLevelCount - 1; } /************************************************************************/ /* GetOverview() */ /************************************************************************/ GDALRasterBand* OZIRasterBand::GetOverview(int nLevel) { if (nZoomLevel != 0) return NULL; OZIDataset *poGDS = (OZIDataset *) poDS; if (nLevel < 0 || nLevel >= poGDS->nZoomLevelCount - 1) return NULL; return poGDS->papoOvrBands[nLevel + 1]; } /************************************************************************/ /* ~OZIDataset() */ /************************************************************************/ OZIDataset::OZIDataset() { fp = NULL; nZoomLevelCount = 0; panZoomLevelOffsets = NULL; papoOvrBands = NULL; bOzi3 = FALSE; nKeyInit = 0; } /************************************************************************/ /* ~OZIDataset() */ /************************************************************************/ OZIDataset::~OZIDataset() { if (fp) VSIFCloseL(fp); if (papoOvrBands != NULL ) { /* start at 1: do not destroy the base band ! */ for(int i=1;i<nZoomLevelCount;i++) delete papoOvrBands[i]; CPLFree(papoOvrBands); } CPLFree(panZoomLevelOffsets); } /************************************************************************/ /* Identify() */ /************************************************************************/ int OZIDataset::Identify( GDALOpenInfo * poOpenInfo ) { if (poOpenInfo->nHeaderBytes < 14) return FALSE; if (EQUALN((const char*)poOpenInfo->pabyHeader, "OziExplorer Map Data File Version ", 34) ) return TRUE; if (poOpenInfo->pabyHeader[0] == 0x80 && poOpenInfo->pabyHeader[1] == 0x77) return TRUE; return poOpenInfo->pabyHeader[0] == 0x78 && poOpenInfo->pabyHeader[1] == 0x77 && poOpenInfo->pabyHeader[6] == 0x40 && poOpenInfo->pabyHeader[7] == 0x00 && poOpenInfo->pabyHeader[8] == 0x01 && poOpenInfo->pabyHeader[9] == 0x00 && poOpenInfo->pabyHeader[10] == 0x36 && poOpenInfo->pabyHeader[11] == 0x04 && poOpenInfo->pabyHeader[12] == 0x00 && poOpenInfo->pabyHeader[13] == 0x00; } /************************************************************************/ /* Open() */ /************************************************************************/ GDALDataset *OZIDataset::Open( GDALOpenInfo * poOpenInfo ) { if (!Identify(poOpenInfo)) return NULL; GByte abyHeader[14]; CPLString osImgFilename = poOpenInfo->pszFilename; memcpy(abyHeader, poOpenInfo->pabyHeader, 14); int bOzi3 = (abyHeader[0] == 0x80 && abyHeader[1] == 0x77); VSILFILE* fp = VSIFOpenL(osImgFilename.c_str(), "rb"); if (fp == NULL) return NULL; OZIDataset* poDS = new OZIDataset(); poDS->fp = fp; GByte nRandomNumber = 0; GByte nKeyInit = 0; if (bOzi3) { VSIFSeekL(fp, 14, SEEK_SET); VSIFReadL(&nRandomNumber, 1, 1, fp); //printf("nRandomNumber = %d\n", nRandomNumber); if (nRandomNumber < 0x94) { delete poDS; return NULL; } VSIFSeekL(fp, 0x93, SEEK_CUR); VSIFReadL(&nKeyInit, 1, 1, fp); VSIFSeekL(fp, 0, SEEK_SET); VSIFReadL(abyHeader, 1, 14, fp); OZIDecrypt(abyHeader, 14, nKeyInit); if (!(abyHeader[6] == 0x40 && abyHeader[7] == 0x00 && abyHeader[8] == 0x01 && abyHeader[9] == 0x00 && abyHeader[10] == 0x36 && abyHeader[11] == 0x04 && abyHeader[12] == 0x00 && abyHeader[13] == 0x00)) { delete poDS; return NULL; } VSIFSeekL(fp, 14 + 1 + nRandomNumber, SEEK_SET); int nMagic = ReadInt(fp, bOzi3, nKeyInit); CPLDebug("OZI", "OZI version code : 0x%08X", nMagic); poDS->bOzi3 = bOzi3; } else { VSIFSeekL(fp, 14, SEEK_SET); } GByte abyHeader2[40], abyHeader2_Backup[40]; VSIFReadL(abyHeader2, 40, 1, fp); memcpy(abyHeader2_Backup, abyHeader2, 40); /* There's apparently a relationship between the nMagic number */ /* and the nKeyInit, but I'm too lazy to add switch/cases that might */ /* be not exhaustive, so let's try the 'brute force' attack !!! */ /* It is much so funny to be able to run one in a few microseconds :-) */ int i; for(i = 0; i < 256; i ++) { nKeyInit = (GByte)i; GByte* pabyHeader2 = abyHeader2; if (bOzi3) OZIDecrypt(abyHeader2, 40, nKeyInit); int nHeaderSize = ReadInt(&pabyHeader2); /* should be 40 */ poDS->nRasterXSize = ReadInt(&pabyHeader2); poDS->nRasterYSize = ReadInt(&pabyHeader2); int nDepth = ReadShort(&pabyHeader2); /* should be 1 */ int nBPP = ReadShort(&pabyHeader2); /* should be 8 */ ReadInt(&pabyHeader2); /* reserved */ ReadInt(&pabyHeader2); /* pixel number (height * width) : unused */ ReadInt(&pabyHeader2); /* reserved */ ReadInt(&pabyHeader2); /* reserved */ ReadInt(&pabyHeader2); /* ?? 0x100 */ ReadInt(&pabyHeader2); /* ?? 0x100 */ if (nHeaderSize != 40 || nDepth != 1 || nBPP != 8) { if (bOzi3) { if (nKeyInit != 255) { memcpy(abyHeader2, abyHeader2_Backup,40); continue; } else { CPLDebug("OZI", "Cannot decypher 2nd header. Sorry..."); delete poDS; return NULL; } } else { CPLDebug("OZI", "nHeaderSize = %d, nDepth = %d, nBPP = %d", nHeaderSize, nDepth, nBPP); delete poDS; return NULL; } } else break; } poDS->nKeyInit = nKeyInit; int nSeparator = ReadInt(fp); if (!bOzi3 && nSeparator != 0x77777777) { CPLDebug("OZI", "didn't get end of header2 marker"); delete poDS; return NULL; } poDS->nZoomLevelCount = ReadShort(fp); //CPLDebug("OZI", "nZoomLevelCount = %d", poDS->nZoomLevelCount); if (poDS->nZoomLevelCount < 0 || poDS->nZoomLevelCount >= 256) { CPLDebug("OZI", "nZoomLevelCount = %d", poDS->nZoomLevelCount); delete poDS; return NULL; } /* Skip array of zoom level percentage. We don't need it for GDAL */ VSIFSeekL(fp, sizeof(float) * poDS->nZoomLevelCount, SEEK_CUR); nSeparator = ReadInt(fp); if (!bOzi3 && nSeparator != 0x77777777) { /* Some files have 8 extra bytes before the marker. I'm not sure */ /* what they are used for. So just skeep them and hope that */ /* we'll find the marker */ nSeparator = ReadInt(fp); nSeparator = ReadInt(fp); if (nSeparator != 0x77777777) { CPLDebug("OZI", "didn't get end of zoom levels marker"); delete poDS; return NULL; } } VSIFSeekL(fp, 0, SEEK_END); vsi_l_offset nFileSize = VSIFTellL(fp); poDS->nFileSize = nFileSize; VSIFSeekL(fp, nFileSize - 4, SEEK_SET); int nZoomLevelTableOffset = ReadInt(fp, bOzi3, nKeyInit); if (nZoomLevelTableOffset < 0 || (vsi_l_offset)nZoomLevelTableOffset >= nFileSize) { CPLDebug("OZI", "nZoomLevelTableOffset = %d", nZoomLevelTableOffset); delete poDS; return NULL; } VSIFSeekL(fp, nZoomLevelTableOffset, SEEK_SET); poDS->panZoomLevelOffsets = (int*)CPLMalloc(sizeof(int) * poDS->nZoomLevelCount); for(i=0;i<poDS->nZoomLevelCount;i++) { poDS->panZoomLevelOffsets[i] = ReadInt(fp, bOzi3, nKeyInit); if (poDS->panZoomLevelOffsets[i] < 0 || (vsi_l_offset)poDS->panZoomLevelOffsets[i] >= nFileSize) { CPLDebug("OZI", "panZoomLevelOffsets[%d] = %d", i, poDS->panZoomLevelOffsets[i]); delete poDS; return NULL; } } poDS->papoOvrBands = (OZIRasterBand**)CPLCalloc(sizeof(OZIRasterBand*), poDS->nZoomLevelCount); for(i=0;i<poDS->nZoomLevelCount;i++) { VSIFSeekL(fp, poDS->panZoomLevelOffsets[i], SEEK_SET); int nW = ReadInt(fp, bOzi3, nKeyInit); int nH = ReadInt(fp, bOzi3, nKeyInit); short nTileX = ReadShort(fp, bOzi3, nKeyInit); short nTileY = ReadShort(fp, bOzi3, nKeyInit); if (i == 0 && (nW != poDS->nRasterXSize || nH != poDS->nRasterYSize)) { CPLDebug("OZI", "zoom[%d] inconsistant dimensions for zoom level 0 : nW=%d, nH=%d, nTileX=%d, nTileY=%d, nRasterXSize=%d, nRasterYSize=%d", i, nW, nH, nTileX, nTileY, poDS->nRasterXSize, poDS->nRasterYSize); delete poDS; return NULL; } /* Note (#3895): some files such as world.ozf2 provided with OziExplorer */ /* expose nTileY=33, but have nH=2048, so only require 32 tiles in vertical dimension. */ /* So there's apparently one extra and useless tile that will be ignored */ /* without causing apparent issues */ /* Some other files have more tile in horizontal direction than needed, so let's */ /* accept that. But in that case we really need to keep the nTileX value for IReadBlock() */ /* to work properly */ if ((nW + 63) / 64 > nTileX || (nH + 63) / 64 > nTileY) { CPLDebug("OZI", "zoom[%d] unexpected number of tiles : nW=%d, nH=%d, nTileX=%d, nTileY=%d", i, nW, nH, nTileX, nTileY); delete poDS; return NULL; } GDALColorTable* poColorTable = new GDALColorTable(); GByte abyColorTable[256*4]; VSIFReadL(abyColorTable, 1, 1024, fp); if (bOzi3) OZIDecrypt(abyColorTable, 1024, nKeyInit); int j; for(j=0;j<256;j++) { GDALColorEntry sEntry; sEntry.c1 = abyColorTable[4*j + 2]; sEntry.c2 = abyColorTable[4*j + 1]; sEntry.c3 = abyColorTable[4*j + 0]; sEntry.c4 = 255; poColorTable->SetColorEntry(j, &sEntry); } poDS->papoOvrBands[i] = new OZIRasterBand(poDS, i, nW, nH, nTileX, poColorTable); if (i > 0) { GByte* pabyTranslationTable = poDS->papoOvrBands[i]->GetIndexColorTranslationTo(poDS->papoOvrBands[0], NULL, NULL); delete poDS->papoOvrBands[i]->poColorTable; poDS->papoOvrBands[i]->poColorTable = poDS->papoOvrBands[0]->poColorTable->Clone(); poDS->papoOvrBands[i]->pabyTranslationTable = pabyTranslationTable; } } poDS->SetBand(1, poDS->papoOvrBands[0]); /* -------------------------------------------------------------------- */ /* Initialize any PAM information. */ /* -------------------------------------------------------------------- */ poDS->SetDescription( poOpenInfo->pszFilename ); poDS->TryLoadXML(); /* -------------------------------------------------------------------- */ /* Support overviews. */ /* -------------------------------------------------------------------- */ poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); return( poDS ); } /************************************************************************/ /* GDALRegister_OZI() */ /************************************************************************/ void GDALRegister_OZI() { GDALDriver *poDriver; if (! GDAL_CHECK_VERSION("OZI driver")) return; if( GDALGetDriverByName( "OZI" ) == NULL ) { poDriver = new GDALDriver(); poDriver->SetDescription( "OZI" ); poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "OziExplorer Image File" ); poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_ozi.html" ); poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); poDriver->pfnOpen = OZIDataset::Open; poDriver->pfnIdentify = OZIDataset::Identify; GetGDALDriverManager()->RegisterDriver( poDriver ); } }