Line data Source code
1 : /******************************************************************************
2 : * $Id$
3 : *
4 : * Project: Arc/Info Binary Grid Translator
5 : * Purpose: Grid file reading code.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 1999, Frank Warmerdam
10 : * Copyright (c) 2007-2010, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : ****************************************************************************/
30 :
31 : #include "aigrid.h"
32 :
33 : #ifndef CPL_IGNORE_RET_VAL_INT_defined
34 : #define CPL_IGNORE_RET_VAL_INT_defined
35 :
36 30 : CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused)
37 : {
38 30 : }
39 : #endif
40 :
41 : /************************************************************************/
42 : /* AIGProcessRaw32bitFloatBlock() */
43 : /* */
44 : /* Process a block using ``00'' (32 bit) raw format. */
45 : /************************************************************************/
46 :
47 0 : static CPLErr AIGProcessRaw32BitFloatBlock(GByte *pabyCur, int nDataSize,
48 : int nMin, int nBlockXSize,
49 : int nBlockYSize, float *pafData)
50 :
51 : {
52 : int i;
53 :
54 : (void)nMin;
55 0 : if (nDataSize < nBlockXSize * nBlockYSize * 4)
56 : {
57 0 : CPLError(CE_Failure, CPLE_AppDefined, "Block too small");
58 0 : return CE_Failure;
59 : }
60 :
61 : /* -------------------------------------------------------------------- */
62 : /* Collect raw data. */
63 : /* -------------------------------------------------------------------- */
64 0 : for (i = 0; i < nBlockXSize * nBlockYSize; i++)
65 : {
66 : float fWork;
67 :
68 : #ifdef CPL_LSB
69 0 : ((GByte *)&fWork)[3] = *(pabyCur++);
70 0 : ((GByte *)&fWork)[2] = *(pabyCur++);
71 0 : ((GByte *)&fWork)[1] = *(pabyCur++);
72 0 : ((GByte *)&fWork)[0] = *(pabyCur++);
73 : #else
74 : ((GByte *)&fWork)[0] = *(pabyCur++);
75 : ((GByte *)&fWork)[1] = *(pabyCur++);
76 : ((GByte *)&fWork)[2] = *(pabyCur++);
77 : ((GByte *)&fWork)[3] = *(pabyCur++);
78 : #endif
79 :
80 0 : pafData[i] = fWork;
81 : }
82 :
83 0 : return (CE_None);
84 : }
85 :
86 : /************************************************************************/
87 : /* AIGProcessIntConstBlock() */
88 : /* */
89 : /* Process a block using ``00'' constant 32bit integer format. */
90 : /************************************************************************/
91 :
92 0 : static CPLErr AIGProcessIntConstBlock(GByte *pabyCur, int nDataSize, int nMin,
93 : int nBlockXSize, int nBlockYSize,
94 : GInt32 *panData)
95 :
96 : {
97 : int i;
98 :
99 : (void)pabyCur;
100 : (void)nDataSize;
101 :
102 : /* -------------------------------------------------------------------- */
103 : /* Apply constant min value. */
104 : /* -------------------------------------------------------------------- */
105 0 : for (i = 0; i < nBlockXSize * nBlockYSize; i++)
106 0 : panData[i] = nMin;
107 :
108 0 : return (CE_None);
109 : }
110 :
111 : /************************************************************************/
112 : /* AIGRolloverSignedAdd() */
113 : /************************************************************************/
114 :
115 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
116 6 : static GInt32 AIGRolloverSignedAdd(GInt32 a, GInt32 b)
117 : {
118 : // Not really portable as assumes complement to 2 representation
119 : // but AIG assumes typical unsigned rollover on signed
120 : // integer operations.
121 : GInt32 res;
122 6 : GUInt32 resUnsigned = (GUInt32)(a) + (GUInt32)(b);
123 6 : memcpy(&res, &resUnsigned, sizeof(res));
124 6 : return res;
125 : }
126 :
127 : /************************************************************************/
128 : /* AIGProcess32bitRawBlock() */
129 : /* */
130 : /* Process a block using ``20'' (thirty two bit) raw format. */
131 : /************************************************************************/
132 :
133 0 : static CPLErr AIGProcessRaw32BitBlock(GByte *pabyCur, int nDataSize, int nMin,
134 : int nBlockXSize, int nBlockYSize,
135 : GInt32 *panData)
136 :
137 : {
138 : int i;
139 :
140 0 : if (nDataSize < nBlockXSize * nBlockYSize * 4)
141 : {
142 0 : CPLError(CE_Failure, CPLE_AppDefined, "Block too small");
143 0 : return CE_Failure;
144 : }
145 :
146 : /* -------------------------------------------------------------------- */
147 : /* Collect raw data. */
148 : /* -------------------------------------------------------------------- */
149 0 : for (i = 0; i < nBlockXSize * nBlockYSize; i++)
150 : {
151 0 : memcpy(panData + i, pabyCur, 4);
152 0 : panData[i] = CPL_MSBWORD32(panData[i]);
153 0 : panData[i] = AIGRolloverSignedAdd(panData[i], nMin);
154 0 : pabyCur += 4;
155 : }
156 :
157 0 : return (CE_None);
158 : }
159 :
160 : /************************************************************************/
161 : /* AIGProcess16bitRawBlock() */
162 : /* */
163 : /* Process a block using ``10'' (sixteen bit) raw format. */
164 : /************************************************************************/
165 :
166 0 : static CPLErr AIGProcessRaw16BitBlock(GByte *pabyCur, int nDataSize, int nMin,
167 : int nBlockXSize, int nBlockYSize,
168 : GInt32 *panData)
169 :
170 : {
171 : int i;
172 :
173 0 : if (nDataSize < nBlockXSize * nBlockYSize * 2)
174 : {
175 0 : CPLError(CE_Failure, CPLE_AppDefined, "Block too small");
176 0 : return CE_Failure;
177 : }
178 :
179 : /* -------------------------------------------------------------------- */
180 : /* Collect raw data. */
181 : /* -------------------------------------------------------------------- */
182 0 : for (i = 0; i < nBlockXSize * nBlockYSize; i++)
183 : {
184 0 : panData[i] = AIGRolloverSignedAdd(pabyCur[0] * 256 + pabyCur[1], nMin);
185 0 : pabyCur += 2;
186 : }
187 :
188 0 : return (CE_None);
189 : }
190 :
191 : /************************************************************************/
192 : /* AIGProcess4BitRawBlock() */
193 : /* */
194 : /* Process a block using ``08'' raw format. */
195 : /************************************************************************/
196 :
197 0 : static CPLErr AIGProcessRaw4BitBlock(GByte *pabyCur, int nDataSize, int nMin,
198 : int nBlockXSize, int nBlockYSize,
199 : GInt32 *panData)
200 :
201 : {
202 : int i;
203 :
204 0 : if (nDataSize < (nBlockXSize * nBlockYSize + 1) / 2)
205 : {
206 0 : CPLError(CE_Failure, CPLE_AppDefined, "Block too small");
207 0 : return CE_Failure;
208 : }
209 :
210 : /* -------------------------------------------------------------------- */
211 : /* Collect raw data. */
212 : /* -------------------------------------------------------------------- */
213 0 : for (i = 0; i < nBlockXSize * nBlockYSize; i++)
214 : {
215 0 : if (i % 2 == 0)
216 0 : panData[i] = AIGRolloverSignedAdd((*(pabyCur)&0xf0) >> 4, nMin);
217 : else
218 0 : panData[i] = AIGRolloverSignedAdd(*(pabyCur++) & 0xf, nMin);
219 : }
220 :
221 0 : return (CE_None);
222 : }
223 :
224 : /************************************************************************/
225 : /* AIGProcess1BitRawBlock() */
226 : /* */
227 : /* Process a block using ``0x01'' raw format. */
228 : /************************************************************************/
229 :
230 0 : static CPLErr AIGProcessRaw1BitBlock(GByte *pabyCur, int nDataSize, int nMin,
231 : int nBlockXSize, int nBlockYSize,
232 : GInt32 *panData)
233 :
234 : {
235 : int i;
236 :
237 0 : if (nDataSize < (nBlockXSize * nBlockYSize + 7) / 8)
238 : {
239 0 : CPLError(CE_Failure, CPLE_AppDefined, "Block too small");
240 0 : return CE_Failure;
241 : }
242 :
243 : /* -------------------------------------------------------------------- */
244 : /* Collect raw data. */
245 : /* -------------------------------------------------------------------- */
246 0 : for (i = 0; i < nBlockXSize * nBlockYSize; i++)
247 : {
248 0 : if (pabyCur[i >> 3] & (0x80 >> (i & 0x7)))
249 0 : panData[i] = AIGRolloverSignedAdd(1, nMin);
250 : else
251 0 : panData[i] = 0 + nMin;
252 : }
253 :
254 0 : return (CE_None);
255 : }
256 :
257 : /************************************************************************/
258 : /* AIGProcessRawBlock() */
259 : /* */
260 : /* Process a block using ``08'' raw format. */
261 : /************************************************************************/
262 :
263 0 : static CPLErr AIGProcessRawBlock(GByte *pabyCur, int nDataSize, int nMin,
264 : int nBlockXSize, int nBlockYSize,
265 : GInt32 *panData)
266 :
267 : {
268 : int i;
269 :
270 0 : if (nDataSize < nBlockXSize * nBlockYSize)
271 : {
272 0 : CPLError(CE_Failure, CPLE_AppDefined, "Block too small");
273 0 : return CE_Failure;
274 : }
275 :
276 : /* -------------------------------------------------------------------- */
277 : /* Collect raw data. */
278 : /* -------------------------------------------------------------------- */
279 0 : for (i = 0; i < nBlockXSize * nBlockYSize; i++)
280 : {
281 0 : panData[i] = AIGRolloverSignedAdd(*(pabyCur++), nMin);
282 : }
283 :
284 0 : return (CE_None);
285 : }
286 :
287 : /************************************************************************/
288 : /* AIGProcessFFBlock() */
289 : /* */
290 : /* Process a type 0xFF (CCITT RLE) compressed block. */
291 : /************************************************************************/
292 :
293 0 : static CPLErr AIGProcessFFBlock(GByte *pabyCur, int nDataSize, int nMin,
294 : int nBlockXSize, int nBlockYSize,
295 : GInt32 *panData)
296 :
297 : {
298 : /* -------------------------------------------------------------------- */
299 : /* Convert CCITT compress bitstream into 1bit raw data. */
300 : /* -------------------------------------------------------------------- */
301 : CPLErr eErr;
302 0 : int i, nDstBytes = (nBlockXSize * nBlockYSize + 7) / 8;
303 : unsigned char *pabyIntermediate;
304 :
305 0 : pabyIntermediate = (unsigned char *)VSI_MALLOC_VERBOSE(nDstBytes);
306 0 : if (pabyIntermediate == NULL)
307 : {
308 0 : return CE_Failure;
309 : }
310 :
311 0 : eErr = DecompressCCITTRLETile(pabyCur, nDataSize, pabyIntermediate,
312 : nDstBytes, nBlockXSize, nBlockYSize);
313 0 : if (eErr != CE_None)
314 : {
315 0 : CPLFree(pabyIntermediate);
316 0 : return eErr;
317 : }
318 :
319 : /* -------------------------------------------------------------------- */
320 : /* Convert the bit buffer into 32bit integers and account for */
321 : /* nMin. */
322 : /* -------------------------------------------------------------------- */
323 0 : for (i = 0; i < nBlockXSize * nBlockYSize; i++)
324 : {
325 0 : if (pabyIntermediate[i >> 3] & (0x80 >> (i & 0x7)))
326 0 : panData[i] = AIGRolloverSignedAdd(nMin, 1);
327 : else
328 0 : panData[i] = nMin;
329 : }
330 :
331 0 : CPLFree(pabyIntermediate);
332 :
333 0 : return (CE_None);
334 : }
335 :
336 : /************************************************************************/
337 : /* AIGProcessBlock() */
338 : /* */
339 : /* Process a block using ``D7'', ``E0'' or ``DF'' compression. */
340 : /************************************************************************/
341 :
342 2 : static CPLErr AIGProcessBlock(GByte *pabyCur, int nDataSize, int nMin,
343 : int nMagic, int nBlockXSize, int nBlockYSize,
344 : GInt32 *panData)
345 :
346 : {
347 : int nTotPixels, nPixels;
348 : int i;
349 :
350 : /* ==================================================================== */
351 : /* Process runs till we are done. */
352 : /* ==================================================================== */
353 2 : nTotPixels = nBlockXSize * nBlockYSize;
354 2 : nPixels = 0;
355 :
356 22 : while (nPixels < nTotPixels && nDataSize > 0)
357 : {
358 20 : int nMarker = *(pabyCur++);
359 :
360 20 : nDataSize--;
361 :
362 : /* --------------------------------------------------------------------
363 : */
364 : /* Repeat data - four byte data block (0xE0) */
365 : /* --------------------------------------------------------------------
366 : */
367 20 : if (nMagic == 0xE0)
368 : {
369 : GInt32 nValue;
370 :
371 0 : if (nMarker + nPixels > nTotPixels)
372 : {
373 0 : CPLError(CE_Failure, CPLE_AppDefined,
374 : "Run too long in AIGProcessBlock, needed %d values, "
375 : "got %d.",
376 : nTotPixels - nPixels, nMarker);
377 0 : return CE_Failure;
378 : }
379 :
380 0 : if (nDataSize < 4)
381 : {
382 0 : CPLError(CE_Failure, CPLE_AppDefined, "Block too small");
383 0 : return CE_Failure;
384 : }
385 :
386 0 : nValue = 0;
387 0 : memcpy(&nValue, pabyCur, 4);
388 0 : pabyCur += 4;
389 0 : nDataSize -= 4;
390 :
391 0 : nValue = CPL_MSBWORD32(nValue);
392 0 : nValue = AIGRolloverSignedAdd(nValue, nMin);
393 0 : for (i = 0; i < nMarker; i++)
394 0 : panData[nPixels++] = nValue;
395 : }
396 :
397 : /* --------------------------------------------------------------------
398 : */
399 : /* Repeat data - two byte data block (0xF0) */
400 : /* --------------------------------------------------------------------
401 : */
402 20 : else if (nMagic == 0xF0)
403 : {
404 : GInt32 nValue;
405 :
406 0 : if (nMarker + nPixels > nTotPixels)
407 : {
408 0 : CPLError(CE_Failure, CPLE_AppDefined,
409 : "Run too long in AIGProcessBlock, needed %d values, "
410 : "got %d.",
411 : nTotPixels - nPixels, nMarker);
412 0 : return CE_Failure;
413 : }
414 :
415 0 : if (nDataSize < 2)
416 : {
417 0 : CPLError(CE_Failure, CPLE_AppDefined, "Block too small");
418 0 : return CE_Failure;
419 : }
420 :
421 0 : nValue = AIGRolloverSignedAdd(pabyCur[0] * 256 + pabyCur[1], nMin);
422 0 : pabyCur += 2;
423 0 : nDataSize -= 2;
424 :
425 0 : for (i = 0; i < nMarker; i++)
426 0 : panData[nPixels++] = nValue;
427 : }
428 :
429 : /* --------------------------------------------------------------------
430 : */
431 : /* Repeat data - one byte data block (0xFC) */
432 : /* --------------------------------------------------------------------
433 : */
434 20 : else if (nMagic == 0xFC || nMagic == 0xF8)
435 0 : {
436 : GInt32 nValue;
437 :
438 0 : if (nMarker + nPixels > nTotPixels)
439 : {
440 0 : CPLError(CE_Failure, CPLE_AppDefined,
441 : "Run too long in AIGProcessBlock, needed %d values, "
442 : "got %d.",
443 : nTotPixels - nPixels, nMarker);
444 0 : return CE_Failure;
445 : }
446 :
447 0 : if (nDataSize < 1)
448 : {
449 0 : CPLError(CE_Failure, CPLE_AppDefined, "Block too small");
450 0 : return CE_Failure;
451 : }
452 :
453 0 : nValue = AIGRolloverSignedAdd(*(pabyCur++), nMin);
454 0 : nDataSize--;
455 :
456 0 : for (i = 0; i < nMarker; i++)
457 0 : panData[nPixels++] = nValue;
458 : }
459 :
460 : /* --------------------------------------------------------------------
461 : */
462 : /* Repeat data - no actual data, just assign minimum (0xDF) */
463 : /* --------------------------------------------------------------------
464 : */
465 20 : else if (nMagic == 0xDF && nMarker < 128)
466 : {
467 0 : if (nMarker + nPixels > nTotPixels)
468 : {
469 0 : CPLError(CE_Failure, CPLE_AppDefined,
470 : "Run too long in AIGProcessBlock, needed %d values, "
471 : "got %d.",
472 : nTotPixels - nPixels, nMarker);
473 0 : return CE_Failure;
474 : }
475 :
476 0 : for (i = 0; i < nMarker; i++)
477 0 : panData[nPixels++] = nMin;
478 : }
479 :
480 : /* --------------------------------------------------------------------
481 : */
482 : /* Literal data (0xD7): 8bit values. */
483 : /* --------------------------------------------------------------------
484 : */
485 20 : else if (nMagic == 0xD7 && nMarker < 128)
486 : {
487 2 : if (nMarker + nPixels > nTotPixels)
488 : {
489 0 : CPLError(CE_Failure, CPLE_AppDefined,
490 : "Run too long in AIGProcessBlock, needed %d values, "
491 : "got %d.",
492 : nTotPixels - nPixels, nMarker);
493 0 : return CE_Failure;
494 : }
495 :
496 8 : while (nMarker > 0 && nDataSize > 0)
497 : {
498 6 : panData[nPixels++] = AIGRolloverSignedAdd(*(pabyCur++), nMin);
499 6 : nMarker--;
500 6 : nDataSize--;
501 : }
502 : }
503 :
504 : /* --------------------------------------------------------------------
505 : */
506 : /* Literal data (0xCF): 16 bit values. */
507 : /* --------------------------------------------------------------------
508 : */
509 18 : else if (nMagic == 0xCF && nMarker < 128)
510 0 : {
511 : GInt32 nValue;
512 :
513 0 : if (nMarker + nPixels > nTotPixels)
514 : {
515 0 : CPLError(CE_Failure, CPLE_AppDefined,
516 : "Run too long in AIGProcessBlock, needed %d values, "
517 : "got %d.",
518 : nTotPixels - nPixels, nMarker);
519 0 : return CE_Failure;
520 : }
521 :
522 0 : while (nMarker > 0 && nDataSize >= 2)
523 : {
524 : nValue =
525 0 : AIGRolloverSignedAdd(pabyCur[0] * 256 + pabyCur[1], nMin);
526 0 : panData[nPixels++] = nValue;
527 0 : pabyCur += 2;
528 :
529 0 : nMarker--;
530 0 : nDataSize -= 2;
531 : }
532 : }
533 :
534 : /* --------------------------------------------------------------------
535 : */
536 : /* Nodata repeat */
537 : /* --------------------------------------------------------------------
538 : */
539 18 : else if (nMarker > 128)
540 : {
541 18 : nMarker = 256 - nMarker;
542 :
543 18 : if (nMarker + nPixels > nTotPixels)
544 : {
545 0 : CPLError(CE_Failure, CPLE_AppDefined,
546 : "Run too long in AIGProcessBlock, needed %d values, "
547 : "got %d.",
548 : nTotPixels - nPixels, nMarker);
549 0 : return CE_Failure;
550 : }
551 :
552 2060 : while (nMarker > 0)
553 : {
554 2042 : panData[nPixels++] = ESRI_GRID_NO_DATA;
555 2042 : nMarker--;
556 : }
557 : }
558 :
559 : else
560 : {
561 0 : return CE_Failure;
562 : }
563 : }
564 :
565 2 : if (nPixels < nTotPixels || nDataSize < 0)
566 : {
567 0 : CPLError(CE_Failure, CPLE_AppDefined,
568 : "Ran out of data processing block with nMagic=%d.", nMagic);
569 0 : return CE_Failure;
570 : }
571 :
572 2 : return CE_None;
573 : }
574 :
575 : /************************************************************************/
576 : /* AIGReadBlock() */
577 : /* */
578 : /* Read a single block of integer grid data. */
579 : /************************************************************************/
580 :
581 2 : CPLErr AIGReadBlock(VSILFILE *fp, GUInt32 nBlockOffset, int nBlockSize,
582 : int nBlockXSize, int nBlockYSize, GInt32 *panData,
583 : int nCellType, int bCompressed)
584 :
585 : {
586 : GByte *pabyRaw, *pabyCur;
587 : CPLErr eErr;
588 2 : int i, nMagic, nMinSize = 0, nDataSize;
589 2 : GInt32 nMin = 0;
590 :
591 : /* -------------------------------------------------------------------- */
592 : /* If the block has zero size it is all dummies. */
593 : /* -------------------------------------------------------------------- */
594 2 : if (nBlockSize == 0)
595 : {
596 0 : for (i = 0; i < nBlockXSize * nBlockYSize; i++)
597 0 : panData[i] = ESRI_GRID_NO_DATA;
598 :
599 0 : return (CE_None);
600 : }
601 :
602 : /* -------------------------------------------------------------------- */
603 : /* Read the block into memory. */
604 : /* -------------------------------------------------------------------- */
605 2 : if (nBlockSize <= 0 || nBlockSize > 65535 * 2)
606 : {
607 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid block size : %d",
608 : nBlockSize);
609 0 : return CE_Failure;
610 : }
611 :
612 2 : pabyRaw = (GByte *)VSIMalloc(nBlockSize + 2);
613 2 : if (pabyRaw == NULL)
614 : {
615 0 : CPLError(CE_Failure, CPLE_AppDefined,
616 : "Cannot allocate memory for block");
617 0 : return CE_Failure;
618 : }
619 :
620 4 : if (VSIFSeekL(fp, nBlockOffset, SEEK_SET) != 0 ||
621 2 : VSIFReadL(pabyRaw, nBlockSize + 2, 1, fp) != 1)
622 : {
623 0 : memset(panData, 0, sizeof(int32_t) * nBlockXSize * nBlockYSize);
624 0 : CPLError(CE_Failure, CPLE_AppDefined,
625 : "Read of %d bytes from offset %d for grid block failed.",
626 : nBlockSize + 2, nBlockOffset);
627 0 : CPLFree(pabyRaw);
628 0 : return CE_Failure;
629 : }
630 :
631 : /* -------------------------------------------------------------------- */
632 : /* Verify the block size. */
633 : /* -------------------------------------------------------------------- */
634 2 : if (nBlockSize != (pabyRaw[0] * 256 + pabyRaw[1]) * 2)
635 : {
636 0 : memset(panData, 0, sizeof(int32_t) * nBlockXSize * nBlockYSize);
637 0 : CPLError(CE_Failure, CPLE_AppDefined,
638 : "Block is corrupt, block size was %d, but expected to be %d.",
639 0 : (pabyRaw[0] * 256 + pabyRaw[1]) * 2, nBlockSize);
640 0 : CPLFree(pabyRaw);
641 0 : return CE_Failure;
642 : }
643 :
644 2 : nDataSize = nBlockSize;
645 :
646 : /* -------------------------------------------------------------------- */
647 : /* Handle float files and uncompressed integer files directly. */
648 : /* -------------------------------------------------------------------- */
649 2 : if (nCellType == AIG_CELLTYPE_FLOAT)
650 : {
651 0 : AIGProcessRaw32BitFloatBlock(pabyRaw + 2, nDataSize, 0, nBlockXSize,
652 : nBlockYSize, (float *)panData);
653 0 : CPLFree(pabyRaw);
654 :
655 0 : return CE_None;
656 : }
657 :
658 2 : if (nCellType == AIG_CELLTYPE_INT && !bCompressed)
659 : {
660 0 : AIGProcessRaw32BitBlock(pabyRaw + 2, nDataSize, nMin, nBlockXSize,
661 : nBlockYSize, panData);
662 0 : CPLFree(pabyRaw);
663 0 : return CE_None;
664 : }
665 :
666 : /* -------------------------------------------------------------------- */
667 : /* Collect minimum value. */
668 : /* -------------------------------------------------------------------- */
669 :
670 : /* The first 2 bytes that give the block size are not included in nDataSize
671 : */
672 : /* and have already been safely read */
673 2 : pabyCur = pabyRaw + 2;
674 :
675 : /* Need at least 2 byte to read the nMinSize and the nMagic */
676 2 : if (nDataSize < 2)
677 : {
678 0 : CPLError(CE_Failure, CPLE_AppDefined,
679 : "Corrupt block. Need 2 bytes to read nMagic and nMinSize, "
680 : "only %d available",
681 : nDataSize);
682 0 : CPLFree(pabyRaw);
683 0 : return CE_Failure;
684 : }
685 2 : nMagic = pabyCur[0];
686 2 : nMinSize = pabyCur[1];
687 2 : pabyCur += 2;
688 2 : nDataSize -= 2;
689 :
690 : /* Need at least nMinSize bytes to read the nMin value */
691 2 : if (nDataSize < nMinSize)
692 : {
693 0 : CPLError(CE_Failure, CPLE_AppDefined,
694 : "Corrupt block. Need %d bytes to read nMin. Only %d available",
695 : nMinSize, nDataSize);
696 0 : CPLFree(pabyRaw);
697 0 : return CE_Failure;
698 : }
699 :
700 2 : if (nMinSize > 4)
701 : {
702 0 : memset(panData, 0, sizeof(int32_t) * nBlockXSize * nBlockYSize);
703 0 : CPLError(CE_Failure, CPLE_AppDefined,
704 : "Corrupt 'minsize' of %d in block header. Read aborted.",
705 : nMinSize);
706 0 : CPLFree(pabyRaw);
707 0 : return CE_Failure;
708 : }
709 :
710 2 : if (nMinSize == 4)
711 : {
712 0 : memcpy(&nMin, pabyCur, 4);
713 0 : nMin = CPL_MSBWORD32(nMin);
714 0 : pabyCur += 4;
715 : }
716 : else
717 : {
718 2 : nMin = 0;
719 2 : for (i = 0; i < nMinSize; i++)
720 : {
721 0 : nMin = nMin * 256 + *pabyCur;
722 0 : pabyCur++;
723 : }
724 :
725 : /* If nMinSize = 0, then we might have only 4 bytes in pabyRaw */
726 : /* don't try to read the 5th one then */
727 2 : if (nMinSize != 0 && pabyRaw[4] > 127)
728 : {
729 0 : if (nMinSize == 2)
730 0 : nMin = nMin - 65536;
731 0 : else if (nMinSize == 1)
732 0 : nMin = nMin - 256;
733 0 : else if (nMinSize == 3)
734 0 : nMin = nMin - 256 * 256 * 256;
735 : }
736 : }
737 :
738 2 : nDataSize -= nMinSize;
739 :
740 : /* -------------------------------------------------------------------- */
741 : /* Call an appropriate handler depending on magic code. */
742 : /* -------------------------------------------------------------------- */
743 2 : eErr = CE_None;
744 2 : if (nMagic == 0x08)
745 : {
746 0 : AIGProcessRawBlock(pabyCur, nDataSize, nMin, nBlockXSize, nBlockYSize,
747 : panData);
748 : }
749 2 : else if (nMagic == 0x04)
750 : {
751 0 : AIGProcessRaw4BitBlock(pabyCur, nDataSize, nMin, nBlockXSize,
752 : nBlockYSize, panData);
753 : }
754 2 : else if (nMagic == 0x01)
755 : {
756 0 : AIGProcessRaw1BitBlock(pabyCur, nDataSize, nMin, nBlockXSize,
757 : nBlockYSize, panData);
758 : }
759 2 : else if (nMagic == 0x00)
760 : {
761 0 : AIGProcessIntConstBlock(pabyCur, nDataSize, nMin, nBlockXSize,
762 : nBlockYSize, panData);
763 : }
764 2 : else if (nMagic == 0x10)
765 : {
766 0 : AIGProcessRaw16BitBlock(pabyCur, nDataSize, nMin, nBlockXSize,
767 : nBlockYSize, panData);
768 : }
769 2 : else if (nMagic == 0x20)
770 : {
771 0 : AIGProcessRaw32BitBlock(pabyCur, nDataSize, nMin, nBlockXSize,
772 : nBlockYSize, panData);
773 : }
774 2 : else if (nMagic == 0xFF)
775 : {
776 0 : eErr = AIGProcessFFBlock(pabyCur, nDataSize, nMin, nBlockXSize,
777 : nBlockYSize, panData);
778 : }
779 : else
780 : {
781 2 : eErr = AIGProcessBlock(pabyCur, nDataSize, nMin, nMagic, nBlockXSize,
782 : nBlockYSize, panData);
783 :
784 2 : if (eErr == CE_Failure)
785 : {
786 : static int bHasWarned = FALSE;
787 :
788 0 : for (i = 0; i < nBlockXSize * nBlockYSize; i++)
789 0 : panData[i] = ESRI_GRID_NO_DATA;
790 :
791 0 : if (!bHasWarned)
792 : {
793 0 : CPLError(CE_Warning, CPLE_AppDefined,
794 : "Unsupported Arc/Info Binary Grid tile of type 0x%X"
795 : " encountered.\n"
796 : "This and subsequent unsupported tile types set to"
797 : " no data value.\n",
798 : nMagic);
799 0 : bHasWarned = TRUE;
800 : }
801 : }
802 : }
803 :
804 2 : CPLFree(pabyRaw);
805 :
806 2 : return eErr;
807 : }
808 :
809 : /************************************************************************/
810 : /* AIGReadHeader() */
811 : /* */
812 : /* Read the hdr.adf file, and populate the given info structure */
813 : /* appropriately. */
814 : /************************************************************************/
815 :
816 9 : CPLErr AIGReadHeader(const char *pszCoverName, AIGInfo_t *psInfo)
817 :
818 : {
819 : char *pszHDRFilename;
820 : VSILFILE *fp;
821 : GByte abyData[308];
822 9 : const size_t nHDRFilenameLen = strlen(pszCoverName) + 30;
823 :
824 : /* -------------------------------------------------------------------- */
825 : /* Open the file hdr.adf file. */
826 : /* -------------------------------------------------------------------- */
827 9 : pszHDRFilename = (char *)CPLMalloc(nHDRFilenameLen);
828 9 : snprintf(pszHDRFilename, nHDRFilenameLen, "%s/hdr.adf", pszCoverName);
829 :
830 9 : fp = AIGLLOpen(pszHDRFilename, "rb");
831 :
832 9 : if (fp == NULL)
833 : {
834 0 : CPLError(CE_Failure, CPLE_OpenFailed,
835 : "Failed to open grid header file:\n%s\n", pszHDRFilename);
836 :
837 0 : CPLFree(pszHDRFilename);
838 0 : return (CE_Failure);
839 : }
840 :
841 9 : CPLFree(pszHDRFilename);
842 :
843 : /* -------------------------------------------------------------------- */
844 : /* Read the whole file (we expect it to always be 308 bytes */
845 : /* long. */
846 : /* -------------------------------------------------------------------- */
847 :
848 9 : if (VSIFReadL(abyData, 1, 308, fp) != 308)
849 : {
850 0 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
851 0 : return (CE_Failure);
852 : }
853 :
854 9 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
855 :
856 : /* -------------------------------------------------------------------- */
857 : /* Read the block size information. */
858 : /* -------------------------------------------------------------------- */
859 9 : memcpy(&(psInfo->nCellType), abyData + 16, 4);
860 9 : memcpy(&(psInfo->bCompressed), abyData + 20, 4);
861 9 : memcpy(&(psInfo->nBlocksPerRow), abyData + 288, 4);
862 9 : memcpy(&(psInfo->nBlocksPerColumn), abyData + 292, 4);
863 9 : memcpy(&(psInfo->nBlockXSize), abyData + 296, 4);
864 9 : memcpy(&(psInfo->nBlockYSize), abyData + 304, 4);
865 9 : memcpy(&(psInfo->dfCellSizeX), abyData + 256, 8);
866 9 : memcpy(&(psInfo->dfCellSizeY), abyData + 264, 8);
867 :
868 : #ifdef CPL_LSB
869 9 : psInfo->nCellType = CPL_SWAP32(psInfo->nCellType);
870 9 : psInfo->bCompressed = CPL_SWAP32(psInfo->bCompressed);
871 9 : psInfo->nBlocksPerRow = CPL_SWAP32(psInfo->nBlocksPerRow);
872 9 : psInfo->nBlocksPerColumn = CPL_SWAP32(psInfo->nBlocksPerColumn);
873 9 : psInfo->nBlockXSize = CPL_SWAP32(psInfo->nBlockXSize);
874 9 : psInfo->nBlockYSize = CPL_SWAP32(psInfo->nBlockYSize);
875 9 : CPL_SWAPDOUBLE(&(psInfo->dfCellSizeX));
876 9 : CPL_SWAPDOUBLE(&(psInfo->dfCellSizeY));
877 : #endif
878 :
879 9 : psInfo->bCompressed = !psInfo->bCompressed;
880 :
881 9 : return (CE_None);
882 : }
883 :
884 : /************************************************************************/
885 : /* AIGReadBlockIndex() */
886 : /* */
887 : /* Read the w001001x.adf file, and populate the given info */
888 : /* structure with the block offsets, and sizes. */
889 : /************************************************************************/
890 :
891 3 : CPLErr AIGReadBlockIndex(AIGInfo_t *psInfo, AIGTileInfo *psTInfo,
892 : const char *pszBasename)
893 :
894 : {
895 : char *pszHDRFilename;
896 : VSILFILE *fp;
897 : int i;
898 : GUInt32 nValue, nLength;
899 : GUInt32 *panIndex;
900 : GByte abyHeader[8];
901 3 : const size_t nHDRFilenameLen = strlen(psInfo->pszCoverName) + 40;
902 :
903 : /* -------------------------------------------------------------------- */
904 : /* Open the file hdr.adf file. */
905 : /* -------------------------------------------------------------------- */
906 3 : pszHDRFilename = (char *)CPLMalloc(nHDRFilenameLen);
907 3 : snprintf(pszHDRFilename, nHDRFilenameLen, "%s/%sx.adf",
908 : psInfo->pszCoverName, pszBasename);
909 :
910 3 : fp = AIGLLOpen(pszHDRFilename, "rb");
911 :
912 3 : if (fp == NULL)
913 : {
914 0 : CPLError(CE_Failure, CPLE_OpenFailed,
915 : "Failed to open grid block index file:\n%s\n", pszHDRFilename);
916 :
917 0 : CPLFree(pszHDRFilename);
918 0 : return (CE_Failure);
919 : }
920 :
921 3 : CPLFree(pszHDRFilename);
922 :
923 : /* -------------------------------------------------------------------- */
924 : /* Verify the magic number. This is often corrupted by CR/LF */
925 : /* translation. */
926 : /* -------------------------------------------------------------------- */
927 3 : if (VSIFReadL(abyHeader, 1, 8, fp) != 8)
928 : {
929 0 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
930 0 : return CE_Failure;
931 : }
932 3 : if (abyHeader[3] == 0x0D && abyHeader[4] == 0x0A)
933 : {
934 0 : CPLError(CE_Failure, CPLE_AppDefined,
935 : "w001001x.adf file header has been corrupted by unix to dos "
936 : "text conversion.");
937 0 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
938 0 : return CE_Failure;
939 : }
940 :
941 3 : if (abyHeader[0] != 0x00 || abyHeader[1] != 0x00 || abyHeader[2] != 0x27 ||
942 3 : abyHeader[3] != 0x0A || abyHeader[4] != 0xFF || abyHeader[5] != 0xFF)
943 : {
944 0 : CPLError(CE_Failure, CPLE_AppDefined,
945 : "w001001x.adf file header magic number is corrupt.");
946 0 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
947 0 : return CE_Failure;
948 : }
949 :
950 : /* -------------------------------------------------------------------- */
951 : /* Get the file length (in 2 byte shorts) */
952 : /* -------------------------------------------------------------------- */
953 3 : if (VSIFSeekL(fp, 24, SEEK_SET) != 0 || VSIFReadL(&nValue, 1, 4, fp) != 4)
954 : {
955 0 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
956 0 : return CE_Failure;
957 : }
958 :
959 3 : nValue = CPL_MSBWORD32(nValue);
960 3 : if (nValue > INT_MAX)
961 : {
962 0 : CPLError(CE_Failure, CPLE_AppDefined, "AIGReadBlockIndex: Bad length");
963 0 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
964 0 : return CE_Failure;
965 : }
966 3 : nLength = nValue * 2;
967 3 : if (nLength <= 100)
968 : {
969 0 : CPLError(CE_Failure, CPLE_AppDefined, "AIGReadBlockIndex: Bad length");
970 0 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
971 0 : return CE_Failure;
972 : }
973 :
974 : /* -------------------------------------------------------------------- */
975 : /* Allocate buffer, and read the file (from beyond the header) */
976 : /* into the buffer. */
977 : /* -------------------------------------------------------------------- */
978 3 : psTInfo->nBlocks = (nLength - 100) / 8;
979 3 : if (psTInfo->nBlocks >= 1000000)
980 : {
981 : // Avoid excessive memory consumption.
982 : vsi_l_offset nFileSize;
983 0 : VSIFSeekL(fp, 0, SEEK_END);
984 0 : nFileSize = VSIFTellL(fp);
985 0 : if (nFileSize < 100 ||
986 0 : (vsi_l_offset)psTInfo->nBlocks > (nFileSize - 100) / 8)
987 : {
988 0 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
989 0 : return CE_Failure;
990 : }
991 : }
992 3 : panIndex = (GUInt32 *)VSI_MALLOC2_VERBOSE(psTInfo->nBlocks, 8);
993 3 : if (panIndex == NULL)
994 : {
995 0 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
996 0 : return CE_Failure;
997 : }
998 3 : if (VSIFSeekL(fp, 100, SEEK_SET) != 0 ||
999 3 : (int)VSIFReadL(panIndex, 8, psTInfo->nBlocks, fp) != psTInfo->nBlocks)
1000 : {
1001 0 : CPLError(CE_Failure, CPLE_AppDefined,
1002 : "AIGReadBlockIndex: Cannot read block info");
1003 0 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
1004 0 : CPLFree(panIndex);
1005 0 : return CE_Failure;
1006 : }
1007 :
1008 3 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
1009 :
1010 : /* -------------------------------------------------------------------- */
1011 : /* Allocate AIGInfo block info arrays. */
1012 : /* -------------------------------------------------------------------- */
1013 3 : psTInfo->panBlockOffset =
1014 3 : (GUInt32 *)VSI_MALLOC2_VERBOSE(4, psTInfo->nBlocks);
1015 3 : psTInfo->panBlockSize = (int *)VSI_MALLOC2_VERBOSE(4, psTInfo->nBlocks);
1016 3 : if (psTInfo->panBlockOffset == NULL || psTInfo->panBlockSize == NULL)
1017 : {
1018 0 : CPLFree(psTInfo->panBlockOffset);
1019 0 : CPLFree(psTInfo->panBlockSize);
1020 0 : psTInfo->panBlockOffset = NULL;
1021 0 : psTInfo->panBlockSize = NULL;
1022 0 : CPLFree(panIndex);
1023 0 : return CE_Failure;
1024 : }
1025 :
1026 : /* -------------------------------------------------------------------- */
1027 : /* Populate the block information. */
1028 : /* -------------------------------------------------------------------- */
1029 5 : for (i = 0; i < psTInfo->nBlocks; i++)
1030 : {
1031 : GUInt32 nVal;
1032 :
1033 3 : nVal = CPL_MSBWORD32(panIndex[i * 2]);
1034 3 : if (nVal >= INT_MAX)
1035 : {
1036 1 : CPLError(CE_Failure, CPLE_AppDefined,
1037 : "AIGReadBlockIndex: Bad offset for block %d", i);
1038 1 : CPLFree(psTInfo->panBlockOffset);
1039 1 : CPLFree(psTInfo->panBlockSize);
1040 1 : psTInfo->panBlockOffset = NULL;
1041 1 : psTInfo->panBlockSize = NULL;
1042 1 : CPLFree(panIndex);
1043 1 : return CE_Failure;
1044 : }
1045 2 : psTInfo->panBlockOffset[i] = nVal * 2;
1046 :
1047 2 : nVal = CPL_MSBWORD32(panIndex[i * 2 + 1]);
1048 2 : if (nVal >= INT_MAX / 2)
1049 : {
1050 0 : CPLError(CE_Failure, CPLE_AppDefined,
1051 : "AIGReadBlockIndex: Bad size for block %d", i);
1052 0 : CPLFree(psTInfo->panBlockOffset);
1053 0 : CPLFree(psTInfo->panBlockSize);
1054 0 : psTInfo->panBlockOffset = NULL;
1055 0 : psTInfo->panBlockSize = NULL;
1056 0 : CPLFree(panIndex);
1057 0 : return CE_Failure;
1058 : }
1059 2 : psTInfo->panBlockSize[i] = nVal * 2;
1060 : }
1061 :
1062 2 : CPLFree(panIndex);
1063 :
1064 2 : return (CE_None);
1065 : }
1066 :
1067 : /************************************************************************/
1068 : /* AIGReadBounds() */
1069 : /* */
1070 : /* Read the dblbnd.adf file for the georeferenced bounds. */
1071 : /************************************************************************/
1072 :
1073 9 : CPLErr AIGReadBounds(const char *pszCoverName, AIGInfo_t *psInfo)
1074 :
1075 : {
1076 : char *pszHDRFilename;
1077 : VSILFILE *fp;
1078 : double adfBound[4];
1079 9 : const size_t nHDRFilenameLen = strlen(pszCoverName) + 40;
1080 :
1081 : /* -------------------------------------------------------------------- */
1082 : /* Open the file dblbnd.adf file. */
1083 : /* -------------------------------------------------------------------- */
1084 9 : pszHDRFilename = (char *)CPLMalloc(nHDRFilenameLen);
1085 9 : snprintf(pszHDRFilename, nHDRFilenameLen, "%s/dblbnd.adf", pszCoverName);
1086 :
1087 9 : fp = AIGLLOpen(pszHDRFilename, "rb");
1088 :
1089 9 : if (fp == NULL)
1090 : {
1091 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1092 : "Failed to open grid bounds file:\n%s\n", pszHDRFilename);
1093 :
1094 0 : CPLFree(pszHDRFilename);
1095 0 : return (CE_Failure);
1096 : }
1097 :
1098 9 : CPLFree(pszHDRFilename);
1099 :
1100 : /* -------------------------------------------------------------------- */
1101 : /* Get the contents - four doubles. */
1102 : /* -------------------------------------------------------------------- */
1103 9 : if (VSIFReadL(adfBound, 1, 32, fp) != 32)
1104 : {
1105 0 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
1106 0 : return CE_Failure;
1107 : }
1108 :
1109 9 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
1110 :
1111 : #ifdef CPL_LSB
1112 9 : CPL_SWAPDOUBLE(adfBound + 0);
1113 9 : CPL_SWAPDOUBLE(adfBound + 1);
1114 9 : CPL_SWAPDOUBLE(adfBound + 2);
1115 9 : CPL_SWAPDOUBLE(adfBound + 3);
1116 : #endif
1117 :
1118 9 : psInfo->dfLLX = adfBound[0];
1119 9 : psInfo->dfLLY = adfBound[1];
1120 9 : psInfo->dfURX = adfBound[2];
1121 9 : psInfo->dfURY = adfBound[3];
1122 :
1123 9 : return (CE_None);
1124 : }
1125 :
1126 : /************************************************************************/
1127 : /* AIGReadStatistics() */
1128 : /* */
1129 : /* Read the sta.adf file for the layer statistics. */
1130 : /************************************************************************/
1131 :
1132 9 : CPLErr AIGReadStatistics(const char *pszCoverName, AIGInfo_t *psInfo)
1133 :
1134 : {
1135 : char *pszHDRFilename;
1136 : VSILFILE *fp;
1137 : double adfStats[4];
1138 9 : const size_t nHDRFilenameLen = strlen(pszCoverName) + 40;
1139 : size_t nRead;
1140 :
1141 9 : psInfo->dfMin = 0.0;
1142 9 : psInfo->dfMax = 0.0;
1143 9 : psInfo->dfMean = 0.0;
1144 9 : psInfo->dfStdDev = -1.0;
1145 :
1146 : /* -------------------------------------------------------------------- */
1147 : /* Open the file sta.adf file. */
1148 : /* -------------------------------------------------------------------- */
1149 9 : pszHDRFilename = (char *)CPLMalloc(nHDRFilenameLen);
1150 9 : snprintf(pszHDRFilename, nHDRFilenameLen, "%s/sta.adf", pszCoverName);
1151 :
1152 9 : fp = AIGLLOpen(pszHDRFilename, "rb");
1153 :
1154 9 : if (fp == NULL)
1155 : {
1156 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1157 : "Failed to open grid statistics file:\n%s\n", pszHDRFilename);
1158 :
1159 0 : CPLFree(pszHDRFilename);
1160 0 : return (CE_Failure);
1161 : }
1162 :
1163 : /* -------------------------------------------------------------------- */
1164 : /* Get the contents - 3 or 4 doubles. */
1165 : /* -------------------------------------------------------------------- */
1166 9 : nRead = VSIFReadL(adfStats, 1, 32, fp);
1167 :
1168 9 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp));
1169 :
1170 9 : if (nRead == 32)
1171 : {
1172 : #ifdef CPL_LSB
1173 8 : CPL_SWAPDOUBLE(adfStats + 0);
1174 8 : CPL_SWAPDOUBLE(adfStats + 1);
1175 8 : CPL_SWAPDOUBLE(adfStats + 2);
1176 8 : CPL_SWAPDOUBLE(adfStats + 3);
1177 : #endif
1178 :
1179 8 : psInfo->dfMin = adfStats[0];
1180 8 : psInfo->dfMax = adfStats[1];
1181 8 : psInfo->dfMean = adfStats[2];
1182 8 : psInfo->dfStdDev = adfStats[3];
1183 : }
1184 1 : else if (nRead == 24)
1185 : {
1186 : /* See dataset at https://trac.osgeo.org/gdal/ticket/6633 */
1187 : /* In that case, we have only min, max and mean, in LSB ordering */
1188 : CPL_LSBPTR64(adfStats + 0);
1189 : CPL_LSBPTR64(adfStats + 1);
1190 : CPL_LSBPTR64(adfStats + 2);
1191 :
1192 1 : psInfo->dfMin = adfStats[0];
1193 1 : psInfo->dfMax = adfStats[1];
1194 1 : psInfo->dfMean = adfStats[2];
1195 : }
1196 : else
1197 : {
1198 0 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong content for %s",
1199 : pszHDRFilename);
1200 0 : CPLFree(pszHDRFilename);
1201 0 : return CE_Failure;
1202 : }
1203 :
1204 9 : CPLFree(pszHDRFilename);
1205 9 : return CE_None;
1206 : }
|