Coverage Report

Created: 2025-06-09 07:07

/src/gdal/frmts/raw/genbindataset.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  Generic Binary format driver (.hdr but not ESRI .hdr!)
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2008-2011, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_string.h"
15
#include "gdal_frmts.h"
16
#include "ogr_spatialref.h"
17
#include "rawdataset.h"
18
19
#include <algorithm>
20
#include <cstdlib>
21
22
#include "usgs_esri_zones.h"
23
24
/************************************************************************/
25
/* ==================================================================== */
26
/*                              GenBinDataset                           */
27
/* ==================================================================== */
28
/************************************************************************/
29
30
class GenBinDataset final : public RawDataset
31
{
32
    friend class GenBinBitRasterBand;
33
34
    VSILFILE *fpImage;  // image data file.
35
36
    bool bGotTransform;
37
    double adfGeoTransform[6];
38
    OGRSpatialReference m_oSRS{};
39
40
    char **papszHDR;
41
42
    void ParseCoordinateSystem(char **);
43
44
    CPL_DISALLOW_COPY_ASSIGN(GenBinDataset)
45
46
    CPLErr Close() override;
47
48
  public:
49
    GenBinDataset();
50
    ~GenBinDataset() override;
51
52
    CPLErr GetGeoTransform(double *padfTransform) override;
53
54
    const OGRSpatialReference *GetSpatialRef() const override
55
0
    {
56
0
        return m_oSRS.IsEmpty() ? RawDataset::GetSpatialRef() : &m_oSRS;
57
0
    }
58
59
    char **GetFileList() override;
60
61
    static GDALDataset *Open(GDALOpenInfo *);
62
};
63
64
/************************************************************************/
65
/* ==================================================================== */
66
/*                       GenBinBitRasterBand                            */
67
/* ==================================================================== */
68
/************************************************************************/
69
70
class GenBinBitRasterBand final : public GDALPamRasterBand
71
{
72
    int nBits;
73
74
    CPL_DISALLOW_COPY_ASSIGN(GenBinBitRasterBand)
75
76
  public:
77
    GenBinBitRasterBand(GenBinDataset *poDS, int nBits);
78
79
    ~GenBinBitRasterBand() override
80
0
    {
81
0
    }
82
83
    CPLErr IReadBlock(int, int, void *) override;
84
};
85
86
/************************************************************************/
87
/*                        GenBinBitRasterBand()                         */
88
/************************************************************************/
89
90
GenBinBitRasterBand::GenBinBitRasterBand(GenBinDataset *poDSIn, int nBitsIn)
91
0
    : nBits(nBitsIn)
92
0
{
93
0
    SetMetadataItem("NBITS", CPLString().Printf("%d", nBitsIn),
94
0
                    "IMAGE_STRUCTURE");
95
96
0
    poDS = poDSIn;
97
0
    nBand = 1;
98
99
0
    eDataType = GDT_Byte;
100
101
0
    nBlockXSize = poDSIn->nRasterXSize;
102
0
    nBlockYSize = 1;
103
0
}
104
105
/************************************************************************/
106
/*                             IReadBlock()                             */
107
/************************************************************************/
108
109
CPLErr GenBinBitRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
110
                                       void *pImage)
111
112
0
{
113
0
    GenBinDataset *poGDS = reinterpret_cast<GenBinDataset *>(poDS);
114
115
    /* -------------------------------------------------------------------- */
116
    /*      Establish desired position.                                     */
117
    /* -------------------------------------------------------------------- */
118
0
    const vsi_l_offset nLineStart =
119
0
        (static_cast<vsi_l_offset>(nBlockXSize) * nBlockYOff * nBits) / 8;
120
0
    int iBitOffset = static_cast<int>(
121
0
        (static_cast<vsi_l_offset>(nBlockXSize) * nBlockYOff * nBits) % 8);
122
0
    const unsigned int nLineBytes = static_cast<unsigned int>(
123
0
        (static_cast<vsi_l_offset>(nBlockXSize) * (nBlockYOff + 1) * nBits +
124
0
         7) /
125
0
            8 -
126
0
        nLineStart);
127
128
    /* -------------------------------------------------------------------- */
129
    /*      Read data into buffer.                                          */
130
    /* -------------------------------------------------------------------- */
131
0
    GByte *pabyBuffer = static_cast<GByte *>(CPLCalloc(nLineBytes, 1));
132
133
0
    if (VSIFSeekL(poGDS->fpImage, nLineStart, SEEK_SET) != 0 ||
134
0
        VSIFReadL(pabyBuffer, 1, nLineBytes, poGDS->fpImage) != nLineBytes)
135
0
    {
136
0
        CPLError(CE_Failure, CPLE_FileIO,
137
0
                 "Failed to read %u bytes at offset %lu.\n%s", nLineBytes,
138
0
                 static_cast<unsigned long>(nLineStart), VSIStrerror(errno));
139
0
        CPLFree(pabyBuffer);
140
0
        return CE_Failure;
141
0
    }
142
143
    /* -------------------------------------------------------------------- */
144
    /*      Copy data, promoting to 8bit.                                   */
145
    /* -------------------------------------------------------------------- */
146
0
    GByte *pafImage = reinterpret_cast<GByte *>(pImage);
147
0
    if (nBits == 1)
148
0
    {
149
0
        for (int iX = 0; iX < nBlockXSize; iX++, iBitOffset += nBits)
150
0
        {
151
0
            if (pabyBuffer[iBitOffset >> 3] & (0x80 >> (iBitOffset & 7)))
152
0
                pafImage[iX] = 1;
153
0
            else
154
0
                pafImage[iX] = 0;
155
0
        }
156
0
    }
157
0
    else if (nBits == 2)
158
0
    {
159
0
        for (int iX = 0; iX < nBlockXSize; iX++, iBitOffset += nBits)
160
0
        {
161
0
            pafImage[iX] =
162
0
                (pabyBuffer[iBitOffset >> 3]) >> (6 - (iBitOffset & 0x7)) & 0x3;
163
0
        }
164
0
    }
165
0
    else if (nBits == 4)
166
0
    {
167
0
        for (int iX = 0; iX < nBlockXSize; iX++, iBitOffset += nBits)
168
0
        {
169
0
            if (iBitOffset == 0)
170
0
                pafImage[iX] = (pabyBuffer[iBitOffset >> 3]) >> 4;
171
0
            else
172
0
                pafImage[iX] = (pabyBuffer[iBitOffset >> 3]) & 0xf;
173
0
        }
174
0
    }
175
0
    else
176
0
    {
177
0
        CPLAssert(false);
178
0
    }
179
180
0
    CPLFree(pabyBuffer);
181
182
0
    return CE_None;
183
0
}
184
185
/************************************************************************/
186
/* ==================================================================== */
187
/*                              GenBinDataset                           */
188
/* ==================================================================== */
189
/************************************************************************/
190
191
/************************************************************************/
192
/*                            GenBinDataset()                             */
193
/************************************************************************/
194
195
GenBinDataset::GenBinDataset()
196
0
    : fpImage(nullptr), bGotTransform(false), papszHDR(nullptr)
197
0
{
198
0
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
199
0
    adfGeoTransform[0] = 0.0;
200
0
    adfGeoTransform[1] = 1.0;
201
0
    adfGeoTransform[2] = 0.0;
202
0
    adfGeoTransform[3] = 0.0;
203
0
    adfGeoTransform[4] = 0.0;
204
0
    adfGeoTransform[5] = 1.0;
205
0
}
206
207
/************************************************************************/
208
/*                            ~GenBinDataset()                          */
209
/************************************************************************/
210
211
GenBinDataset::~GenBinDataset()
212
213
0
{
214
0
    GenBinDataset::Close();
215
0
}
216
217
/************************************************************************/
218
/*                              Close()                                 */
219
/************************************************************************/
220
221
CPLErr GenBinDataset::Close()
222
0
{
223
0
    CPLErr eErr = CE_None;
224
0
    if (nOpenFlags != OPEN_FLAGS_CLOSED)
225
0
    {
226
0
        if (GenBinDataset::FlushCache(true) != CE_None)
227
0
            eErr = CE_Failure;
228
229
0
        if (fpImage)
230
0
        {
231
0
            if (VSIFCloseL(fpImage) != 0)
232
0
            {
233
0
                CPLError(CE_Failure, CPLE_FileIO, "I/O error");
234
0
                eErr = CE_Failure;
235
0
            }
236
0
        }
237
238
0
        CSLDestroy(papszHDR);
239
240
0
        if (GDALPamDataset::Close() != CE_None)
241
0
            eErr = CE_Failure;
242
0
    }
243
0
    return eErr;
244
0
}
245
246
/************************************************************************/
247
/*                          GetGeoTransform()                           */
248
/************************************************************************/
249
250
CPLErr GenBinDataset::GetGeoTransform(double *padfTransform)
251
252
0
{
253
0
    if (bGotTransform)
254
0
    {
255
0
        memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
256
0
        return CE_None;
257
0
    }
258
259
0
    return GDALPamDataset::GetGeoTransform(padfTransform);
260
0
}
261
262
/************************************************************************/
263
/*                            GetFileList()                             */
264
/************************************************************************/
265
266
char **GenBinDataset::GetFileList()
267
268
0
{
269
0
    const CPLString osPath = CPLGetPathSafe(GetDescription());
270
0
    const CPLString osName = CPLGetBasenameSafe(GetDescription());
271
272
    // Main data file, etc.
273
0
    char **papszFileList = GDALPamDataset::GetFileList();
274
275
    // Header file.
276
0
    const CPLString osFilename = CPLFormCIFilenameSafe(osPath, osName, "hdr");
277
0
    papszFileList = CSLAddString(papszFileList, osFilename);
278
279
0
    return papszFileList;
280
0
}
281
282
/************************************************************************/
283
/*                       ParseCoordinateSystem()                        */
284
/************************************************************************/
285
286
void GenBinDataset::ParseCoordinateSystem(char **papszHdr)
287
288
0
{
289
0
    const char *pszProjName = CSLFetchNameValue(papszHdr, "PROJECTION_NAME");
290
0
    if (pszProjName == nullptr)
291
0
        return;
292
293
    /* -------------------------------------------------------------------- */
294
    /*      Translate zone and parameters into numeric form.                */
295
    /* -------------------------------------------------------------------- */
296
0
    int nZone = 0;
297
0
    if (const char *pszProjectionZone =
298
0
            CSLFetchNameValue(papszHdr, "PROJECTION_ZONE"))
299
0
        nZone = atoi(pszProjectionZone);
300
301
#if 0
302
    // TODO(schwehr): Why was this being done but not used?
303
    double adfProjParams[15] = { 0.0 };
304
    if( CSLFetchNameValue( papszHdr, "PROJECTION_PARAMETERS" ) )
305
    {
306
        char **papszTokens = CSLTokenizeString(
307
            CSLFetchNameValue( papszHdr, "PROJECTION_PARAMETERS" ) );
308
309
        for( int i = 0; i < 15 && papszTokens[i] != NULL; i++ )
310
            adfProjParams[i] = CPLAtofM( papszTokens[i] );
311
312
        CSLDestroy( papszTokens );
313
    }
314
#endif
315
316
    /* -------------------------------------------------------------------- */
317
    /*      Handle projections.                                             */
318
    /* -------------------------------------------------------------------- */
319
0
    const char *pszDatumName = CSLFetchNameValue(papszHdr, "DATUM_NAME");
320
321
0
    if (EQUAL(pszProjName, "UTM") && nZone != 0 && nZone > INT_MIN)
322
0
    {
323
        // Just getting that the negative zone for southern hemisphere is used.
324
0
        m_oSRS.SetUTM(std::abs(nZone), nZone > 0);
325
0
    }
326
327
0
    else if (EQUAL(pszProjName, "State Plane") && nZone != 0 && nZone > INT_MIN)
328
0
    {
329
0
        const int nPairs = sizeof(anUsgsEsriZones) / (2 * sizeof(int));
330
331
0
        for (int i = 0; i < nPairs; i++)
332
0
        {
333
0
            if (anUsgsEsriZones[i * 2 + 1] == nZone)
334
0
            {
335
0
                nZone = anUsgsEsriZones[i * 2];
336
0
                break;
337
0
            }
338
0
        }
339
340
0
        const char *pszUnits = CSLFetchNameValueDef(papszHdr, "MAP_UNITS", "");
341
0
        double dfUnits = 0.0;
342
0
        if (EQUAL(pszUnits, "feet"))
343
0
            dfUnits = CPLAtofM(SRS_UL_US_FOOT_CONV);
344
0
        else if (STARTS_WITH_CI(pszUnits, "MET"))
345
0
            dfUnits = 1.0;
346
0
        else
347
0
            pszUnits = nullptr;
348
349
0
        m_oSRS.SetStatePlane(std::abs(nZone),
350
0
                             pszDatumName == nullptr ||
351
0
                                 !EQUAL(pszDatumName, "NAD27"),
352
0
                             pszUnits, dfUnits);
353
0
    }
354
355
    /* -------------------------------------------------------------------- */
356
    /*      Setup the geographic coordinate system.                         */
357
    /* -------------------------------------------------------------------- */
358
0
    if (m_oSRS.GetAttrNode("GEOGCS") == nullptr)
359
0
    {
360
0
        const char *pszSpheroidName =
361
0
            CSLFetchNameValue(papszHdr, "SPHEROID_NAME");
362
0
        const char *pszSemiMajor =
363
0
            CSLFetchNameValue(papszHdr, "SEMI_MAJOR_AXIS");
364
0
        const char *pszSemiMinor =
365
0
            CSLFetchNameValue(papszHdr, "SEMI_MINOR_AXIS");
366
0
        if (pszDatumName != nullptr &&
367
0
            m_oSRS.SetWellKnownGeogCS(pszDatumName) == OGRERR_NONE)
368
0
        {
369
            // good
370
0
        }
371
0
        else if (pszSpheroidName && pszSemiMajor && pszSemiMinor)
372
0
        {
373
0
            const double dfSemiMajor = CPLAtofM(pszSemiMajor);
374
0
            const double dfSemiMinor = CPLAtofM(pszSemiMinor);
375
376
0
            m_oSRS.SetGeogCS(pszSpheroidName, pszSpheroidName, pszSpheroidName,
377
0
                             dfSemiMajor,
378
0
                             (dfSemiMajor == 0.0 || dfSemiMajor == dfSemiMinor)
379
0
                                 ? 0.0
380
0
                                 : 1.0 / (1.0 - dfSemiMinor / dfSemiMajor));
381
0
        }
382
0
        else  // fallback default.
383
0
            m_oSRS.SetWellKnownGeogCS("WGS84");
384
0
    }
385
0
}
386
387
/************************************************************************/
388
/*                                Open()                                */
389
/************************************************************************/
390
391
GDALDataset *GenBinDataset::Open(GDALOpenInfo *poOpenInfo)
392
393
11.5k
{
394
    /* -------------------------------------------------------------------- */
395
    /*      We assume the user is pointing to the binary (i.e. .bil) file.  */
396
    /* -------------------------------------------------------------------- */
397
11.5k
    if (poOpenInfo->nHeaderBytes < 2 || poOpenInfo->fpL == nullptr)
398
467
        return nullptr;
399
400
    /* -------------------------------------------------------------------- */
401
    /*      Now we need to tear apart the filename to form a .HDR           */
402
    /*      filename.                                                       */
403
    /* -------------------------------------------------------------------- */
404
11.1k
    const CPLString osPath = CPLGetPathSafe(poOpenInfo->pszFilename);
405
11.1k
    const CPLString osName = CPLGetBasenameSafe(poOpenInfo->pszFilename);
406
11.1k
    CPLString osHDRFilename;
407
408
11.1k
    char **papszSiblingFiles = poOpenInfo->GetSiblingFiles();
409
11.1k
    if (papszSiblingFiles)
410
11.0k
    {
411
11.0k
        const int iFile =
412
11.0k
            CSLFindString(papszSiblingFiles,
413
11.0k
                          CPLFormFilenameSafe(nullptr, osName, "hdr").c_str());
414
11.0k
        if (iFile < 0)  // return if there is no corresponding .hdr file
415
10.4k
            return nullptr;
416
417
601
        osHDRFilename =
418
601
            CPLFormFilenameSafe(osPath, papszSiblingFiles[iFile], nullptr);
419
601
    }
420
89
    else
421
89
    {
422
89
        osHDRFilename = CPLFormCIFilenameSafe(osPath, osName, "hdr");
423
89
    }
424
425
690
    const bool bSelectedHDR = EQUAL(osHDRFilename, poOpenInfo->pszFilename);
426
427
    /* -------------------------------------------------------------------- */
428
    /*      Do we have a .hdr file?                                         */
429
    /* -------------------------------------------------------------------- */
430
690
    VSILFILE *fp = VSIFOpenL(osHDRFilename, "r");
431
690
    if (fp == nullptr)
432
89
    {
433
89
        return nullptr;
434
89
    }
435
436
    /* -------------------------------------------------------------------- */
437
    /*      Read a chunk to skim for expected keywords.                     */
438
    /* -------------------------------------------------------------------- */
439
601
    char achHeader[1000] = {'\0'};
440
441
601
    const int nRead =
442
601
        static_cast<int>(VSIFReadL(achHeader, 1, sizeof(achHeader) - 1, fp));
443
601
    achHeader[nRead] = '\0';
444
601
    CPL_IGNORE_RET_VAL(VSIFSeekL(fp, 0, SEEK_SET));
445
446
601
    if (strstr(achHeader, "BANDS:") == nullptr ||
447
601
        strstr(achHeader, "ROWS:") == nullptr ||
448
601
        strstr(achHeader, "COLS:") == nullptr)
449
601
    {
450
601
        CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
451
601
        return nullptr;
452
601
    }
453
454
    /* -------------------------------------------------------------------- */
455
    /*      Has the user selected the .hdr file to open?                    */
456
    /* -------------------------------------------------------------------- */
457
0
    if (bSelectedHDR)
458
0
    {
459
0
        CPLError(
460
0
            CE_Failure, CPLE_AppDefined,
461
0
            "The selected file is an Generic Binary header file, but to "
462
0
            "open Generic Binary datasets, the data file should be selected "
463
0
            "instead of the .hdr file.  Please try again selecting"
464
0
            "the raw data file corresponding to the header file: %s",
465
0
            poOpenInfo->pszFilename);
466
0
        CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
467
0
        return nullptr;
468
0
    }
469
470
    /* -------------------------------------------------------------------- */
471
    /*      Read the .hdr file.                                             */
472
    /* -------------------------------------------------------------------- */
473
0
    char **papszHdr = nullptr;
474
0
    const char *pszLine = CPLReadLineL(fp);
475
476
0
    while (pszLine != nullptr)
477
0
    {
478
0
        if (EQUAL(pszLine, "PROJECTION_PARAMETERS:"))
479
0
        {
480
0
            CPLString osPP = pszLine;
481
482
0
            pszLine = CPLReadLineL(fp);
483
0
            while (pszLine != nullptr && (*pszLine == '\t' || *pszLine == ' '))
484
0
            {
485
0
                osPP += pszLine;
486
0
                pszLine = CPLReadLineL(fp);
487
0
            }
488
0
            papszHdr = CSLAddString(papszHdr, osPP);
489
0
        }
490
0
        else
491
0
        {
492
0
            char *pszName = nullptr;
493
0
            const char *pszKey = CPLParseNameValue(pszLine, &pszName);
494
0
            if (pszKey && pszName)
495
0
            {
496
0
                CPLString osValue = pszKey;
497
0
                osValue.Trim();
498
499
0
                papszHdr = CSLSetNameValue(papszHdr, pszName, osValue);
500
0
            }
501
0
            CPLFree(pszName);
502
503
0
            pszLine = CPLReadLineL(fp);
504
0
        }
505
0
    }
506
507
0
    CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
508
509
0
    if (CSLFetchNameValue(papszHdr, "COLS") == nullptr ||
510
0
        CSLFetchNameValue(papszHdr, "ROWS") == nullptr ||
511
0
        CSLFetchNameValue(papszHdr, "BANDS") == nullptr)
512
0
    {
513
0
        CSLDestroy(papszHdr);
514
0
        return nullptr;
515
0
    }
516
517
    /* -------------------------------------------------------------------- */
518
    /*      Create a corresponding GDALDataset.                             */
519
    /* -------------------------------------------------------------------- */
520
0
    auto poDS = std::make_unique<GenBinDataset>();
521
522
    /* -------------------------------------------------------------------- */
523
    /*      Capture some information from the file that is of interest.     */
524
    /* -------------------------------------------------------------------- */
525
0
    const int nBands = atoi(CSLFetchNameValue(papszHdr, "BANDS"));
526
527
0
    poDS->nRasterXSize = atoi(CSLFetchNameValue(papszHdr, "COLS"));
528
0
    poDS->nRasterYSize = atoi(CSLFetchNameValue(papszHdr, "ROWS"));
529
0
    poDS->papszHDR = papszHdr;
530
531
0
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
532
0
        !GDALCheckBandCount(nBands, FALSE))
533
0
    {
534
0
        return nullptr;
535
0
    }
536
537
0
    std::swap(poDS->fpImage, poOpenInfo->fpL);
538
0
    poDS->eAccess = poOpenInfo->eAccess;
539
540
    /* -------------------------------------------------------------------- */
541
    /*      Figure out the data type.                                       */
542
    /* -------------------------------------------------------------------- */
543
0
    const char *pszDataType = CSLFetchNameValue(papszHdr, "DATATYPE");
544
0
    GDALDataType eDataType = GDT_Byte;
545
0
    int nBits = -1;  // Only needed for partial byte types
546
547
0
    if (pszDataType == nullptr)
548
0
    {
549
        // nothing to do
550
0
    }
551
0
    else if (EQUAL(pszDataType, "U16"))
552
0
        eDataType = GDT_UInt16;
553
0
    else if (EQUAL(pszDataType, "S16"))
554
0
        eDataType = GDT_Int16;
555
0
    else if (EQUAL(pszDataType, "F32"))
556
0
        eDataType = GDT_Float32;
557
0
    else if (EQUAL(pszDataType, "F64"))
558
0
        eDataType = GDT_Float64;
559
0
    else if (EQUAL(pszDataType, "U8"))
560
0
    {
561
        // nothing to do
562
0
    }
563
0
    else if (EQUAL(pszDataType, "U1") || EQUAL(pszDataType, "U2") ||
564
0
             EQUAL(pszDataType, "U4"))
565
0
    {
566
0
        nBits = atoi(pszDataType + 1);
567
0
        if (nBands != 1)
568
0
        {
569
0
            CPLError(CE_Failure, CPLE_OpenFailed,
570
0
                     "Only one band is supported for U1/U2/U4 data type");
571
0
            return nullptr;
572
0
        }
573
0
    }
574
0
    else
575
0
    {
576
0
        CPLError(CE_Warning, CPLE_AppDefined,
577
0
                 "DATATYPE=%s not recognised, assuming Byte.", pszDataType);
578
0
    }
579
580
    /* -------------------------------------------------------------------- */
581
    /*      Do we need byte swapping?                                       */
582
    /* -------------------------------------------------------------------- */
583
584
0
    RawRasterBand::ByteOrder eByteOrder = RawRasterBand::NATIVE_BYTE_ORDER;
585
586
0
    const char *pszByteOrder = CSLFetchNameValue(papszHdr, "BYTE_ORDER");
587
0
    if (pszByteOrder)
588
0
    {
589
0
        eByteOrder = EQUAL(pszByteOrder, "LSB")
590
0
                         ? RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN
591
0
                         : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
592
0
    }
593
594
    /* -------------------------------------------------------------------- */
595
    /*      Work out interleaving info.                                     */
596
    /* -------------------------------------------------------------------- */
597
0
    const int nItemSize = GDALGetDataTypeSizeBytes(eDataType);
598
0
    int nPixelOffset = 0;
599
0
    int nLineOffset = 0;
600
0
    vsi_l_offset nBandOffset = 0;
601
0
    bool bIntOverflow = false;
602
603
0
    const char *pszInterleaving = CSLFetchNameValue(papszHdr, "INTERLEAVING");
604
0
    if (pszInterleaving == nullptr)
605
0
        pszInterleaving = "BIL";
606
607
0
    if (EQUAL(pszInterleaving, "BSQ") || EQUAL(pszInterleaving, "NA"))
608
0
    {
609
0
        nPixelOffset = nItemSize;
610
0
        if (nItemSize <= 0 || poDS->nRasterXSize > INT_MAX / nItemSize)
611
0
            bIntOverflow = true;
612
0
        else
613
0
        {
614
0
            nLineOffset = nItemSize * poDS->nRasterXSize;
615
0
            nBandOffset =
616
0
                nLineOffset * static_cast<vsi_l_offset>(poDS->nRasterYSize);
617
0
        }
618
0
    }
619
0
    else if (EQUAL(pszInterleaving, "BIP"))
620
0
    {
621
0
        nPixelOffset = nItemSize * nBands;
622
0
        if (nPixelOffset == 0 || poDS->nRasterXSize > INT_MAX / nPixelOffset)
623
0
            bIntOverflow = true;
624
0
        else
625
0
        {
626
0
            nLineOffset = nPixelOffset * poDS->nRasterXSize;
627
0
            nBandOffset = nItemSize;
628
0
        }
629
0
    }
630
0
    else
631
0
    {
632
0
        if (!EQUAL(pszInterleaving, "BIL"))
633
0
            CPLError(CE_Warning, CPLE_AppDefined,
634
0
                     "INTERLEAVING:%s not recognised, assume BIL.",
635
0
                     pszInterleaving);
636
637
0
        nPixelOffset = nItemSize;
638
0
        if (nPixelOffset == 0 || nBands == 0 ||
639
0
            poDS->nRasterXSize > INT_MAX / (nPixelOffset * nBands))
640
0
            bIntOverflow = true;
641
0
        else
642
0
        {
643
0
            nLineOffset = nPixelOffset * nBands * poDS->nRasterXSize;
644
0
            nBandOffset =
645
0
                nItemSize * static_cast<vsi_l_offset>(poDS->nRasterXSize);
646
0
        }
647
0
    }
648
649
0
    if (bIntOverflow)
650
0
    {
651
0
        CPLError(CE_Failure, CPLE_AppDefined, "Int overflow occurred.");
652
0
        return nullptr;
653
0
    }
654
655
0
    if (nBits < 0 &&
656
0
        !RAWDatasetCheckMemoryUsage(poDS->nRasterXSize, poDS->nRasterYSize,
657
0
                                    nBands, nItemSize, nPixelOffset,
658
0
                                    nLineOffset, 0, nBandOffset, poDS->fpImage))
659
0
    {
660
0
        return nullptr;
661
0
    }
662
663
0
    poDS->SetDescription(poOpenInfo->pszFilename);
664
0
    poDS->PamInitialize();
665
666
    /* -------------------------------------------------------------------- */
667
    /*      Create band information objects.                                */
668
    /* -------------------------------------------------------------------- */
669
0
    for (int i = 0; i < nBands; i++)
670
0
    {
671
0
        if (nBits != -1)
672
0
        {
673
0
            poDS->SetBand(i + 1, new GenBinBitRasterBand(poDS.get(), nBits));
674
0
        }
675
0
        else
676
0
        {
677
0
            auto poBand = RawRasterBand::Create(
678
0
                poDS.get(), i + 1, poDS->fpImage, nBandOffset * i, nPixelOffset,
679
0
                nLineOffset, eDataType, eByteOrder, RawRasterBand::OwnFP::NO);
680
0
            if (!poBand)
681
0
                return nullptr;
682
0
            poDS->SetBand(i + 1, std::move(poBand));
683
0
        }
684
0
    }
685
686
    /* -------------------------------------------------------------------- */
687
    /*      Get geotransform.                                               */
688
    /* -------------------------------------------------------------------- */
689
0
    if (poDS->nRasterXSize > 1 && poDS->nRasterYSize > 1 &&
690
0
        CSLFetchNameValue(papszHdr, "UL_X_COORDINATE") != nullptr &&
691
0
        CSLFetchNameValue(papszHdr, "UL_Y_COORDINATE") != nullptr &&
692
0
        CSLFetchNameValue(papszHdr, "LR_X_COORDINATE") != nullptr &&
693
0
        CSLFetchNameValue(papszHdr, "LR_Y_COORDINATE") != nullptr)
694
0
    {
695
0
        const double dfULX =
696
0
            CPLAtofM(CSLFetchNameValue(papszHdr, "UL_X_COORDINATE"));
697
0
        const double dfULY =
698
0
            CPLAtofM(CSLFetchNameValue(papszHdr, "UL_Y_COORDINATE"));
699
0
        const double dfLRX =
700
0
            CPLAtofM(CSLFetchNameValue(papszHdr, "LR_X_COORDINATE"));
701
0
        const double dfLRY =
702
0
            CPLAtofM(CSLFetchNameValue(papszHdr, "LR_Y_COORDINATE"));
703
704
0
        poDS->adfGeoTransform[1] = (dfLRX - dfULX) / (poDS->nRasterXSize - 1);
705
0
        poDS->adfGeoTransform[2] = 0.0;
706
0
        poDS->adfGeoTransform[4] = 0.0;
707
0
        poDS->adfGeoTransform[5] = (dfLRY - dfULY) / (poDS->nRasterYSize - 1);
708
709
0
        poDS->adfGeoTransform[0] = dfULX - poDS->adfGeoTransform[1] * 0.5;
710
0
        poDS->adfGeoTransform[3] = dfULY - poDS->adfGeoTransform[5] * 0.5;
711
712
0
        poDS->bGotTransform = true;
713
0
    }
714
715
    /* -------------------------------------------------------------------- */
716
    /*      Try and parse the coordinate system.                            */
717
    /* -------------------------------------------------------------------- */
718
0
    poDS->ParseCoordinateSystem(papszHdr);
719
720
    /* -------------------------------------------------------------------- */
721
    /*      Initialize any PAM information.                                 */
722
    /* -------------------------------------------------------------------- */
723
0
    poDS->TryLoadXML();
724
725
    /* -------------------------------------------------------------------- */
726
    /*      Check for overviews.                                            */
727
    /* -------------------------------------------------------------------- */
728
0
    poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
729
730
0
    return poDS.release();
731
0
}
732
733
/************************************************************************/
734
/*                         GDALRegister_GenBin()                        */
735
/************************************************************************/
736
737
void GDALRegister_GenBin()
738
739
2
{
740
2
    if (GDALGetDriverByName("GenBin") != nullptr)
741
0
        return;
742
743
2
    GDALDriver *poDriver = new GDALDriver();
744
745
2
    poDriver->SetDescription("GenBin");
746
2
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
747
2
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
748
2
                              "Generic Binary (.hdr Labelled)");
749
2
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/genbin.html");
750
2
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
751
752
2
    poDriver->pfnOpen = GenBinDataset::Open;
753
754
2
    GetGDALDriverManager()->RegisterDriver(poDriver);
755
2
}