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 : }
|