Line data Source code
1 : /******************************************************************************
2 : * $Id$
3 : *
4 : * Project: CEOS Translator
5 : * Purpose: Implementation of non-GDAL dependent CEOS support.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 1999, Frank Warmerdam
10 : * Copyright (c) 2007-2012, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "ceosopen.h"
16 :
17 4 : CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused)
18 : {
19 4 : }
20 :
21 : /************************************************************************/
22 : /* CEOSScanInt() */
23 : /* */
24 : /* Read up to nMaxChars from the passed string, and interpret */
25 : /* as an integer. */
26 : /************************************************************************/
27 :
28 18 : static int CEOSScanInt(const char *pszString, int nMaxChars)
29 :
30 : {
31 18 : char szWorking[33] = {0};
32 : int i;
33 :
34 18 : if (nMaxChars > 32 || nMaxChars == 0)
35 0 : nMaxChars = 32;
36 :
37 114 : for (i = 0; i < nMaxChars && pszString[i] != '\0'; i++)
38 96 : szWorking[i] = pszString[i];
39 :
40 18 : szWorking[i] = '\0';
41 :
42 18 : return (atoi(szWorking));
43 : }
44 :
45 : /************************************************************************/
46 : /* CEOSReadRecord() */
47 : /* */
48 : /* Read a single CEOS record at the current point in the file. */
49 : /* Return NULL after reporting an error if it fails, otherwise */
50 : /* return the record. */
51 : /************************************************************************/
52 :
53 4 : CEOSRecord *CEOSReadRecord(CEOSImage *psImage)
54 :
55 : {
56 : GByte abyHeader[12];
57 : CEOSRecord *psRecord;
58 : GUInt32 nRecordNumUInt32, nLengthUInt32;
59 :
60 : /* -------------------------------------------------------------------- */
61 : /* Read the standard CEOS header. */
62 : /* -------------------------------------------------------------------- */
63 4 : if (VSIFEofL(psImage->fpImage))
64 0 : return NULL;
65 :
66 4 : if (VSIFReadL(abyHeader, 1, 12, psImage->fpImage) != 12)
67 : {
68 0 : CPLError(CE_Failure, CPLE_FileIO,
69 : "Ran out of data reading CEOS record.");
70 0 : return NULL;
71 : }
72 :
73 : /* -------------------------------------------------------------------- */
74 : /* Extract this information. */
75 : /* -------------------------------------------------------------------- */
76 4 : psRecord = (CEOSRecord *)CPLMalloc(sizeof(CEOSRecord));
77 4 : if (psImage->bLittleEndian)
78 : {
79 2 : CPL_SWAP32PTR(abyHeader + 0);
80 2 : CPL_SWAP32PTR(abyHeader + 8);
81 : }
82 :
83 4 : nRecordNumUInt32 = ((unsigned)abyHeader[0] << 24) + (abyHeader[1] << 16) +
84 4 : (abyHeader[2] << 8) + abyHeader[3];
85 :
86 4 : psRecord->nRecordType = ((unsigned)abyHeader[4] << 24) +
87 4 : (abyHeader[5] << 16) + (abyHeader[6] << 8) +
88 4 : abyHeader[7];
89 :
90 4 : nLengthUInt32 = ((unsigned)abyHeader[8] << 24) + (abyHeader[9] << 16) +
91 4 : (abyHeader[10] << 8) + abyHeader[11];
92 :
93 : /* -------------------------------------------------------------------- */
94 : /* Does it look reasonable? We assume there can't be too many */
95 : /* records and that the length must be between 12 and 200000. */
96 : /* -------------------------------------------------------------------- */
97 4 : if (nRecordNumUInt32 > 200000 || nLengthUInt32 < 12 ||
98 : nLengthUInt32 > 200000)
99 : {
100 0 : CPLError(CE_Failure, CPLE_AppDefined,
101 : "CEOS record leader appears to be corrupt.\n"
102 : "Record Number = %u, Record Length = %u\n",
103 : nRecordNumUInt32, nLengthUInt32);
104 0 : CPLFree(psRecord);
105 0 : return NULL;
106 : }
107 :
108 4 : psRecord->nRecordNum = (int)nRecordNumUInt32;
109 4 : psRecord->nLength = (int)nLengthUInt32;
110 :
111 : /* -------------------------------------------------------------------- */
112 : /* Read the remainder of the record into a buffer. Ensure that */
113 : /* the first 12 bytes gets moved into this buffer as well. */
114 : /* -------------------------------------------------------------------- */
115 4 : psRecord->pachData = (char *)VSI_MALLOC_VERBOSE(psRecord->nLength);
116 4 : if (psRecord->pachData == NULL)
117 : {
118 0 : CPLFree(psRecord);
119 0 : return NULL;
120 : }
121 :
122 4 : memcpy(psRecord->pachData, abyHeader, 12);
123 :
124 4 : if ((int)VSIFReadL(psRecord->pachData + 12, 1, psRecord->nLength - 12,
125 4 : psImage->fpImage) != psRecord->nLength - 12)
126 : {
127 0 : CPLError(CE_Failure, CPLE_FileIO, "Short read on CEOS record data.\n");
128 0 : CPLFree(psRecord->pachData);
129 0 : CPLFree(psRecord);
130 0 : return NULL;
131 : }
132 :
133 4 : return psRecord;
134 : }
135 :
136 : /************************************************************************/
137 : /* CEOSDestroyRecord() */
138 : /* */
139 : /* Free a record. */
140 : /************************************************************************/
141 :
142 4 : void CEOSDestroyRecord(CEOSRecord *psRecord)
143 :
144 : {
145 4 : if (psRecord)
146 : {
147 4 : CPLFree(psRecord->pachData);
148 4 : CPLFree(psRecord);
149 : }
150 4 : }
151 :
152 : /************************************************************************/
153 : /* CEOSOpen() */
154 : /************************************************************************/
155 :
156 : /**
157 : * Open a CEOS transfer.
158 : *
159 : * @param pszFilename The name of the CEOS imagery file (i.e. imag_01.dat).
160 : * @param pszAccess An fopen() style access string. Should be either "rb" for
161 : * read-only access, or "r+b" for read, and update access.
162 : *
163 : * @return A CEOSImage pointer as a handle to the image. The CEOSImage also
164 : * has various information about the image available. A NULL is returned
165 : * if an error occurs.
166 : */
167 :
168 4 : CEOSImage *CEOSOpen(const char *pszFilename, const char *pszAccess)
169 :
170 : {
171 : VSILFILE *fp;
172 : CEOSRecord *psRecord;
173 : CEOSImage *psImage;
174 : int nSeqNum, i;
175 : GByte abyHeader[16];
176 :
177 : /* -------------------------------------------------------------------- */
178 : /* Try to open the imagery file. */
179 : /* -------------------------------------------------------------------- */
180 4 : fp = VSIFOpenL(pszFilename, pszAccess);
181 :
182 4 : if (fp == NULL)
183 : {
184 0 : CPLError(CE_Failure, CPLE_OpenFailed,
185 : "Failed to open CEOS file `%s' with access `%s'.\n",
186 : pszFilename, pszAccess);
187 0 : return NULL;
188 : }
189 :
190 : /* -------------------------------------------------------------------- */
191 : /* Create a CEOSImage structure, and initialize it. */
192 : /* -------------------------------------------------------------------- */
193 4 : psImage = (CEOSImage *)CPLCalloc(1, sizeof(CEOSImage));
194 4 : psImage->fpImage = fp;
195 :
196 4 : psImage->nPixels = psImage->nLines = psImage->nBands = 0;
197 :
198 : /* -------------------------------------------------------------------- */
199 : /* Preread info on the first record, to establish if it is */
200 : /* little endian. */
201 : /* -------------------------------------------------------------------- */
202 4 : if (VSIFReadL(abyHeader, 16, 1, fp) != 1 || VSIFSeekL(fp, 0, SEEK_SET) < 0)
203 : {
204 0 : CEOSClose(psImage);
205 0 : return NULL;
206 : }
207 :
208 4 : if (abyHeader[0] != 0 || abyHeader[1] != 0)
209 2 : psImage->bLittleEndian = TRUE;
210 :
211 : /* -------------------------------------------------------------------- */
212 : /* Try to read the header record. */
213 : /* -------------------------------------------------------------------- */
214 4 : psRecord = CEOSReadRecord(psImage);
215 4 : if (psRecord == NULL || psRecord->nLength < 288 + 4)
216 : {
217 0 : CEOSDestroyRecord(psRecord);
218 0 : CEOSClose(psImage);
219 0 : return NULL;
220 : }
221 :
222 4 : char format_doc[13] = {0};
223 4 : memcpy(format_doc, psRecord->pachData + 16, 12);
224 4 : if (strncmp("CEOS-SAR-CCT", format_doc, 12) == 0)
225 : {
226 2 : CEOSDestroyRecord(psRecord);
227 2 : CEOSClose(psImage);
228 2 : return NULL;
229 : }
230 :
231 2 : if (psRecord->nRecordType != CRT_IMAGE_FDR)
232 : {
233 0 : CPLError(CE_Failure, CPLE_AppDefined,
234 : "Got a %X type record, instead of the expected\n"
235 : "file descriptor record on file %s.\n",
236 : psRecord->nRecordType, pszFilename);
237 :
238 0 : CEOSDestroyRecord(psRecord);
239 0 : CEOSClose(psImage);
240 0 : return NULL;
241 : }
242 :
243 : /* -------------------------------------------------------------------- */
244 : /* The sequence number should be 2 indicating this is the */
245 : /* imagery file. */
246 : /* -------------------------------------------------------------------- */
247 2 : nSeqNum = CEOSScanInt(psRecord->pachData + 44, 4);
248 2 : if (nSeqNum != 2)
249 : {
250 0 : CPLError(CE_Warning, CPLE_AppDefined,
251 : "Got a %d file sequence number, instead of the expected\n"
252 : "2 indicating imagery on file %s.\n"
253 : "Continuing to access anyways.\n",
254 : nSeqNum, pszFilename);
255 : }
256 :
257 : /* -------------------------------------------------------------------- */
258 : /* Extract various information. */
259 : /* -------------------------------------------------------------------- */
260 2 : psImage->nImageRecCount = CEOSScanInt(psRecord->pachData + 180, 6);
261 2 : psImage->nImageRecLength = CEOSScanInt(psRecord->pachData + 186, 6);
262 2 : psImage->nBitsPerPixel = CEOSScanInt(psRecord->pachData + 216, 4);
263 2 : psImage->nBands = CEOSScanInt(psRecord->pachData + 232, 4);
264 2 : psImage->nLines = CEOSScanInt(psRecord->pachData + 236, 8);
265 2 : psImage->nPixels = CEOSScanInt(psRecord->pachData + 248, 8);
266 :
267 2 : psImage->nPrefixBytes = CEOSScanInt(psRecord->pachData + 276, 4);
268 2 : psImage->nSuffixBytes = CEOSScanInt(psRecord->pachData + 288, 4);
269 :
270 2 : if (psImage->nImageRecLength <= 0 || psImage->nPrefixBytes < 0 ||
271 2 : psImage->nBands > INT_MAX / psImage->nImageRecLength ||
272 2 : (size_t)psImage->nBands > INT_MAX / sizeof(int))
273 : {
274 0 : CEOSDestroyRecord(psRecord);
275 0 : CEOSClose(psImage);
276 0 : return NULL;
277 : }
278 :
279 : /* -------------------------------------------------------------------- */
280 : /* Try to establish the layout of the imagery data. */
281 : /* -------------------------------------------------------------------- */
282 2 : psImage->nLineOffset = psImage->nBands * psImage->nImageRecLength;
283 :
284 2 : psImage->panDataStart = (int *)VSIMalloc(sizeof(int) * psImage->nBands);
285 2 : if (psImage->panDataStart == NULL)
286 : {
287 0 : CEOSDestroyRecord(psRecord);
288 0 : CEOSClose(psImage);
289 0 : return NULL;
290 : }
291 :
292 10 : for (i = 0; i < psImage->nBands; i++)
293 : {
294 8 : psImage->panDataStart[i] = psRecord->nLength +
295 8 : i * psImage->nImageRecLength + 12 +
296 8 : psImage->nPrefixBytes;
297 : }
298 :
299 2 : CEOSDestroyRecord(psRecord);
300 :
301 2 : return psImage;
302 : }
303 :
304 : /************************************************************************/
305 : /* CEOSReadScanline() */
306 : /************************************************************************/
307 :
308 : /**
309 : * Read a scanline of image.
310 : *
311 : * @param psCEOS The CEOS dataset handle returned by CEOSOpen().
312 : * @param nBand The band number (i.e. 1, 2, 3).
313 : * @param nScanline The scanline requested, one based.
314 : * @param pData The data buffer to read into. Must be at least nPixels *
315 : * nBitesPerPixel bits long.
316 : *
317 : * @return CPLErr Returns error indicator or CE_None if the read succeeds.
318 : */
319 :
320 3 : CPLErr CEOSReadScanline(CEOSImage *psCEOS, int nBand, int nScanline,
321 : void *pData)
322 :
323 : {
324 : int nOffset, nBytes;
325 :
326 : /*
327 : * As a short cut, I currently just seek to the data, and read it
328 : * raw, rather than trying to read ceos records properly.
329 : */
330 :
331 3 : nOffset =
332 3 : psCEOS->panDataStart[nBand - 1] + (nScanline - 1) * psCEOS->nLineOffset;
333 :
334 3 : if (VSIFSeekL(psCEOS->fpImage, nOffset, SEEK_SET) != 0)
335 : {
336 0 : CPLError(CE_Failure, CPLE_FileIO,
337 : "Seek to %d for scanline %d failed.\n", nOffset, nScanline);
338 0 : return CE_Failure;
339 : }
340 :
341 : /* -------------------------------------------------------------------- */
342 : /* Read the data. */
343 : /* -------------------------------------------------------------------- */
344 3 : nBytes = psCEOS->nPixels * psCEOS->nBitsPerPixel / 8;
345 3 : if ((int)VSIFReadL(pData, 1, nBytes, psCEOS->fpImage) != nBytes)
346 : {
347 0 : CPLError(CE_Failure, CPLE_FileIO,
348 : "Read of %d bytes for scanline %d failed.\n", nBytes,
349 : nScanline);
350 0 : return CE_Failure;
351 : }
352 :
353 3 : return CE_None;
354 : }
355 :
356 : /************************************************************************/
357 : /* CEOSClose() */
358 : /************************************************************************/
359 :
360 : /**
361 : * Close a CEOS transfer. Any open files are closed, and memory deallocated.
362 : *
363 : * @param psCEOS The CEOSImage handle from CEOSOpen to be closed.
364 : */
365 :
366 4 : void CEOSClose(CEOSImage *psCEOS)
367 :
368 : {
369 4 : CPLFree(psCEOS->panDataStart);
370 4 : CPL_IGNORE_RET_VAL_INT(VSIFCloseL(psCEOS->fpImage));
371 4 : CPLFree(psCEOS);
372 4 : }
|