LCOV - code coverage report
Current view: top level - frmts/aigrid - aigopen.c (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 92 178 51.7 %
Date: 2025-01-18 12:42:00 Functions: 6 7 85.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Arc/Info Binary Grid Translator
       4             :  * Purpose:  Grid file access cover API for non-GDAL use.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2009-2010, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "aigrid.h"
      15             : 
      16             : #ifndef CPL_IGNORE_RET_VAL_INT_defined
      17             : #define CPL_IGNORE_RET_VAL_INT_defined
      18             : 
      19           3 : CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused)
      20             : {
      21           3 : }
      22             : #endif
      23             : 
      24             : /************************************************************************/
      25             : /*                              AIGOpen()                               */
      26             : /************************************************************************/
      27             : 
      28           9 : AIGInfo_t *AIGOpen(const char *pszInputName, const char *pszAccess)
      29             : 
      30             : {
      31             :     AIGInfo_t *psInfo;
      32             :     char *pszCoverName;
      33             : 
      34             :     (void)pszAccess;
      35             : 
      36             :     /* -------------------------------------------------------------------- */
      37             :     /*      If the pass name ends in .adf assume a file within the          */
      38             :     /*      coverage has been selected, and strip that off the coverage     */
      39             :     /*      name.                                                           */
      40             :     /* -------------------------------------------------------------------- */
      41           9 :     pszCoverName = CPLStrdup(pszInputName);
      42           9 :     if (EQUAL(pszCoverName + strlen(pszCoverName) - 4, ".adf"))
      43             :     {
      44             :         int i;
      45             : 
      46           0 :         for (i = (int)strlen(pszCoverName) - 1; i > 0; i--)
      47             :         {
      48           0 :             if (pszCoverName[i] == '\\' || pszCoverName[i] == '/')
      49             :             {
      50           0 :                 pszCoverName[i] = '\0';
      51           0 :                 break;
      52             :             }
      53             :         }
      54             : 
      55           0 :         if (i == 0)
      56           0 :             strcpy(pszCoverName, ".");
      57             :     }
      58             : 
      59             :     /* -------------------------------------------------------------------- */
      60             :     /*      Allocate info structure.                                        */
      61             :     /* -------------------------------------------------------------------- */
      62           9 :     psInfo = (AIGInfo_t *)CPLCalloc(sizeof(AIGInfo_t), 1);
      63           9 :     psInfo->bHasWarned = FALSE;
      64           9 :     psInfo->nFailedOpenings = 0;
      65           9 :     psInfo->pszCoverName = pszCoverName;
      66             : 
      67             :     /* -------------------------------------------------------------------- */
      68             :     /*      Read the header file.                                           */
      69             :     /* -------------------------------------------------------------------- */
      70           9 :     if (AIGReadHeader(pszCoverName, psInfo) != CE_None)
      71             :     {
      72           0 :         CPLFree(pszCoverName);
      73           0 :         CPLFree(psInfo);
      74           0 :         return NULL;
      75             :     }
      76             : 
      77             :     /* -------------------------------------------------------------------- */
      78             :     /*      Read the extents.                                               */
      79             :     /* -------------------------------------------------------------------- */
      80           9 :     if (AIGReadBounds(pszCoverName, psInfo) != CE_None)
      81             :     {
      82           0 :         AIGClose(psInfo);
      83           0 :         return NULL;
      84             :     }
      85             : 
      86             :     /* -------------------------------------------------------------------- */
      87             :     /*      Compute the number of pixels and lines, and the number of       */
      88             :     /*      tile files.                                                     */
      89             :     /* -------------------------------------------------------------------- */
      90           9 :     if (psInfo->dfCellSizeX <= 0 || psInfo->dfCellSizeY <= 0)
      91             :     {
      92           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Illegal cell size : %f x %f",
      93             :                  psInfo->dfCellSizeX, psInfo->dfCellSizeY);
      94           0 :         AIGClose(psInfo);
      95           0 :         return NULL;
      96             :     }
      97             : 
      98           9 :     psInfo->nPixels =
      99           9 :         (int)((psInfo->dfURX - psInfo->dfLLX + 0.5 * psInfo->dfCellSizeX) /
     100           9 :               psInfo->dfCellSizeX);
     101           9 :     psInfo->nLines =
     102           9 :         (int)((psInfo->dfURY - psInfo->dfLLY + 0.5 * psInfo->dfCellSizeY) /
     103           9 :               psInfo->dfCellSizeY);
     104             : 
     105           9 :     if (psInfo->nPixels <= 0 || psInfo->nLines <= 0)
     106             :     {
     107           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     108             :                  "Invalid raster dimensions : %d x %d", psInfo->nPixels,
     109             :                  psInfo->nLines);
     110           0 :         AIGClose(psInfo);
     111           0 :         return NULL;
     112             :     }
     113             : 
     114           9 :     if (psInfo->nBlockXSize <= 0 || psInfo->nBlockYSize <= 0 ||
     115           9 :         psInfo->nBlocksPerRow <= 0 || psInfo->nBlocksPerColumn <= 0 ||
     116           9 :         psInfo->nBlockXSize > INT_MAX / psInfo->nBlocksPerRow ||
     117           9 :         psInfo->nBlockYSize > INT_MAX / psInfo->nBlocksPerColumn)
     118             :     {
     119           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     120             :                  "Invalid block characteristics: nBlockXSize=%d, "
     121             :                  "nBlockYSize=%d, nBlocksPerRow=%d, nBlocksPerColumn=%d",
     122             :                  psInfo->nBlockXSize, psInfo->nBlockYSize,
     123             :                  psInfo->nBlocksPerRow, psInfo->nBlocksPerColumn);
     124           0 :         AIGClose(psInfo);
     125           0 :         return NULL;
     126             :     }
     127             : 
     128           9 :     if (psInfo->nBlocksPerRow > INT_MAX / psInfo->nBlocksPerColumn)
     129             :     {
     130           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "Too many blocks");
     131           0 :         AIGClose(psInfo);
     132           0 :         return NULL;
     133             :     }
     134             : 
     135           9 :     psInfo->nTileXSize = psInfo->nBlockXSize * psInfo->nBlocksPerRow;
     136           9 :     psInfo->nTileYSize = psInfo->nBlockYSize * psInfo->nBlocksPerColumn;
     137             : 
     138           9 :     psInfo->nTilesPerRow = (psInfo->nPixels - 1) / psInfo->nTileXSize + 1;
     139           9 :     psInfo->nTilesPerColumn = (psInfo->nLines - 1) / psInfo->nTileYSize + 1;
     140             : 
     141             :     /* Each tile map to a file and there are only 3 characters in the */
     142             :     /* filename for the X and Y components. */
     143           9 :     if (psInfo->nTilesPerRow > 1000 * 1000 / psInfo->nTilesPerColumn)
     144             :     {
     145           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Too many tiles");
     146           0 :         psInfo->nTilesPerRow = 0; /* to avoid int32 overflow in AIGClose() */
     147           0 :         psInfo->nTilesPerColumn = 0;
     148           0 :         AIGClose(psInfo);
     149           0 :         return NULL;
     150             :     }
     151             : 
     152             :     /* -------------------------------------------------------------------- */
     153             :     /*      Setup tile infos, but defer reading of tile data.               */
     154             :     /* -------------------------------------------------------------------- */
     155           9 :     psInfo->pasTileInfo = (AIGTileInfo *)VSI_CALLOC_VERBOSE(
     156             :         sizeof(AIGTileInfo),
     157             :         (size_t)psInfo->nTilesPerRow * psInfo->nTilesPerColumn);
     158           9 :     if (psInfo->pasTileInfo == NULL)
     159             :     {
     160           0 :         AIGClose(psInfo);
     161           0 :         return NULL;
     162             :     }
     163             : 
     164             :     /* -------------------------------------------------------------------- */
     165             :     /*      Read the statistics.                                            */
     166             :     /* -------------------------------------------------------------------- */
     167           9 :     if (AIGReadStatistics(pszCoverName, psInfo) != CE_None)
     168             :     {
     169           0 :         AIGClose(psInfo);
     170           0 :         return NULL;
     171             :     }
     172             : 
     173           9 :     return (psInfo);
     174             : }
     175             : 
     176             : /************************************************************************/
     177             : /*                           AIGAccessTile()                            */
     178             : /************************************************************************/
     179             : 
     180           4 : CPLErr AIGAccessTile(AIGInfo_t *psInfo, int iTileX, int iTileY)
     181             : 
     182             : {
     183             :     char szBasename[32];
     184             :     char *pszFilename;
     185             :     AIGTileInfo *psTInfo;
     186           4 :     const size_t nFilenameLen = strlen(psInfo->pszCoverName) + 40;
     187             : 
     188             :     /* -------------------------------------------------------------------- */
     189             :     /*      Identify our tile.                                              */
     190             :     /* -------------------------------------------------------------------- */
     191           4 :     if (iTileX < 0 || iTileX >= psInfo->nTilesPerRow || iTileY < 0 ||
     192           4 :         iTileY >= psInfo->nTilesPerColumn)
     193             :     {
     194           0 :         CPLAssert(FALSE);
     195             :         return CE_Failure;
     196             :     }
     197             : 
     198           4 :     psTInfo = psInfo->pasTileInfo + iTileX + iTileY * psInfo->nTilesPerRow;
     199             : 
     200           4 :     if (psTInfo->fpGrid != NULL)
     201           1 :         return psTInfo->panBlockOffset != NULL ? CE_None : CE_Failure;
     202             : 
     203           3 :     if (psTInfo->bTriedToLoad)
     204           0 :         return CE_None;
     205             : 
     206             : #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
     207             :     /* After a significant number of failed openings, don't even try further */
     208             :     if (psInfo->nFailedOpenings == 1000)
     209             :         return CE_None;
     210             : #endif
     211             : 
     212             :     /* -------------------------------------------------------------------- */
     213             :     /*      Compute the basename.                                           */
     214             :     /* -------------------------------------------------------------------- */
     215           3 :     if (iTileY == 0)
     216           3 :         snprintf(szBasename, sizeof(szBasename), "w%03d001", iTileX + 1);
     217           0 :     else if (iTileY == 1)
     218           0 :         snprintf(szBasename, sizeof(szBasename), "w%03d000", iTileX + 1);
     219             :     else
     220           0 :         snprintf(szBasename, sizeof(szBasename), "z%03d%03d", iTileX + 1,
     221             :                  iTileY - 1);
     222             : 
     223             :     /* -------------------------------------------------------------------- */
     224             :     /*      Open the file w001001.adf file itself.                          */
     225             :     /* -------------------------------------------------------------------- */
     226           3 :     pszFilename = (char *)CPLMalloc(nFilenameLen);
     227           3 :     snprintf(pszFilename, nFilenameLen, "%s/%s.adf", psInfo->pszCoverName,
     228             :              szBasename);
     229             : 
     230           3 :     psTInfo->fpGrid = AIGLLOpen(pszFilename, "rb");
     231           3 :     psTInfo->bTriedToLoad = TRUE;
     232             : 
     233           3 :     if (psTInfo->fpGrid == NULL)
     234             :     {
     235           0 :         psInfo->nFailedOpenings++;
     236           0 :         if (psInfo->nFailedOpenings < 100)
     237             :         {
     238           0 :             CPLError(
     239             :                 CE_Warning, CPLE_OpenFailed,
     240             :                 "Failed to open grid file, assuming region is nodata:\n%s\n",
     241             :                 pszFilename);
     242             :         }
     243             : 
     244           0 :         CPLFree(pszFilename);
     245           0 :         return CE_Warning;
     246             :     }
     247             : 
     248           3 :     CPLFree(pszFilename);
     249           3 :     pszFilename = NULL;
     250             : 
     251             :     /* -------------------------------------------------------------------- */
     252             :     /*      Read the block index file.                                      */
     253             :     /* -------------------------------------------------------------------- */
     254           3 :     return AIGReadBlockIndex(psInfo, psTInfo, szBasename);
     255             : }
     256             : 
     257             : /************************************************************************/
     258             : /*                            AIGReadTile()                             */
     259             : /************************************************************************/
     260             : 
     261           4 : CPLErr AIGReadTile(AIGInfo_t *psInfo, int nBlockXOff, int nBlockYOff,
     262             :                    GInt32 *panData)
     263             : 
     264             : {
     265             :     int nBlockID;
     266             :     CPLErr eErr;
     267             :     int iTileX, iTileY;
     268             :     AIGTileInfo *psTInfo;
     269             : 
     270             :     /* -------------------------------------------------------------------- */
     271             :     /*      Compute our tile, and ensure it is accessible (open).  Then     */
     272             :     /*      reduce block x/y values to be the block within that tile.       */
     273             :     /* -------------------------------------------------------------------- */
     274           4 :     iTileX = nBlockXOff / psInfo->nBlocksPerRow;
     275           4 :     iTileY = nBlockYOff / psInfo->nBlocksPerColumn;
     276             : 
     277           4 :     eErr = AIGAccessTile(psInfo, iTileX, iTileY);
     278           4 :     if (eErr == CE_Failure)
     279           2 :         return eErr;
     280             : 
     281           2 :     psTInfo = psInfo->pasTileInfo + iTileX + iTileY * psInfo->nTilesPerRow;
     282             : 
     283           2 :     nBlockXOff -= iTileX * psInfo->nBlocksPerRow;
     284           2 :     nBlockYOff -= iTileY * psInfo->nBlocksPerColumn;
     285             : 
     286             :     /* -------------------------------------------------------------------- */
     287             :     /*      Request for tile from a file which does not exist - treat as    */
     288             :     /*      all nodata.                                                     */
     289             :     /* -------------------------------------------------------------------- */
     290           2 :     if (psTInfo->fpGrid == NULL)
     291             :     {
     292             :         int i;
     293           0 :         for (i = psInfo->nBlockXSize * psInfo->nBlockYSize - 1; i >= 0; i--)
     294           0 :             panData[i] = ESRI_GRID_NO_DATA;
     295           0 :         return CE_None;
     296             :     }
     297             : 
     298             :     /* -------------------------------------------------------------------- */
     299             :     /*      validate block id.                                              */
     300             :     /* -------------------------------------------------------------------- */
     301           2 :     nBlockID = nBlockXOff + nBlockYOff * psInfo->nBlocksPerRow;
     302           2 :     if (nBlockID < 0 ||
     303           2 :         nBlockID >= psInfo->nBlocksPerRow * psInfo->nBlocksPerColumn)
     304             :     {
     305           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Illegal block requested.");
     306           0 :         return CE_Failure;
     307             :     }
     308             : 
     309           2 :     if (nBlockID >= psTInfo->nBlocks)
     310             :     {
     311             :         int i;
     312           0 :         CPLDebug("AIG",
     313             :                  "Request legal block, but from beyond end of block map.\n"
     314             :                  "Assuming all nodata.");
     315           0 :         for (i = psInfo->nBlockXSize * psInfo->nBlockYSize - 1; i >= 0; i--)
     316           0 :             panData[i] = ESRI_GRID_NO_DATA;
     317           0 :         return CE_None;
     318             :     }
     319             : 
     320             :     /* -------------------------------------------------------------------- */
     321             :     /*      Read block.                                                     */
     322             :     /* -------------------------------------------------------------------- */
     323           2 :     eErr = AIGReadBlock(psTInfo->fpGrid, psTInfo->panBlockOffset[nBlockID],
     324           2 :                         psTInfo->panBlockSize[nBlockID], psInfo->nBlockXSize,
     325             :                         psInfo->nBlockYSize, panData, psInfo->nCellType,
     326             :                         psInfo->bCompressed);
     327             : 
     328             :     /* -------------------------------------------------------------------- */
     329             :     /*      Apply floating point post-processing.                           */
     330             :     /* -------------------------------------------------------------------- */
     331           2 :     if (eErr == CE_None && psInfo->nCellType == AIG_CELLTYPE_FLOAT)
     332             :     {
     333           0 :         float *pafData = (float *)panData;
     334           0 :         int i, nPixels = psInfo->nBlockXSize * psInfo->nBlockYSize;
     335             : 
     336           0 :         for (i = 0; i < nPixels; i++)
     337             :         {
     338           0 :             panData[i] = (int)pafData[i];
     339             :         }
     340             :     }
     341             : 
     342           2 :     return (eErr);
     343             : }
     344             : 
     345             : /************************************************************************/
     346             : /*                          AIGReadFloatTile()                          */
     347             : /************************************************************************/
     348             : 
     349           0 : CPLErr AIGReadFloatTile(AIGInfo_t *psInfo, int nBlockXOff, int nBlockYOff,
     350             :                         float *pafData)
     351             : 
     352             : {
     353             :     int nBlockID;
     354             :     CPLErr eErr;
     355             :     int iTileX, iTileY;
     356             :     AIGTileInfo *psTInfo;
     357             : 
     358             :     /* -------------------------------------------------------------------- */
     359             :     /*      Compute our tile, and ensure it is accessible (open).  Then     */
     360             :     /*      reduce block x/y values to be the block within that tile.       */
     361             :     /* -------------------------------------------------------------------- */
     362           0 :     iTileX = nBlockXOff / psInfo->nBlocksPerRow;
     363           0 :     iTileY = nBlockYOff / psInfo->nBlocksPerColumn;
     364             : 
     365           0 :     eErr = AIGAccessTile(psInfo, iTileX, iTileY);
     366           0 :     if (eErr == CE_Failure)
     367           0 :         return eErr;
     368             : 
     369           0 :     psTInfo = psInfo->pasTileInfo + iTileX + iTileY * psInfo->nTilesPerRow;
     370             : 
     371           0 :     nBlockXOff -= iTileX * psInfo->nBlocksPerRow;
     372           0 :     nBlockYOff -= iTileY * psInfo->nBlocksPerColumn;
     373             : 
     374             :     /* -------------------------------------------------------------------- */
     375             :     /*      Request for tile from a file which does not exist - treat as    */
     376             :     /*      all nodata.                                                     */
     377             :     /* -------------------------------------------------------------------- */
     378           0 :     if (psTInfo->fpGrid == NULL)
     379             :     {
     380             :         int i;
     381           0 :         for (i = psInfo->nBlockXSize * psInfo->nBlockYSize - 1; i >= 0; i--)
     382           0 :             pafData[i] = ESRI_GRID_FLOAT_NO_DATA;
     383           0 :         return CE_None;
     384             :     }
     385             : 
     386             :     /* -------------------------------------------------------------------- */
     387             :     /*      validate block id.                                              */
     388             :     /* -------------------------------------------------------------------- */
     389           0 :     nBlockID = nBlockXOff + nBlockYOff * psInfo->nBlocksPerRow;
     390           0 :     if (nBlockID < 0 ||
     391           0 :         nBlockID >= psInfo->nBlocksPerRow * psInfo->nBlocksPerColumn)
     392             :     {
     393           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Illegal block requested.");
     394           0 :         return CE_Failure;
     395             :     }
     396             : 
     397           0 :     if (nBlockID >= psTInfo->nBlocks)
     398             :     {
     399             :         int i;
     400           0 :         CPLDebug("AIG",
     401             :                  "Request legal block, but from beyond end of block map.\n"
     402             :                  "Assuming all nodata.");
     403           0 :         for (i = psInfo->nBlockXSize * psInfo->nBlockYSize - 1; i >= 0; i--)
     404           0 :             pafData[i] = ESRI_GRID_FLOAT_NO_DATA;
     405           0 :         return CE_None;
     406             :     }
     407             : 
     408             :     /* -------------------------------------------------------------------- */
     409             :     /*      Read block.                                                     */
     410             :     /* -------------------------------------------------------------------- */
     411           0 :     eErr = AIGReadBlock(psTInfo->fpGrid, psTInfo->panBlockOffset[nBlockID],
     412           0 :                         psTInfo->panBlockSize[nBlockID], psInfo->nBlockXSize,
     413             :                         psInfo->nBlockYSize, (GInt32 *)pafData,
     414             :                         psInfo->nCellType, psInfo->bCompressed);
     415             : 
     416             :     /* -------------------------------------------------------------------- */
     417             :     /*      Perform integer post processing.                                */
     418             :     /* -------------------------------------------------------------------- */
     419           0 :     if (eErr == CE_None && psInfo->nCellType == AIG_CELLTYPE_INT)
     420             :     {
     421           0 :         GUInt32 *panData = (GUInt32 *)pafData;
     422           0 :         int i, nPixels = psInfo->nBlockXSize * psInfo->nBlockYSize;
     423             : 
     424           0 :         for (i = 0; i < nPixels; i++)
     425             :         {
     426           0 :             pafData[i] = (float)panData[i];
     427             :         }
     428             :     }
     429             : 
     430           0 :     return (eErr);
     431             : }
     432             : 
     433             : /************************************************************************/
     434             : /*                              AIGClose()                              */
     435             : /************************************************************************/
     436             : 
     437           9 : void AIGClose(AIGInfo_t *psInfo)
     438             : 
     439             : {
     440           9 :     if (psInfo->pasTileInfo != NULL)
     441             :     {
     442           9 :         int nTileCount = psInfo->nTilesPerRow * psInfo->nTilesPerColumn;
     443             :         int iTile;
     444             : 
     445          18 :         for (iTile = 0; iTile < nTileCount; iTile++)
     446             :         {
     447           9 :             if (psInfo->pasTileInfo[iTile].fpGrid)
     448             :             {
     449           3 :                 CPL_IGNORE_RET_VAL_INT(
     450           3 :                     VSIFCloseL(psInfo->pasTileInfo[iTile].fpGrid));
     451             : 
     452           3 :                 CPLFree(psInfo->pasTileInfo[iTile].panBlockOffset);
     453           3 :                 CPLFree(psInfo->pasTileInfo[iTile].panBlockSize);
     454             :             }
     455             :         }
     456             :     }
     457             : 
     458           9 :     CPLFree(psInfo->pasTileInfo);
     459           9 :     CPLFree(psInfo->pszCoverName);
     460           9 :     CPLFree(psInfo);
     461           9 : }
     462             : 
     463             : /************************************************************************/
     464             : /*                             AIGLLOpen()                              */
     465             : /*                                                                      */
     466             : /*      Low level fopen() replacement that will try provided, and       */
     467             : /*      upper cased versions of file names.                             */
     468             : /************************************************************************/
     469             : 
     470          33 : VSILFILE *AIGLLOpen(const char *pszFilename, const char *pszAccess)
     471             : 
     472             : {
     473             :     VSILFILE *fp;
     474             : 
     475          33 :     fp = VSIFOpenL(pszFilename, pszAccess);
     476          33 :     if (fp == NULL)
     477             :     {
     478          11 :         char *pszUCFilename = CPLStrdup(pszFilename);
     479             :         int i;
     480             : 
     481          11 :         for (i = (int)strlen(pszUCFilename) - 1;
     482         106 :              pszUCFilename[i] != '/' && pszUCFilename[i] != '\\'; i--)
     483             :         {
     484          95 :             pszUCFilename[i] = (char)toupper((unsigned char)(pszUCFilename[i]));
     485             :         }
     486             : 
     487          11 :         fp = VSIFOpenL(pszUCFilename, pszAccess);
     488             : 
     489          11 :         CPLFree(pszUCFilename);
     490             :     }
     491             : 
     492          33 :     return fp;
     493             : }

Generated by: LCOV version 1.14