Coverage Report

Created: 2026-09-26 08:22

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