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