Coverage Report

Created: 2026-08-11 08:26

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