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  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "cpl_string.h"
31 #include "gdal_frmts.h"
32 #include "ogr_spatialref.h"
33 #include "rawdataset.h"
34 
35 #include <cstdlib>
36 
37 CPL_CVSID("$Id: genbindataset.cpp 18c08feb1540d58a5076265e837d49ace106f302 2021-03-08 23:30:56 +0100 Even Rouault $")
38 
39 /* ==================================================================== */
40 /*      Table relating USGS and ESRI state plane zones.                 */
41 /* ==================================================================== */
42 constexpr int anUsgsEsriZones[] =
43 {
44   101, 3101,
45   102, 3126,
46   201, 3151,
47   202, 3176,
48   203, 3201,
49   301, 3226,
50   302, 3251,
51   401, 3276,
52   402, 3301,
53   403, 3326,
54   404, 3351,
55   405, 3376,
56   406, 3401,
57   407, 3426,
58   501, 3451,
59   502, 3476,
60   503, 3501,
61   600, 3526,
62   700, 3551,
63   901, 3601,
64   902, 3626,
65   903, 3576,
66  1001, 3651,
67  1002, 3676,
68  1101, 3701,
69  1102, 3726,
70  1103, 3751,
71  1201, 3776,
72  1202, 3801,
73  1301, 3826,
74  1302, 3851,
75  1401, 3876,
76  1402, 3901,
77  1501, 3926,
78  1502, 3951,
79  1601, 3976,
80  1602, 4001,
81  1701, 4026,
82  1702, 4051,
83  1703, 6426,
84  1801, 4076,
85  1802, 4101,
86  1900, 4126,
87  2001, 4151,
88  2002, 4176,
89  2101, 4201,
90  2102, 4226,
91  2103, 4251,
92  2111, 6351,
93  2112, 6376,
94  2113, 6401,
95  2201, 4276,
96  2202, 4301,
97  2203, 4326,
98  2301, 4351,
99  2302, 4376,
100  2401, 4401,
101  2402, 4426,
102  2403, 4451,
103  2500,    0,
104  2501, 4476,
105  2502, 4501,
106  2503, 4526,
107  2600,    0,
108  2601, 4551,
109  2602, 4576,
110  2701, 4601,
111  2702, 4626,
112  2703, 4651,
113  2800, 4676,
114  2900, 4701,
115  3001, 4726,
116  3002, 4751,
117  3003, 4776,
118  3101, 4801,
119  3102, 4826,
120  3103, 4851,
121  3104, 4876,
122  3200, 4901,
123  3301, 4926,
124  3302, 4951,
125  3401, 4976,
126  3402, 5001,
127  3501, 5026,
128  3502, 5051,
129  3601, 5076,
130  3602, 5101,
131  3701, 5126,
132  3702, 5151,
133  3800, 5176,
134  3900,    0,
135  3901, 5201,
136  3902, 5226,
137  4001, 5251,
138  4002, 5276,
139  4100, 5301,
140  4201, 5326,
141  4202, 5351,
142  4203, 5376,
143  4204, 5401,
144  4205, 5426,
145  4301, 5451,
146  4302, 5476,
147  4303, 5501,
148  4400, 5526,
149  4501, 5551,
150  4502, 5576,
151  4601, 5601,
152  4602, 5626,
153  4701, 5651,
154  4702, 5676,
155  4801, 5701,
156  4802, 5726,
157  4803, 5751,
158  4901, 5776,
159  4902, 5801,
160  4903, 5826,
161  4904, 5851,
162  5001, 6101,
163  5002, 6126,
164  5003, 6151,
165  5004, 6176,
166  5005, 6201,
167  5006, 6226,
168  5007, 6251,
169  5008, 6276,
170  5009, 6301,
171  5010, 6326,
172  5101, 5876,
173  5102, 5901,
174  5103, 5926,
175  5104, 5951,
176  5105, 5976,
177  5201, 6001,
178  5200, 6026,
179  5200, 6076,
180  5201, 6051,
181  5202, 6051,
182  5300,    0,
183  5400,    0
184 };
185 
186 /************************************************************************/
187 /* ==================================================================== */
188 /*                              GenBinDataset                           */
189 /* ==================================================================== */
190 /************************************************************************/
191 
192 class GenBinDataset final: public RawDataset
193 {
194     friend class GenBinBitRasterBand;
195 
196     VSILFILE    *fpImage;  // image data file.
197 
198     bool        bGotTransform;
199     double      adfGeoTransform[6];
200     char       *pszProjection;
201 
202     char      **papszHDR;
203 
204     void        ParseCoordinateSystem( char ** );
205 
206     CPL_DISALLOW_COPY_ASSIGN(GenBinDataset)
207 
208   public:
209     GenBinDataset();
210     ~GenBinDataset() override;
211 
212     CPLErr GetGeoTransform( double * padfTransform ) override;
213     const char *_GetProjectionRef(void) override;
GetSpatialRef() const214     const OGRSpatialReference* GetSpatialRef() const override {
215         return GetSpatialRefFromOldGetProjectionRef();
216     }
217 
218     char **GetFileList() override;
219 
220     static GDALDataset *Open( GDALOpenInfo * );
221 };
222 
223 /************************************************************************/
224 /* ==================================================================== */
225 /*                       GenBinBitRasterBand                            */
226 /* ==================================================================== */
227 /************************************************************************/
228 
229 class GenBinBitRasterBand final: public GDALPamRasterBand
230 {
231     int            nBits;
232 
233     CPL_DISALLOW_COPY_ASSIGN(GenBinBitRasterBand)
234 
235   public:
236     GenBinBitRasterBand( GenBinDataset *poDS, int nBits );
~GenBinBitRasterBand()237     ~GenBinBitRasterBand() override {}
238 
239     CPLErr IReadBlock( int, int, void * ) override;
240 };
241 
242 /************************************************************************/
243 /*                        GenBinBitRasterBand()                         */
244 /************************************************************************/
245 
GenBinBitRasterBand(GenBinDataset * poDSIn,int nBitsIn)246 GenBinBitRasterBand::GenBinBitRasterBand( GenBinDataset *poDSIn, int nBitsIn ) :
247     nBits(nBitsIn)
248 {
249     SetMetadataItem( "NBITS",
250                      CPLString().Printf("%d",nBitsIn),
251                      "IMAGE_STRUCTURE" );
252 
253     poDS = poDSIn;
254     nBand = 1;
255 
256     eDataType = GDT_Byte;
257 
258     nBlockXSize = poDSIn->nRasterXSize;
259     nBlockYSize = 1;
260 }
261 
262 /************************************************************************/
263 /*                             IReadBlock()                             */
264 /************************************************************************/
265 
IReadBlock(int,int nBlockYOff,void * pImage)266 CPLErr GenBinBitRasterBand::IReadBlock( int /* nBlockXOff */,
267                                         int nBlockYOff,
268                                         void * pImage )
269 
270 {
271     GenBinDataset *poGDS = reinterpret_cast<GenBinDataset *>( poDS );
272 
273 /* -------------------------------------------------------------------- */
274 /*      Establish desired position.                                     */
275 /* -------------------------------------------------------------------- */
276     const vsi_l_offset nLineStart
277         = (static_cast<vsi_l_offset>( nBlockXSize ) * nBlockYOff * nBits) / 8;
278     int iBitOffset = static_cast<int>(
279         ( static_cast<vsi_l_offset>( nBlockXSize ) * nBlockYOff * nBits) % 8 );
280     const unsigned int nLineBytes = static_cast<unsigned int>(
281         ( static_cast<vsi_l_offset>( nBlockXSize ) * (nBlockYOff+1) * nBits + 7)
282         / 8 - nLineStart);
283 
284 /* -------------------------------------------------------------------- */
285 /*      Read data into buffer.                                          */
286 /* -------------------------------------------------------------------- */
287     GByte *pabyBuffer = static_cast<GByte *>( CPLCalloc( nLineBytes, 1 ) );
288 
289     if( VSIFSeekL( poGDS->fpImage, nLineStart, SEEK_SET ) != 0
290         || VSIFReadL( pabyBuffer, 1, nLineBytes, poGDS->fpImage) != nLineBytes )
291     {
292         CPLError( CE_Failure, CPLE_FileIO,
293                   "Failed to read %u bytes at offset %lu.\n%s",
294                   nLineBytes, static_cast<unsigned long>(nLineStart),
295                   VSIStrerror( errno ) );
296         CPLFree( pabyBuffer );
297         return CE_Failure;
298     }
299 
300 /* -------------------------------------------------------------------- */
301 /*      Copy data, promoting to 8bit.                                   */
302 /* -------------------------------------------------------------------- */
303     GByte *pafImage = reinterpret_cast<GByte *>( pImage );
304     if( nBits == 1 )
305     {
306         for( int iX = 0; iX < nBlockXSize; iX++, iBitOffset += nBits )
307         {
308             if( pabyBuffer[iBitOffset>>3]  & (0x80 >>(iBitOffset & 7)) )
309                 pafImage[iX] = 1;
310             else
311                 pafImage[iX] = 0;
312         }
313     }
314     else if( nBits == 2 )
315     {
316         for( int iX = 0; iX < nBlockXSize; iX++, iBitOffset += nBits )
317         {
318             pafImage[iX] =
319                 (pabyBuffer[iBitOffset>>3]) >> (6-(iBitOffset&0x7)) & 0x3;
320         }
321     }
322     else if( nBits == 4 )
323     {
324         for( int iX = 0; iX < nBlockXSize; iX++, iBitOffset += nBits )
325         {
326             if( iBitOffset == 0 )
327                 pafImage[iX] = (pabyBuffer[iBitOffset>>3]) >> 4;
328             else
329                 pafImage[iX] = (pabyBuffer[iBitOffset>>3]) & 0xf;
330         }
331     }
332     else {
333         CPLAssert( false );
334     }
335 
336     CPLFree( pabyBuffer );
337 
338     return CE_None;
339 }
340 
341 /************************************************************************/
342 /* ==================================================================== */
343 /*                              GenBinDataset                           */
344 /* ==================================================================== */
345 /************************************************************************/
346 
347 /************************************************************************/
348 /*                            GenBinDataset()                             */
349 /************************************************************************/
350 
GenBinDataset()351 GenBinDataset::GenBinDataset() :
352     fpImage(nullptr),
353     bGotTransform(false),
354     pszProjection(CPLStrdup("")),
355     papszHDR(nullptr)
356 {
357     adfGeoTransform[0] = 0.0;
358     adfGeoTransform[1] = 1.0;
359     adfGeoTransform[2] = 0.0;
360     adfGeoTransform[3] = 0.0;
361     adfGeoTransform[4] = 0.0;
362     adfGeoTransform[5] = 1.0;
363 }
364 
365 /************************************************************************/
366 /*                            ~GenBinDataset()                            */
367 /************************************************************************/
368 
~GenBinDataset()369 GenBinDataset::~GenBinDataset()
370 
371 {
372     FlushCache();
373 
374     if( fpImage != nullptr )
375         CPL_IGNORE_RET_VAL(VSIFCloseL( fpImage ));
376 
377     CPLFree( pszProjection );
378     CSLDestroy( papszHDR );
379 }
380 
381 /************************************************************************/
382 /*                          GetProjectionRef()                          */
383 /************************************************************************/
384 
_GetProjectionRef()385 const char *GenBinDataset::_GetProjectionRef()
386 
387 {
388     if (pszProjection && strlen(pszProjection) > 0)
389         return pszProjection;
390 
391     return GDALPamDataset::_GetProjectionRef();
392 }
393 
394 /************************************************************************/
395 /*                          GetGeoTransform()                           */
396 /************************************************************************/
397 
GetGeoTransform(double * padfTransform)398 CPLErr GenBinDataset::GetGeoTransform( double * padfTransform )
399 
400 {
401     if( bGotTransform )
402     {
403         memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 );
404         return CE_None;
405     }
406 
407     return GDALPamDataset::GetGeoTransform( padfTransform );
408 }
409 
410 /************************************************************************/
411 /*                            GetFileList()                             */
412 /************************************************************************/
413 
GetFileList()414 char **GenBinDataset::GetFileList()
415 
416 {
417     const CPLString osPath = CPLGetPath( GetDescription() );
418     const CPLString osName = CPLGetBasename( GetDescription() );
419 
420     // Main data file, etc.
421     char **papszFileList = GDALPamDataset::GetFileList();
422 
423     // Header file.
424     const CPLString osFilename = CPLFormCIFilename( osPath, osName, "hdr" );
425     papszFileList = CSLAddString( papszFileList, osFilename );
426 
427     return papszFileList;
428 }
429 
430 /************************************************************************/
431 /*                       ParseCoordinateSystem()                        */
432 /************************************************************************/
433 
ParseCoordinateSystem(char ** papszHdr)434 void GenBinDataset::ParseCoordinateSystem( char **papszHdr )
435 
436 {
437     const char *pszProjName = CSLFetchNameValue( papszHdr, "PROJECTION_NAME" );
438     if( pszProjName == nullptr )
439         return;
440 
441 /* -------------------------------------------------------------------- */
442 /*      Translate zone and parameters into numeric form.                */
443 /* -------------------------------------------------------------------- */
444     int nZone = 0;
445     if( CSLFetchNameValue( papszHdr, "PROJECTION_ZONE" ) )
446         nZone = atoi(CSLFetchNameValue( papszHdr, "PROJECTION_ZONE" ));
447 
448 #if 0
449     // TODO(schwehr): Why was this being done but not used?
450     double adfProjParams[15] = { 0.0 };
451     if( CSLFetchNameValue( papszHdr, "PROJECTION_PARAMETERS" ) )
452     {
453         char **papszTokens = CSLTokenizeString(
454             CSLFetchNameValue( papszHdr, "PROJECTION_PARAMETERS" ) );
455 
456         for( int i = 0; i < 15 && papszTokens[i] != NULL; i++ )
457             adfProjParams[i] = CPLAtofM( papszTokens[i] );
458 
459         CSLDestroy( papszTokens );
460     }
461 #endif
462 
463 /* -------------------------------------------------------------------- */
464 /*      Handle projections.                                             */
465 /* -------------------------------------------------------------------- */
466     const char *pszDatumName = CSLFetchNameValue( papszHdr, "DATUM_NAME" );
467     OGRSpatialReference oSRS;
468 
469     if( EQUAL(pszProjName,"UTM") && nZone != 0 )
470     {
471         // Just getting that the negative zone for southern hemisphere is used.
472         oSRS.SetUTM( std::abs(nZone), nZone > 0 );
473     }
474 
475     else if( EQUAL(pszProjName,"State Plane") && nZone != 0 )
476     {
477         const int nPairs = sizeof(anUsgsEsriZones) / (2 * sizeof(int));
478 
479         for( int i = 0; i < nPairs; i++ )
480         {
481             if( anUsgsEsriZones[i*2+1] == nZone )
482             {
483                 nZone = anUsgsEsriZones[i*2];
484                 break;
485             }
486         }
487 
488         const char *pszUnits = CSLFetchNameValueDef( papszHdr, "MAP_UNITS", "" );
489         double dfUnits = 0.0;
490         if( EQUAL(pszUnits,"feet") )
491             dfUnits = CPLAtofM(SRS_UL_US_FOOT_CONV);
492         else if( STARTS_WITH_CI(pszUnits, "MET") )
493             dfUnits = 1.0;
494         else
495             pszUnits = nullptr;
496 
497         oSRS.SetStatePlane( std::abs(nZone),
498                             pszDatumName==nullptr || !EQUAL(pszDatumName,"NAD27"),
499                             pszUnits, dfUnits );
500     }
501 
502 /* -------------------------------------------------------------------- */
503 /*      Setup the geographic coordinate system.                         */
504 /* -------------------------------------------------------------------- */
505     if( oSRS.GetAttrNode( "GEOGCS" ) == nullptr )
506     {
507         const char* pszSpheroidName =
508             CSLFetchNameValue( papszHdr, "SPHEROID_NAME" );
509         const char* pszSemiMajor =
510             CSLFetchNameValue( papszHdr, "SEMI_MAJOR_AXIS");
511         const char* pszSemiMinor =
512             CSLFetchNameValue( papszHdr, "SEMI_MINOR_AXIS");
513         if( pszDatumName != nullptr
514             && oSRS.SetWellKnownGeogCS( pszDatumName ) == OGRERR_NONE )
515         {
516             // good
517         }
518         else if( pszSpheroidName && pszSemiMajor && pszSemiMinor )
519         {
520             const double dfSemiMajor = CPLAtofM(pszSemiMajor);
521             const double dfSemiMinor = CPLAtofM(pszSemiMinor);
522 
523             oSRS.SetGeogCS( pszSpheroidName,
524                             pszSpheroidName,
525                             pszSpheroidName,
526                             dfSemiMajor,
527                             (dfSemiMajor == 0.0 ||
528                              dfSemiMajor == dfSemiMinor) ? 0.0 :
529                                 1.0 / (1.0 - dfSemiMinor/dfSemiMajor) );
530         }
531         else // fallback default.
532             oSRS.SetWellKnownGeogCS( "WGS84" );
533     }
534 
535 /* -------------------------------------------------------------------- */
536 /*      Convert to WKT.                                                 */
537 /* -------------------------------------------------------------------- */
538     CPLFree( pszProjection );
539     pszProjection = nullptr;
540 
541     oSRS.exportToWkt( &pszProjection );
542 }
543 
544 /************************************************************************/
545 /*                                Open()                                */
546 /************************************************************************/
547 
Open(GDALOpenInfo * poOpenInfo)548 GDALDataset *GenBinDataset::Open( GDALOpenInfo * poOpenInfo )
549 
550 {
551 /* -------------------------------------------------------------------- */
552 /*      We assume the user is pointing to the binary (i.e. .bil) file.  */
553 /* -------------------------------------------------------------------- */
554     if( poOpenInfo->nHeaderBytes < 2 || poOpenInfo->fpL == nullptr )
555         return nullptr;
556 
557 /* -------------------------------------------------------------------- */
558 /*      Now we need to tear apart the filename to form a .HDR           */
559 /*      filename.                                                       */
560 /* -------------------------------------------------------------------- */
561     const CPLString osPath = CPLGetPath( poOpenInfo->pszFilename );
562     const CPLString osName = CPLGetBasename( poOpenInfo->pszFilename );
563     CPLString osHDRFilename;
564 
565     char** papszSiblingFiles = poOpenInfo->GetSiblingFiles();
566     if( papszSiblingFiles )
567     {
568         const int iFile = CSLFindString(
569             papszSiblingFiles, CPLFormFilename( nullptr, osName, "hdr" ) );
570         if( iFile < 0 ) // return if there is no corresponding .hdr file
571             return nullptr;
572 
573         osHDRFilename =
574             CPLFormFilename( osPath, papszSiblingFiles[iFile], nullptr );
575     }
576     else
577     {
578         osHDRFilename = CPLFormCIFilename( osPath, osName, "hdr" );
579     }
580 
581     const bool bSelectedHDR = EQUAL( osHDRFilename, poOpenInfo->pszFilename );
582 
583 /* -------------------------------------------------------------------- */
584 /*      Do we have a .hdr file?                                         */
585 /* -------------------------------------------------------------------- */
586     VSILFILE *fp = VSIFOpenL( osHDRFilename, "r" );
587     if( fp == nullptr )
588     {
589         return nullptr;
590     }
591 
592 /* -------------------------------------------------------------------- */
593 /*      Read a chunk to skim for expected keywords.                     */
594 /* -------------------------------------------------------------------- */
595     char achHeader[1000] = { '\0' };
596 
597     const int nRead =
598         static_cast<int>(VSIFReadL( achHeader, 1, sizeof(achHeader) - 1, fp ));
599     achHeader[nRead] = '\0';
600     CPL_IGNORE_RET_VAL(VSIFSeekL( fp, 0, SEEK_SET ));
601 
602     if( strstr( achHeader, "BANDS:" ) == nullptr
603         || strstr( achHeader, "ROWS:" ) == nullptr
604         || strstr( achHeader, "COLS:" ) == nullptr )
605     {
606         CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
607         return nullptr;
608     }
609 
610 /* -------------------------------------------------------------------- */
611 /*      Has the user selected the .hdr file to open?                    */
612 /* -------------------------------------------------------------------- */
613     if( bSelectedHDR )
614     {
615         CPLError(
616             CE_Failure, CPLE_AppDefined,
617             "The selected file is an Generic Binary header file, but to "
618             "open Generic Binary datasets, the data file should be selected "
619             "instead of the .hdr file.  Please try again selecting"
620             "the raw data file corresponding to the header file: %s",
621             poOpenInfo->pszFilename );
622         CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
623         return nullptr;
624     }
625 
626 /* -------------------------------------------------------------------- */
627 /*      Read the .hdr file.                                             */
628 /* -------------------------------------------------------------------- */
629     char **papszHdr = nullptr;
630     const char *pszLine = CPLReadLineL( fp );
631 
632     while( pszLine != nullptr )
633     {
634         if( EQUAL(pszLine,"PROJECTION_PARAMETERS:") )
635         {
636             CPLString osPP = pszLine;
637 
638             pszLine = CPLReadLineL(fp);
639             while( pszLine != nullptr
640                    && (*pszLine == '\t' || *pszLine == ' ') )
641             {
642                 osPP += pszLine;
643                 pszLine = CPLReadLineL(fp);
644             }
645             papszHdr = CSLAddString( papszHdr, osPP );
646         }
647         else
648         {
649             char *pszName = nullptr;
650             const char* pszKey = CPLParseNameValue( pszLine, &pszName );
651             if( pszKey && pszName )
652             {
653                 CPLString osValue = pszKey;
654                 osValue.Trim();
655 
656                 papszHdr = CSLSetNameValue( papszHdr, pszName, osValue );
657             }
658             CPLFree( pszName );
659 
660             pszLine = CPLReadLineL( fp );
661         }
662     }
663 
664     CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
665 
666     if( CSLFetchNameValue( papszHdr, "COLS" ) == nullptr
667         || CSLFetchNameValue( papszHdr, "ROWS" ) == nullptr
668         || CSLFetchNameValue( papszHdr, "BANDS" ) == nullptr )
669     {
670         CSLDestroy( papszHdr );
671         return nullptr;
672     }
673 
674 /* -------------------------------------------------------------------- */
675 /*      Create a corresponding GDALDataset.                             */
676 /* -------------------------------------------------------------------- */
677     GenBinDataset *poDS = new GenBinDataset();
678 
679 /* -------------------------------------------------------------------- */
680 /*      Capture some information from the file that is of interest.     */
681 /* -------------------------------------------------------------------- */
682     const int nBands = atoi(CSLFetchNameValue( papszHdr, "BANDS" ));
683 
684     poDS->nRasterXSize = atoi(CSLFetchNameValue( papszHdr, "COLS" ));
685     poDS->nRasterYSize = atoi(CSLFetchNameValue( papszHdr, "ROWS" ));
686     poDS->papszHDR = papszHdr;
687 
688     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
689         !GDALCheckBandCount(nBands, FALSE))
690     {
691         delete poDS;
692         return nullptr;
693     }
694 
695     poDS->fpImage = poOpenInfo->fpL;
696     poOpenInfo->fpL = nullptr;
697     poDS->eAccess = poOpenInfo->eAccess;
698 
699 /* -------------------------------------------------------------------- */
700 /*      Figure out the data type.                                       */
701 /* -------------------------------------------------------------------- */
702     const char *pszDataType = CSLFetchNameValue( papszHdr, "DATATYPE" );
703     GDALDataType eDataType = GDT_Unknown;
704     int nBits = -1; // Only needed for partial byte types
705 
706     if( pszDataType == nullptr )
707         eDataType = GDT_Byte;
708     else if( EQUAL(pszDataType,"U16") )
709         eDataType = GDT_UInt16;
710     else if( EQUAL(pszDataType,"S16") )
711         eDataType = GDT_Int16;
712     else if( EQUAL(pszDataType,"F32") )
713         eDataType = GDT_Float32;
714     else if( EQUAL(pszDataType,"F64") )
715         eDataType = GDT_Float64;
716     else if( EQUAL(pszDataType,"U8") )
717         eDataType = GDT_Byte;
718     else if( EQUAL(pszDataType,"U1")
719              || EQUAL(pszDataType,"U2")
720              || EQUAL(pszDataType,"U4") )
721     {
722         nBits = atoi(pszDataType+1);
723         eDataType = GDT_Byte;
724         if( nBands != 1 )
725         {
726             CPLError( CE_Failure, CPLE_OpenFailed,
727                       "Only one band is supported for U1/U2/U4 data type" );
728             delete poDS;
729             return nullptr;
730         }
731     }
732     else
733     {
734         eDataType = GDT_Byte;
735         CPLError( CE_Warning, CPLE_AppDefined,
736                   "DATATYPE=%s not recognised, assuming Byte.",
737                   pszDataType );
738     }
739 
740 /* -------------------------------------------------------------------- */
741 /*      Do we need byte swapping?                                       */
742 /* -------------------------------------------------------------------- */
743     const char *pszBYTE_ORDER = CSLFetchNameValue(papszHdr,"BYTE_ORDER");
744     bool bNative = true;
745 
746     if( pszBYTE_ORDER != nullptr )
747     {
748 #ifdef CPL_LSB
749         bNative = STARTS_WITH_CI(pszBYTE_ORDER, "LSB");
750 #else
751         bNative = !STARTS_WITH_CI(pszBYTE_ORDER, "LSB");
752 #endif
753     }
754 
755 /* -------------------------------------------------------------------- */
756 /*      Work out interleaving info.                                     */
757 /* -------------------------------------------------------------------- */
758     const int nItemSize = GDALGetDataTypeSizeBytes(eDataType);
759     int nPixelOffset = 0;
760     int nLineOffset = 0;
761     vsi_l_offset nBandOffset = 0;
762     bool bIntOverflow = false;
763 
764     const char *pszInterleaving = CSLFetchNameValue(papszHdr, "INTERLEAVING");
765     if( pszInterleaving == nullptr )
766         pszInterleaving = "BIL";
767 
768     if( EQUAL(pszInterleaving,"BSQ") || EQUAL(pszInterleaving,"NA") )
769     {
770         nPixelOffset = nItemSize;
771         if( poDS->nRasterXSize > INT_MAX / nItemSize )
772             bIntOverflow = true;
773         else
774         {
775             nLineOffset = nItemSize * poDS->nRasterXSize;
776             nBandOffset =
777                 nLineOffset * static_cast<vsi_l_offset>(poDS->nRasterYSize);
778         }
779     }
780     else if( EQUAL(pszInterleaving,"BIP") )
781     {
782         nPixelOffset = nItemSize * nBands;
783         if( nPixelOffset == 0 || poDS->nRasterXSize > INT_MAX / nPixelOffset )
784             bIntOverflow = true;
785         else
786         {
787             nLineOffset = nPixelOffset * poDS->nRasterXSize;
788             nBandOffset = nItemSize;
789         }
790     }
791     else
792     {
793         if( !EQUAL(pszInterleaving,"BIL") )
794             CPLError( CE_Warning, CPLE_AppDefined,
795                       "INTERLEAVING:%s not recognised, assume BIL.",
796                       pszInterleaving );
797 
798         nPixelOffset = nItemSize;
799         if( nPixelOffset == 0 || nBands == 0 ||
800             poDS->nRasterXSize > INT_MAX / (nPixelOffset * nBands) )
801             bIntOverflow = true;
802         else
803         {
804             nLineOffset = nPixelOffset * nBands * poDS->nRasterXSize;
805             nBandOffset = nItemSize * static_cast<vsi_l_offset>(poDS->nRasterXSize);
806         }
807     }
808 
809     if (bIntOverflow)
810     {
811         delete poDS;
812         CPLError( CE_Failure, CPLE_AppDefined,
813                   "Int overflow occurred." );
814         return nullptr;
815     }
816 
817     if( nBits < 0 &&
818         !RAWDatasetCheckMemoryUsage(
819                         poDS->nRasterXSize, poDS->nRasterYSize, nBands,
820                         nItemSize,
821                         nPixelOffset, nLineOffset, 0, nBandOffset,
822                         poDS->fpImage) )
823     {
824         delete poDS;
825         return nullptr;
826     }
827 
828     poDS->SetDescription( poOpenInfo->pszFilename );
829     poDS->PamInitialize();
830 
831 /* -------------------------------------------------------------------- */
832 /*      Create band information objects.                                */
833 /* -------------------------------------------------------------------- */
834     poDS->nBands = nBands;
835     for( int i = 0; i < poDS->nBands; i++ )
836     {
837         if( nBits != -1 )
838         {
839             poDS->SetBand( i+1, new GenBinBitRasterBand( poDS, nBits ) );
840         }
841         else
842         {
843             poDS->SetBand(
844                 i+1,
845                 new RawRasterBand( poDS, i+1, poDS->fpImage,
846                                    nBandOffset * i, nPixelOffset, nLineOffset,
847                                    eDataType, bNative,
848                                    RawRasterBand::OwnFP::NO ) );
849         }
850     }
851 
852 /* -------------------------------------------------------------------- */
853 /*      Get geotransform.                                               */
854 /* -------------------------------------------------------------------- */
855     if( poDS->nRasterXSize > 1 &&
856         poDS->nRasterYSize > 1 &&
857         CSLFetchNameValue(papszHdr,"UL_X_COORDINATE") != nullptr
858         && CSLFetchNameValue(papszHdr,"UL_Y_COORDINATE") != nullptr
859         && CSLFetchNameValue(papszHdr,"LR_X_COORDINATE") != nullptr
860         && CSLFetchNameValue(papszHdr,"LR_Y_COORDINATE") != nullptr )
861     {
862         const double dfULX =
863             CPLAtofM(CSLFetchNameValue(papszHdr,"UL_X_COORDINATE"));
864         const double dfULY =
865             CPLAtofM(CSLFetchNameValue(papszHdr,"UL_Y_COORDINATE"));
866         const double dfLRX =
867             CPLAtofM(CSLFetchNameValue(papszHdr,"LR_X_COORDINATE"));
868         const double dfLRY =
869             CPLAtofM(CSLFetchNameValue(papszHdr,"LR_Y_COORDINATE"));
870 
871         poDS->adfGeoTransform[1] = (dfLRX - dfULX) / (poDS->nRasterXSize-1);
872         poDS->adfGeoTransform[2] = 0.0;
873         poDS->adfGeoTransform[4] = 0.0;
874         poDS->adfGeoTransform[5] = (dfLRY - dfULY) / (poDS->nRasterYSize-1);
875 
876         poDS->adfGeoTransform[0] = dfULX - poDS->adfGeoTransform[1] * 0.5;
877         poDS->adfGeoTransform[3] = dfULY - poDS->adfGeoTransform[5] * 0.5;
878 
879         poDS->bGotTransform = true;
880     }
881 
882 /* -------------------------------------------------------------------- */
883 /*      Try and parse the coordinate system.                            */
884 /* -------------------------------------------------------------------- */
885     poDS->ParseCoordinateSystem( papszHdr );
886 
887 /* -------------------------------------------------------------------- */
888 /*      Initialize any PAM information.                                 */
889 /* -------------------------------------------------------------------- */
890     poDS->TryLoadXML();
891 
892 /* -------------------------------------------------------------------- */
893 /*      Check for overviews.                                            */
894 /* -------------------------------------------------------------------- */
895     poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
896 
897     return poDS;
898 }
899 
900 /************************************************************************/
901 /*                         GDALRegister_GenBin()                        */
902 /************************************************************************/
903 
GDALRegister_GenBin()904 void GDALRegister_GenBin()
905 
906 {
907     if( GDALGetDriverByName( "GenBin" ) != nullptr )
908         return;
909 
910     GDALDriver *poDriver = new GDALDriver();
911 
912     poDriver->SetDescription( "GenBin" );
913     poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
914     poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
915                                "Generic Binary (.hdr Labelled)" );
916     poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
917                                "drivers/raster/genbin.html" );
918     poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
919 
920     poDriver->pfnOpen = GenBinDataset::Open;
921 
922     GetGDALDriverManager()->RegisterDriver( poDriver );
923 }
924