Coverage Report

Created: 2026-08-11 08:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/ceos/ceosopen.c
Line
Count
Source
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
CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused)
17
21
{
18
21
}
19
20
/************************************************************************/
21
/*                            CEOSScanInt()                             */
22
/*                                                                      */
23
/*      Read up to nMaxChars from the passed string, and interpret      */
24
/*      as an integer.                                                  */
25
/************************************************************************/
26
27
static int CEOSScanInt(const char *pszString, int nMaxChars)
28
29
45
{
30
45
    char szWorking[33] = {0};
31
45
    int i;
32
33
45
    if (nMaxChars > 32 || nMaxChars == 0)
34
0
        nMaxChars = 32;
35
36
240
    for (i = 0; i < nMaxChars && pszString[i] != '\0'; i++)
37
195
        szWorking[i] = pszString[i];
38
39
45
    szWorking[i] = '\0';
40
41
45
    return (atoi(szWorking));
42
45
}
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
CEOSRecord *CEOSReadRecord(CEOSImage *psImage)
53
54
21
{
55
21
    GByte abyHeader[12];
56
21
    CEOSRecord *psRecord;
57
21
    GUInt32 nRecordNumUInt32, nLengthUInt32;
58
59
    /* -------------------------------------------------------------------- */
60
    /*      Read the standard CEOS header.                                  */
61
    /* -------------------------------------------------------------------- */
62
21
    if (VSIFEofL(psImage->fpImage))
63
0
        return NULL;
64
65
21
    if (VSIFReadL(abyHeader, 1, 12, psImage->fpImage) != 12)
66
0
    {
67
0
        CPLError(CE_Failure, CPLE_FileIO,
68
0
                 "Ran out of data reading CEOS record.");
69
0
        return NULL;
70
0
    }
71
72
    /* -------------------------------------------------------------------- */
73
    /*      Extract this information.                                       */
74
    /* -------------------------------------------------------------------- */
75
21
    psRecord = (CEOSRecord *)CPLMalloc(sizeof(CEOSRecord));
76
21
    if (psImage->bLittleEndian)
77
3
    {
78
3
        CPL_SWAP32PTR(abyHeader + 0);
79
3
        CPL_SWAP32PTR(abyHeader + 8);
80
3
    }
81
82
21
    nRecordNumUInt32 = ((unsigned)abyHeader[0] << 24) + (abyHeader[1] << 16) +
83
21
                       (abyHeader[2] << 8) + abyHeader[3];
84
85
21
    psRecord->nRecordType = ((unsigned)abyHeader[4] << 24) +
86
21
                            (abyHeader[5] << 16) + (abyHeader[6] << 8) +
87
21
                            abyHeader[7];
88
89
21
    nLengthUInt32 = ((unsigned)abyHeader[8] << 24) + (abyHeader[9] << 16) +
90
21
                    (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
21
    if (nRecordNumUInt32 > 200000 || nLengthUInt32 < 12 ||
97
20
        nLengthUInt32 > 200000)
98
1
    {
99
1
        CPLError(CE_Failure, CPLE_AppDefined,
100
1
                 "CEOS record leader appears to be corrupt.\n"
101
1
                 "Record Number = %u, Record Length = %u\n",
102
1
                 nRecordNumUInt32, nLengthUInt32);
103
1
        CPLFree(psRecord);
104
1
        return NULL;
105
1
    }
106
107
20
    psRecord->nRecordNum = (int)nRecordNumUInt32;
108
20
    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
20
    psRecord->pachData = (char *)VSI_MALLOC_VERBOSE(psRecord->nLength);
115
20
    if (psRecord->pachData == NULL)
116
0
    {
117
0
        CPLFree(psRecord);
118
0
        return NULL;
119
0
    }
120
121
20
    memcpy(psRecord->pachData, abyHeader, 12);
122
123
20
    if ((int)VSIFReadL(psRecord->pachData + 12, 1, psRecord->nLength - 12,
124
20
                       psImage->fpImage) != psRecord->nLength - 12)
125
1
    {
126
1
        CPLError(CE_Failure, CPLE_FileIO, "Short read on CEOS record data.\n");
127
1
        CPLFree(psRecord->pachData);
128
1
        CPLFree(psRecord);
129
1
        return NULL;
130
1
    }
131
132
19
    return psRecord;
133
20
}
134
135
/************************************************************************/
136
/*                         CEOSDestroyRecord()                          */
137
/*                                                                      */
138
/*      Free a record.                                                  */
139
/************************************************************************/
140
141
void CEOSDestroyRecord(CEOSRecord *psRecord)
142
143
21
{
144
21
    if (psRecord)
145
19
    {
146
19
        CPLFree(psRecord->pachData);
147
19
        CPLFree(psRecord);
148
19
    }
149
21
}
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
CEOSImage *CEOSOpen(const char *pszFilename, const char *pszAccess)
168
169
21
{
170
21
    VSILFILE *fp;
171
21
    CEOSRecord *psRecord;
172
21
    CEOSImage *psImage;
173
21
    int nSeqNum, i;
174
21
    GByte abyHeader[16];
175
176
    /* -------------------------------------------------------------------- */
177
    /*      Try to open the imagery file.                                   */
178
    /* -------------------------------------------------------------------- */
179
21
    fp = VSIFOpenL(pszFilename, pszAccess);
180
181
21
    if (fp == NULL)
182
0
    {
183
0
        CPLError(CE_Failure, CPLE_OpenFailed,
184
0
                 "Failed to open CEOS file `%s' with access `%s'.\n",
185
0
                 pszFilename, pszAccess);
186
0
        return NULL;
187
0
    }
188
189
    /* -------------------------------------------------------------------- */
190
    /*      Create a CEOSImage structure, and initialize it.                */
191
    /* -------------------------------------------------------------------- */
192
21
    psImage = (CEOSImage *)CPLCalloc(1, sizeof(CEOSImage));
193
21
    psImage->fpImage = fp;
194
195
21
    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
21
    if (VSIFReadL(abyHeader, 16, 1, fp) != 1 || VSIFSeekL(fp, 0, SEEK_SET) < 0)
202
0
    {
203
0
        CEOSClose(psImage);
204
0
        return NULL;
205
0
    }
206
207
21
    if (abyHeader[0] != 0 || abyHeader[1] != 0)
208
3
        psImage->bLittleEndian = TRUE;
209
210
    /* -------------------------------------------------------------------- */
211
    /*      Try to read the header record.                                  */
212
    /* -------------------------------------------------------------------- */
213
21
    psRecord = CEOSReadRecord(psImage);
214
21
    if (psRecord == NULL || psRecord->nLength < 288 + 4)
215
2
    {
216
2
        CEOSDestroyRecord(psRecord);
217
2
        CEOSClose(psImage);
218
2
        return NULL;
219
2
    }
220
221
19
    char format_doc[13] = {0};
222
19
    memcpy(format_doc, psRecord->pachData + 16, 12);
223
19
    if (strncmp("CEOS-SAR-CCT", format_doc, 12) == 0)
224
14
    {
225
14
        CEOSDestroyRecord(psRecord);
226
14
        CEOSClose(psImage);
227
14
        return NULL;
228
14
    }
229
230
5
    if (psRecord->nRecordType != CRT_IMAGE_FDR)
231
0
    {
232
0
        CPLError(CE_Failure, CPLE_AppDefined,
233
0
                 "Got a %X type record, instead of the expected\n"
234
0
                 "file descriptor record on file %s.\n",
235
0
                 psRecord->nRecordType, pszFilename);
236
237
0
        CEOSDestroyRecord(psRecord);
238
0
        CEOSClose(psImage);
239
0
        return NULL;
240
0
    }
241
242
    /* -------------------------------------------------------------------- */
243
    /*      The sequence number should be 2 indicating this is the          */
244
    /*      imagery file.                                                   */
245
    /* -------------------------------------------------------------------- */
246
5
    nSeqNum = CEOSScanInt(psRecord->pachData + 44, 4);
247
5
    if (nSeqNum != 2)
248
1
    {
249
1
        CPLError(CE_Warning, CPLE_AppDefined,
250
1
                 "Got a %d file sequence number, instead of the expected\n"
251
1
                 "2 indicating imagery on file %s.\n"
252
1
                 "Continuing to access anyways.\n",
253
1
                 nSeqNum, pszFilename);
254
1
    }
255
256
    /* -------------------------------------------------------------------- */
257
    /*      Extract various information.                                    */
258
    /* -------------------------------------------------------------------- */
259
5
    psImage->nImageRecCount = CEOSScanInt(psRecord->pachData + 180, 6);
260
5
    psImage->nImageRecLength = CEOSScanInt(psRecord->pachData + 186, 6);
261
5
    psImage->nBitsPerPixel = CEOSScanInt(psRecord->pachData + 216, 4);
262
5
    psImage->nBands = CEOSScanInt(psRecord->pachData + 232, 4);
263
5
    psImage->nLines = CEOSScanInt(psRecord->pachData + 236, 8);
264
5
    psImage->nPixels = CEOSScanInt(psRecord->pachData + 248, 8);
265
266
5
    psImage->nPrefixBytes = CEOSScanInt(psRecord->pachData + 276, 4);
267
5
    psImage->nSuffixBytes = CEOSScanInt(psRecord->pachData + 288, 4);
268
269
5
    if (psImage->nImageRecLength <= 0 || psImage->nPrefixBytes < 0 ||
270
4
        psImage->nBands > INT_MAX / psImage->nImageRecLength ||
271
4
        (size_t)psImage->nBands > INT_MAX / sizeof(int))
272
1
    {
273
1
        CEOSDestroyRecord(psRecord);
274
1
        CEOSClose(psImage);
275
1
        return NULL;
276
1
    }
277
278
    /* -------------------------------------------------------------------- */
279
    /*      Try to establish the layout of the imagery data.                */
280
    /* -------------------------------------------------------------------- */
281
4
    psImage->nLineOffset = psImage->nBands * psImage->nImageRecLength;
282
283
4
    psImage->panDataStart = (int *)VSIMalloc(sizeof(int) * psImage->nBands);
284
4
    if (psImage->panDataStart == NULL)
285
0
    {
286
0
        CEOSDestroyRecord(psRecord);
287
0
        CEOSClose(psImage);
288
0
        return NULL;
289
0
    }
290
291
10
    for (i = 0; i < psImage->nBands; i++)
292
6
    {
293
6
        psImage->panDataStart[i] = psRecord->nLength +
294
6
                                   i * psImage->nImageRecLength + 12 +
295
6
                                   psImage->nPrefixBytes;
296
6
    }
297
298
4
    CEOSDestroyRecord(psRecord);
299
300
4
    return psImage;
301
4
}
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
CPLErr CEOSReadScanline(CEOSImage *psCEOS, int nBand, int nScanline,
320
                        void *pData)
321
322
20
{
323
20
    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
20
    nOffset =
331
20
        psCEOS->panDataStart[nBand - 1] + (nScanline - 1) * psCEOS->nLineOffset;
332
333
20
    if (VSIFSeekL(psCEOS->fpImage, nOffset, SEEK_SET) != 0)
334
0
    {
335
0
        CPLError(CE_Failure, CPLE_FileIO,
336
0
                 "Seek to %d for scanline %d failed.\n", nOffset, nScanline);
337
0
        return CE_Failure;
338
0
    }
339
340
    /* -------------------------------------------------------------------- */
341
    /*      Read the data.                                                  */
342
    /* -------------------------------------------------------------------- */
343
20
    nBytes = psCEOS->nPixels * psCEOS->nBitsPerPixel / 8;
344
20
    if ((int)VSIFReadL(pData, 1, nBytes, psCEOS->fpImage) != nBytes)
345
5
    {
346
5
        CPLError(CE_Failure, CPLE_FileIO,
347
5
                 "Read of %d bytes for scanline %d failed.\n", nBytes,
348
5
                 nScanline);
349
5
        return CE_Failure;
350
5
    }
351
352
15
    return CE_None;
353
20
}
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
void CEOSClose(CEOSImage *psCEOS)
366
367
21
{
368
21
    CPLFree(psCEOS->panDataStart);
369
21
    CPL_IGNORE_RET_VAL_INT(VSIFCloseL(psCEOS->fpImage));
370
21
    CPLFree(psCEOS);
371
21
}