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