1 /******************************************************************************
2  * Purpose:  ASRP/USRP Reader
3  * Author:   Frank Warmerdam (warmerdam@pobox.com)
4  *
5  * Derived from ADRG driver by Even Rouault, even.rouault at spatialys.com.
6  *
7  ******************************************************************************
8  * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
9  * Copyright (c) 2009, Frank Warmerdam
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_pam.h"
32 #include "gdal_frmts.h"
33 #include "iso8211.h"
34 #include "ogr_spatialref.h"
35 
36 #include <cstdlib>
37 #include <algorithm>
38 
39 // Uncomment to recognize also .gen files in addition to .img files
40 // #define OPEN_GEN
41 
42 CPL_CVSID("$Id: srpdataset.cpp fa752ad6eabafaf630a704e1892a9d837d683cb3 2021-03-06 17:04:38 +0100 Even Rouault $")
43 
44 class SRPDataset final: public GDALPamDataset
45 {
46     friend class SRPRasterBand;
47 
48     static CPLString ResetTo01( const char* str );
49 
50     VSILFILE*        fdIMG;
51     int*         TILEINDEX;
52     int          offsetInIMG;
53     CPLString    osProduct;
54     CPLString    osSRS;
55     CPLString    osGENFileName;
56     CPLString    osQALFileName;
57     CPLString    osIMGFileName;
58     int          NFC;
59     int          NFL;
60     int          ZNA;
61     double       LSO;
62     double       PSO;
63     double       LOD;
64     double       LAD;
65     int          ARV;
66     int          BRV;
67     int          PCB;
68     int          PVB;
69 
70     char**       papszSubDatasets;
71 
72     GDALColorTable oCT;
73 
74     static char** GetGENListFromTHF(const char* pszFileName);
75     static char** GetIMGListFromGEN(const char* pszFileName, int* pnRecordIndex = nullptr);
76     static SRPDataset* OpenDataset(const char* pszGENFileName, const char* pszIMGFileName, DDFRecord* record = nullptr);
77     static DDFRecord*  FindRecordInGENForIMG(DDFModule& module,
78         const char* pszGENFileName, const char* pszIMGFileName);
79 
80   public:
81     SRPDataset();
82     ~SRPDataset() override;
83 
84     const char *_GetProjectionRef(void) override;
GetSpatialRef() const85     const OGRSpatialReference* GetSpatialRef() const override {
86         return GetSpatialRefFromOldGetProjectionRef();
87     }
88     CPLErr GetGeoTransform( double * padfGeoTransform ) override;
89 
90     char **GetMetadata( const char * pszDomain = "" ) override;
91 
92     char **GetFileList() override;
93 
94     bool GetFromRecord( const char* pszFileName, DDFRecord *record );
95     void AddSubDataset( const char* pszGENFileName,
96                         const char* pszIMGFileName );
97     void AddMetadatafromFromTHF(const char* pszFileName);
98 
99     static GDALDataset *Open( GDALOpenInfo * );
100 };
101 
102 /************************************************************************/
103 /* ==================================================================== */
104 /*                            SRPRasterBand                            */
105 /* ==================================================================== */
106 /************************************************************************/
107 
108 class SRPRasterBand final: public GDALPamRasterBand
109 {
110     friend class SRPDataset;
111 
112   public:
113     SRPRasterBand( SRPDataset *, int );
114 
115     CPLErr IReadBlock( int, int, void * ) override;
116 
117     double GetNoDataValue( int *pbSuccess = nullptr ) override;
118 
119     GDALColorInterp GetColorInterpretation() override;
120     GDALColorTable *GetColorTable() override;
121 };
122 
123 /************************************************************************/
124 /*                           SRPRasterBand()                            */
125 /************************************************************************/
126 
SRPRasterBand(SRPDataset * poDSIn,int nBandIn)127 SRPRasterBand::SRPRasterBand( SRPDataset *poDSIn, int nBandIn )
128 
129 {
130     poDS = poDSIn;
131     nBand = nBandIn;
132 
133     eDataType = GDT_Byte;
134 
135     nBlockXSize = 128;
136     nBlockYSize = 128;
137 }
138 
139 /************************************************************************/
140 /*                            GetNoDataValue()                          */
141 /************************************************************************/
142 
GetNoDataValue(int * pbSuccess)143 double  SRPRasterBand::GetNoDataValue( int *pbSuccess )
144 {
145     if (pbSuccess)
146         *pbSuccess = TRUE;
147 
148     return 0;
149 }
150 
151 /************************************************************************/
152 /*                       GetColorInterpretation()                       */
153 /************************************************************************/
154 
GetColorInterpretation()155 GDALColorInterp SRPRasterBand::GetColorInterpretation()
156 
157 {
158     SRPDataset* l_poDS = (SRPDataset*)this->poDS;
159 
160     if( l_poDS->oCT.GetColorEntryCount() > 0 )
161         return GCI_PaletteIndex;
162     else
163         return GCI_GrayIndex;
164 }
165 
166 /************************************************************************/
167 /*                           GetColorTable()                            */
168 /************************************************************************/
169 
GetColorTable()170 GDALColorTable *SRPRasterBand::GetColorTable()
171 
172 {
173     SRPDataset* l_poDS = (SRPDataset*)this->poDS;
174 
175     if( l_poDS->oCT.GetColorEntryCount() > 0 )
176         return &(l_poDS->oCT);
177     else
178         return nullptr;
179 }
180 
181 /************************************************************************/
182 /*                             IReadBlock()                             */
183 /************************************************************************/
184 
IReadBlock(int nBlockXOff,int nBlockYOff,void * pImage)185 CPLErr SRPRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
186                                    void * pImage )
187 
188 {
189     SRPDataset* l_poDS = (SRPDataset*)this->poDS;
190     vsi_l_offset offset;
191     int nBlock = nBlockYOff * l_poDS->NFC + nBlockXOff;
192     if (nBlockXOff >= l_poDS->NFC || nBlockYOff >= l_poDS->NFL)
193     {
194         CPLError(CE_Failure, CPLE_AppDefined, "nBlockXOff=%d, NFC=%d, nBlockYOff=%d, NFL=%d",
195                  nBlockXOff, l_poDS->NFC, nBlockYOff, l_poDS->NFL);
196         return CE_Failure;
197     }
198 
199 /* -------------------------------------------------------------------- */
200 /*      Is this a null block?                                           */
201 /* -------------------------------------------------------------------- */
202     if (l_poDS->TILEINDEX && l_poDS->TILEINDEX[nBlock] <= 0)
203     {
204         memset(pImage, 0, 128 * 128);
205         return CE_None;
206     }
207 
208 /* -------------------------------------------------------------------- */
209 /*      Compute the offset to the block.                                */
210 /* -------------------------------------------------------------------- */
211     if (l_poDS->TILEINDEX)
212     {
213         if( l_poDS->PCB == 0 ) // uncompressed
214             offset = l_poDS->offsetInIMG + static_cast<vsi_l_offset>(l_poDS->TILEINDEX[nBlock] - 1) * 128 * 128;
215         else // compressed
216             offset = l_poDS->offsetInIMG +  static_cast<vsi_l_offset>(l_poDS->TILEINDEX[nBlock] - 1);
217     }
218     else
219         offset = l_poDS->offsetInIMG + static_cast<vsi_l_offset>(nBlock) * 128 * 128;
220 
221 /* -------------------------------------------------------------------- */
222 /*      Seek to target location.                                        */
223 /* -------------------------------------------------------------------- */
224     if (VSIFSeekL(l_poDS->fdIMG, offset, SEEK_SET) != 0)
225     {
226         CPLError(CE_Failure, CPLE_FileIO,
227                  "Cannot seek to offset " CPL_FRMT_GUIB, offset);
228         return CE_Failure;
229     }
230 
231 /* -------------------------------------------------------------------- */
232 /*      For uncompressed case we read the 128x128 and return with no    */
233 /*      further processing.                                             */
234 /* -------------------------------------------------------------------- */
235     if( l_poDS->PCB == 0 )
236     {
237         if( VSIFReadL(pImage, 1, 128 * 128, l_poDS->fdIMG) != 128*128 )
238         {
239             CPLError(CE_Failure, CPLE_FileIO,
240                      "Cannot read data at offset " CPL_FRMT_GUIB, offset);
241             return CE_Failure;
242         }
243     }
244 
245 /* -------------------------------------------------------------------- */
246 /*      If this is compressed data, we read a goodly chunk of data      */
247 /*      and then decode it.                                             */
248 /* -------------------------------------------------------------------- */
249     else
250     {
251         const int nBufSize = 128*128*2;
252         GByte *pabyCData = (GByte *) CPLCalloc(nBufSize,1);
253 
254         const int nBytesRead =
255             static_cast<int>(VSIFReadL(pabyCData, 1, nBufSize, l_poDS->fdIMG));
256         if( nBytesRead == 0 )
257         {
258             CPLError(CE_Failure, CPLE_FileIO,
259                      "Cannot read data at offset " CPL_FRMT_GUIB, offset);
260             CPLFree(pabyCData);
261             return CE_Failure;
262         }
263 
264         CPLAssert( l_poDS->PVB == 8 );
265         CPLAssert( l_poDS->PCB == 4 || l_poDS->PCB == 8 );
266 
267         bool bHalfByteUsed = false;
268         for( int iSrc = 0, iPixel = 0; iPixel < 128 * 128; )
269         {
270             if( iSrc + 2 > nBytesRead )
271             {
272                 CPLFree( pabyCData );
273                 CPLError(CE_Failure, CPLE_AppDefined,
274                          "Out of data decoding image block, only %d available.",
275                          iSrc );
276                 return CE_Failure;
277             }
278 
279             int nCount = 0;
280             int nValue = 0;
281 
282             if( l_poDS->PCB == 8 )
283             {
284                 nCount = pabyCData[iSrc++];
285                 nValue = pabyCData[iSrc++];
286             }
287             else if( l_poDS->PCB == 4 )
288             {
289                 if( (iPixel % 128) == 0 && bHalfByteUsed )
290                 {
291                     iSrc++;
292                     bHalfByteUsed = false;
293                     continue;
294                 }
295 
296                 if( bHalfByteUsed )
297                 {
298                     nCount = pabyCData[iSrc++] & 0xf;
299                     nValue = pabyCData[iSrc++];
300                     bHalfByteUsed = false;
301                 }
302                 else
303                 {
304                     nCount = pabyCData[iSrc] >> 4;
305                     nValue = ((pabyCData[iSrc] & 0xf) << 4)
306                         + (pabyCData[iSrc+1] >> 4);
307                     bHalfByteUsed = true;
308                     iSrc++;
309                 }
310             }
311 
312             if( iPixel + nCount > 128 * 128 )
313             {
314                 CPLFree( pabyCData );
315                 CPLError(CE_Failure, CPLE_AppDefined,
316                       "Too much data decoding image block, likely corrupt." );
317                 return CE_Failure;
318             }
319 
320             while( nCount > 0 )
321             {
322                 ((GByte *) pImage)[iPixel++] = (GByte) nValue;
323                 nCount--;
324             }
325         }
326 
327         CPLFree( pabyCData );
328     }
329 
330     return CE_None;
331 }
332 
333 /************************************************************************/
334 /*                          SRPDataset()                               */
335 /************************************************************************/
336 
SRPDataset()337 SRPDataset::SRPDataset() :
338     fdIMG(nullptr),
339     TILEINDEX(nullptr),
340     offsetInIMG(0),
341     NFC(0),
342     NFL(0),
343     ZNA(0),
344     LSO(0.0),
345     PSO(0.0),
346     LOD(0.0),
347     LAD(0.0),
348     ARV(0),
349     BRV(0),
350     PCB(0),
351     PVB(0),
352     papszSubDatasets(nullptr)
353 {}
354 
355 /************************************************************************/
356 /*                          ~SRPDataset()                              */
357 /************************************************************************/
358 
~SRPDataset()359 SRPDataset::~SRPDataset()
360 {
361     CSLDestroy(papszSubDatasets);
362 
363     if( fdIMG )
364     {
365         VSIFCloseL(fdIMG);
366     }
367 
368     if( TILEINDEX )
369     {
370         delete [] TILEINDEX;
371     }
372 }
373 
374 /************************************************************************/
375 /*                          ResetTo01()                                 */
376 /* Replace the DD in ZZZZZZDD.XXX with 01.                              */
377 /************************************************************************/
378 
ResetTo01(const char * str)379 CPLString SRPDataset::ResetTo01( const char* str )
380 {
381     CPLString osResult = str;
382 
383     osResult[6] = '0';
384     osResult[7] = '1';
385 
386     return osResult;
387 }
388 
389 /************************************************************************/
390 /*                        GetProjectionRef()                            */
391 /************************************************************************/
392 
_GetProjectionRef()393 const char* SRPDataset::_GetProjectionRef()
394 {
395     return osSRS;
396 }
397 
398 /************************************************************************/
399 /*                        GetGeoTransform()                             */
400 /************************************************************************/
401 
GetGeoTransform(double * padfGeoTransform)402 CPLErr SRPDataset::GetGeoTransform( double * padfGeoTransform)
403 {
404     if( EQUAL(osProduct,"ASRP") )
405     {
406         if( ARV == 0 )
407             return CE_Failure;
408         if( ZNA == 9)
409         {
410             // North Polar Case
411             padfGeoTransform[0] = 111319.4907933 * (90.0 - PSO/3600.0) * sin(LSO * M_PI / 648000.0);
412             padfGeoTransform[1] = 40075016.68558 / ARV;
413             padfGeoTransform[2] = 0.0;
414             padfGeoTransform[3] = -111319.4907933 * (90.0 - PSO/3600.0) * cos(LSO * M_PI / 648000.0);
415             padfGeoTransform[4] = 0.0;
416             padfGeoTransform[5] = -40075016.68558 / ARV;
417         }
418         else if (ZNA == 18)
419         {
420             // South Polar Case
421             padfGeoTransform[0] = 111319.4907933 * (90.0 + PSO/3600.0) * sin(LSO * M_PI / 648000.0);
422             padfGeoTransform[1] = 40075016.68558 / ARV;
423             padfGeoTransform[2] = 0.0;
424             padfGeoTransform[3] = 111319.4907933 * (90.0 + PSO/3600.0) * cos(LSO * M_PI / 648000.0);
425             padfGeoTransform[4] = 0.0;
426             padfGeoTransform[5] = -40075016.68558 / ARV;
427         }
428         else
429         {
430             if( BRV == 0 )
431                 return CE_Failure;
432             padfGeoTransform[0] = LSO/3600.0;
433             padfGeoTransform[1] = 360. / ARV;
434             padfGeoTransform[2] = 0.0;
435             padfGeoTransform[3] = PSO/3600.0;
436             padfGeoTransform[4] = 0.0;
437             padfGeoTransform[5] = - 360. / BRV;
438         }
439 
440         return CE_None;
441     }
442     else if( EQUAL(osProduct,"USRP") )
443     {
444         padfGeoTransform[0] = LSO;
445         padfGeoTransform[1] = LOD;
446         padfGeoTransform[2] = 0.0;
447         padfGeoTransform[3] = PSO;
448         padfGeoTransform[4] = 0.0;
449         padfGeoTransform[5] = -LAD;
450         return CE_None;
451     }
452 
453     return CE_Failure;
454 }
455 
456 /************************************************************************/
457 /*                           GetFromRecord()                            */
458 /************************************************************************/
459 
GetFromRecord(const char * pszFileName,DDFRecord * record)460 bool SRPDataset::GetFromRecord( const char* pszFileName, DDFRecord * record )
461 {
462     int bSuccess;
463 
464 /* -------------------------------------------------------------------- */
465 /*      Read a variety of header fields of interest from the .GEN       */
466 /*      file.                                                           */
467 /* -------------------------------------------------------------------- */
468     const int nSTR = record->GetIntSubfield( "GEN", 0, "STR", 0, &bSuccess );
469     if( !bSuccess || nSTR != 4 )
470     {
471         CPLDebug( "SRP", "Failed to extract STR, or not 4." );
472         return false;
473     }
474 
475     const int SCA = record->GetIntSubfield( "GEN", 0, "SCA", 0, &bSuccess );
476     CPLDebug("SRP", "SCA=%d", SCA);
477 
478     ZNA = record->GetIntSubfield( "GEN", 0, "ZNA", 0, &bSuccess );
479     CPLDebug("SRP", "ZNA=%d", ZNA);
480 
481     const double PSP = record->GetFloatSubfield( "GEN", 0, "PSP", 0, &bSuccess );
482     CPLDebug("SRP", "PSP=%f", PSP);
483 
484     ARV = record->GetIntSubfield( "GEN", 0, "ARV", 0, &bSuccess );
485     CPLDebug("SRP", "ARV=%d", ARV);
486 
487     BRV = record->GetIntSubfield( "GEN", 0, "BRV", 0, &bSuccess );
488     CPLDebug("SRP", "BRV=%d", BRV);
489 
490     LSO = record->GetFloatSubfield( "GEN", 0, "LSO", 0, &bSuccess );
491     CPLDebug("SRP", "LSO=%f", LSO);
492 
493     PSO = record->GetFloatSubfield( "GEN", 0, "PSO", 0, &bSuccess );
494     CPLDebug("SRP", "PSO=%f", PSO);
495 
496     LAD = record->GetFloatSubfield( "GEN", 0, "LAD", 0 );
497     LOD = record->GetFloatSubfield( "GEN", 0, "LOD", 0 );
498 
499     NFL = record->GetIntSubfield( "SPR", 0, "NFL", 0, &bSuccess );
500     CPLDebug("SRP", "NFL=%d", NFL);
501 
502     NFC = record->GetIntSubfield( "SPR", 0, "NFC", 0, &bSuccess );
503     CPLDebug("SRP", "NFC=%d", NFC);
504 
505     const auto knIntMax = std::numeric_limits<int>::max();
506     if( NFL <= 0 || NFC <= 0 ||
507         NFL > knIntMax / 128 ||
508         NFC > knIntMax / 128 ||
509         NFL > knIntMax / NFC )
510     {
511         CPLError( CE_Failure, CPLE_AppDefined,"Invalid NFL / NFC values");
512         return false;
513     }
514 
515     const int PNC = record->GetIntSubfield( "SPR", 0, "PNC", 0, &bSuccess );
516     CPLDebug("SRP", "PNC=%d", PNC);
517 
518     const int PNL = record->GetIntSubfield( "SPR", 0, "PNL", 0, &bSuccess );
519     CPLDebug("SRP", "PNL=%d", PNL);
520 
521     if( PNL != 128 || PNC != 128 )
522     {
523         CPLError( CE_Failure, CPLE_AppDefined,"Unsupported PNL or PNC value.");
524         return false;
525     }
526 
527     PCB = record->GetIntSubfield( "SPR", 0, "PCB", 0 );
528     PVB = record->GetIntSubfield( "SPR", 0, "PVB", 0 );
529     if( (PCB != 8 && PCB != 4 && PCB != 0) || PVB != 8 )
530     {
531         CPLError( CE_Failure, CPLE_AppDefined,
532                   "PCB(%d) or PVB(%d) value unsupported.", PCB, PVB );
533         return false;
534     }
535 
536     const char* pszBAD =
537         record->GetStringSubfield( "SPR", 0, "BAD", 0, &bSuccess );
538     if( pszBAD == nullptr )
539         return false;
540     const CPLString osBAD = pszBAD;
541     {
542         char* c = (char*) strchr(osBAD, ' ');
543         if (c)
544             *c = 0;
545     }
546     CPLDebug("SRP", "BAD=%s", osBAD.c_str());
547 
548 /* -------------------------------------------------------------------- */
549 /*      Read the tile map if available.                                 */
550 /* -------------------------------------------------------------------- */
551     const char* pszTIF = record->GetStringSubfield( "SPR", 0, "TIF", 0 );
552     const bool TIF = pszTIF != nullptr && EQUAL(pszTIF,"Y");
553     CPLDebug("SRP", "TIF=%s", TIF ? "true": "false");
554 
555     if( TIF )
556     {
557         DDFField* field = record->FindField( "TIM" );
558         if( field == nullptr )
559             return false;
560 
561         DDFFieldDefn *fieldDefn = field->GetFieldDefn();
562         DDFSubfieldDefn *subfieldDefn = fieldDefn->FindSubfieldDefn( "TSI" );
563         if( subfieldDefn == nullptr )
564             return false;
565 
566         const int nIndexValueWidth = subfieldDefn->GetWidth();
567 
568         char offset[30] = {0};
569         /* Should be strict comparison, but apparently a few datasets */
570         /* have GetDataSize() greater than the required minimum (#3862) */
571         if (nIndexValueWidth <= 0 ||
572             static_cast<size_t>(nIndexValueWidth) >= sizeof(offset) ||
573             nIndexValueWidth > (INT_MAX - 1) / (NFL * NFC) ||
574             field->GetDataSize() < nIndexValueWidth * NFL * NFC + 1)
575         {
576             return false;
577         }
578 
579         try
580         {
581             TILEINDEX = new int [NFL * NFC];
582         }
583         catch( const std::exception& )
584         {
585             return false;
586         }
587         const char* ptr = field->GetData();
588         offset[nIndexValueWidth] = '\0';
589 
590         for( int i = 0; i < NFL * NFC; i++ )
591         {
592             strncpy(offset, ptr, nIndexValueWidth);
593             ptr += nIndexValueWidth;
594             TILEINDEX[i] = atoi(offset);
595             // CPLDebug("SRP", "TSI[%d]=%d", i, TILEINDEX[i]);
596         }
597     }
598 
599 /* -------------------------------------------------------------------- */
600 /*      Open the .IMG file.  Try to recover gracefully if the case      */
601 /*      of the filename is wrong.                                       */
602 /* -------------------------------------------------------------------- */
603     const CPLString osDirname = CPLGetDirname(pszFileName);
604     const CPLString osImgName = CPLFormCIFilename(osDirname, osBAD, nullptr);
605 
606     fdIMG = VSIFOpenL(osImgName, "rb");
607     if (fdIMG == nullptr)
608     {
609         CPLError( CE_Failure, CPLE_AppDefined,
610                   "Cannot find %s", osImgName.c_str() );
611         return false;
612     }
613 
614 /* -------------------------------------------------------------------- */
615 /*      Establish the offset to the first byte of actual image data     */
616 /*      in the IMG file, skipping the ISO8211 header.                   */
617 /*                                                                      */
618 /*      This code is awfully fragile!                                   */
619 /* -------------------------------------------------------------------- */
620     char c;
621     if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
622     {
623         return false;
624     }
625     while (!VSIFEofL(fdIMG))
626     {
627         if (c == 30)
628         {
629             char recordName[3] = {};
630             if (VSIFReadL(recordName, 1, 3, fdIMG) != 3)
631             {
632                 return false;
633             }
634             offsetInIMG += 3;
635             if (STARTS_WITH(recordName, "IMG"))
636             {
637                 offsetInIMG += 4;
638                 if (VSIFSeekL(fdIMG,3,SEEK_CUR) != 0)
639                 {
640                     return false;
641                 }
642                 if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
643                 {
644                     return false;
645                 }
646                 while( c != 30 )
647                 {
648                     offsetInIMG ++;
649                     if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
650                     {
651                         return false;
652                     }
653                 }
654                 offsetInIMG ++;
655                 break;
656             }
657         }
658 
659         offsetInIMG ++;
660         if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
661         {
662             return false;
663         }
664     }
665 
666     if (VSIFEofL(fdIMG))
667     {
668         return false;
669     }
670 
671     CPLDebug("SRP", "Img offset data = %d", offsetInIMG);
672 
673 /* -------------------------------------------------------------------- */
674 /*      Establish the SRP Dataset.                                     */
675 /* -------------------------------------------------------------------- */
676     nRasterXSize = NFC * 128;
677     nRasterYSize = NFL * 128;
678 
679     char szValue[32] = {};
680     snprintf(szValue, sizeof(szValue), "%d", SCA);
681     SetMetadataItem( "SRP_SCA", szValue );
682 
683     nBands = 1;
684     for( int i = 0; i < nBands; i++ )
685         SetBand( i+1, new SRPRasterBand( this, i+1 ) );
686 
687 /* -------------------------------------------------------------------- */
688 /*      Try to collect a color map from the .QAL file.                  */
689 /* -------------------------------------------------------------------- */
690     const CPLString osBasename = CPLGetBasename(pszFileName);
691     osQALFileName = CPLFormCIFilename(osDirname, osBasename, "QAL");
692 
693     DDFModule oQALModule;
694 
695     if( oQALModule.Open( osQALFileName, TRUE ) )
696     {
697         while( (record = oQALModule.ReadRecord()) != nullptr)
698         {
699             if( record->FindField( "COL" ) != nullptr )
700             {
701                 const int nColorCount =
702                     std::min(256, record->FindField("COL")->GetRepeatCount());
703 
704                 for( int iColor = 0; iColor < nColorCount; iColor++ )
705                 {
706                     const int nCCD =
707                         record->GetIntSubfield( "COL", 0, "CCD", iColor,
708                                                 &bSuccess );
709                     if( !bSuccess || nCCD < 0 || nCCD > 255 )
710                         break;
711 
712                     int nNSR = record->GetIntSubfield("COL", 0, "NSR", iColor);
713                     int nNSG = record->GetIntSubfield("COL", 0, "NSG", iColor);
714                     int nNSB = record->GetIntSubfield("COL", 0, "NSB", iColor);
715 
716                     GDALColorEntry sEntry = {
717                         static_cast<short>(nNSR),
718                         static_cast<short>(nNSG),
719                         static_cast<short>(nNSB),
720                         255
721                     };
722 
723                     oCT.SetColorEntry( nCCD, &sEntry );
724                 }
725             }
726 
727             if (record->FindField( "QUV" ) != nullptr )
728             {
729                 // TODO: Translate to English or state why this should not be in
730                 // English.
731                 // Date de production du produit : QAL.QUV.DAT1
732                 // Numero d'edition  du produit : QAL.QUV.EDN
733 
734                 const int EDN =
735                     record->GetIntSubfield( "QUV", 0, "EDN", 0, &bSuccess );
736                 if (bSuccess)
737                 {
738                     CPLDebug("SRP", "EDN=%d", EDN);
739                     snprintf(szValue, sizeof(szValue), "%d", EDN);
740                     SetMetadataItem( "SRP_EDN", szValue );
741                 }
742 
743                 const char* pszCDV07 =
744                     record->GetStringSubfield( "QUV", 0, "CDV07", 0 );
745                 if (pszCDV07!=nullptr)
746                     SetMetadataItem( "SRP_CREATIONDATE", pszCDV07 );
747                 else
748                 { /*USRP1.2*/
749                     const char* pszDAT =
750                         record->GetStringSubfield("QUV", 0, "DAT1", 0);
751                     if( pszDAT != nullptr && strlen(pszDAT) >= 12 )
752                     {
753                         char dat[9];
754                         strncpy(dat, pszDAT+4, 8);
755                         dat[8]='\0';
756                         CPLDebug("SRP", "Record DAT %s",dat);
757                         SetMetadataItem( "SRP_CREATIONDATE", dat );
758                     }
759                 }
760 
761                 const char* pszCDV24 =
762                     record->GetStringSubfield( "QUV", 0, "CDV24", 0 );
763                 if (pszCDV24!=nullptr)
764                 {
765                     SetMetadataItem( "SRP_REVISIONDATE", pszCDV24 );
766                 }
767                 else
768                 { /*USRP1.2*/
769                     const char* pszDAT =
770                         record->GetStringSubfield("QUV", 0, "DAT2", 0);
771                     if( pszDAT != nullptr && strlen(pszDAT) >= 12 )
772                     {
773                         char dat[9];
774                         strncpy(dat,pszDAT+4,8);
775                         dat[8]='\0';
776                         CPLDebug("SRP", "Record DAT %s",dat);
777                         SetMetadataItem( "SRP_REVISIONDATE", dat );
778                     }
779                 }
780 
781                 const char* pszQSS =
782                     record->GetStringSubfield( "QSR", 0, "QSS", 0 );
783                 if (pszQSS!=nullptr)
784                     SetMetadataItem( "SRP_CLASSIFICATION", pszQSS );
785             }
786         }
787     }
788     else
789     {
790         osQALFileName = "";
791         CPLError( CE_Warning, CPLE_AppDefined,
792                   "Unable to find .QAL file, no color table applied." );
793     }
794 
795 /* -------------------------------------------------------------------- */
796 /*      Derive the coordinate system.                                   */
797 /* -------------------------------------------------------------------- */
798     if( EQUAL(osProduct,"ASRP") )
799     {
800         osSRS = SRS_WKT_WGS84_LAT_LONG;
801 
802         if( ZNA == 9 )
803         {
804             osSRS =
805                 "PROJCS[\"ARC_System_Zone_09\",GEOGCS[\"GCS_Sphere\","
806                 "DATUM[\"D_Sphere\",SPHEROID[\"Sphere\",6378137.0,0.0]],"
807                 "PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],"
808                 "PROJECTION[\"Azimuthal_Equidistant\"],"
809                 "PARAMETER[\"latitude_of_center\",90],"
810                 "PARAMETER[\"longitude_of_center\",0],"
811                 "PARAMETER[\"false_easting\",0],"
812                 "PARAMETER[\"false_northing\",0],"
813                 "UNIT[\"metre\",1]]";
814         }
815 
816         if (ZNA == 18)
817         {
818             osSRS =
819                 "PROJCS[\"ARC_System_Zone_18\",GEOGCS[\"GCS_Sphere\","
820                 "DATUM[\"D_Sphere\",SPHEROID[\"Sphere\",6378137.0,0.0]],"
821                 "PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],"
822                 "PROJECTION[\"Azimuthal_Equidistant\"],"
823                 "PARAMETER[\"latitude_of_center\",-90],"
824                 "PARAMETER[\"longitude_of_center\",0],"
825                 "PARAMETER[\"false_easting\",0],"
826                 "PARAMETER[\"false_northing\",0],"
827                 "UNIT[\"metre\",1]]";
828         }
829     }
830     else
831     {
832         OGRSpatialReference oSRS;
833 
834         if( std::abs(ZNA) >= 1 && std::abs(ZNA) <= 60 )
835         {
836             oSRS.SetUTM(std::abs(ZNA), ZNA > 0);
837             oSRS.SetWellKnownGeogCS( "WGS84" );
838         }
839         else if( ZNA == 61 )
840         {
841             oSRS.importFromEPSG( 32661 ); // WGS84 UPS North
842         }
843         else if( ZNA == -61 )
844         {
845             oSRS.importFromEPSG( 32761 ); // WGS84 UPS South
846         }
847 
848         char *pszWKT = nullptr;
849         oSRS.exportToWkt( &pszWKT );
850         osSRS = pszWKT;
851         CPLFree( pszWKT );
852     }
853 
854     snprintf(szValue, sizeof(szValue), "%d", ZNA);
855     SetMetadataItem( "SRP_ZNA", szValue );
856 
857     return true;
858 }
859 
860 /************************************************************************/
861 /*                            GetFileList()                             */
862 /************************************************************************/
863 
GetFileList()864 char **SRPDataset::GetFileList()
865 
866 {
867     char **papszFileList = GDALPamDataset::GetFileList();
868     if (!osGENFileName.empty() && !osIMGFileName.empty())
869     {
870         CPLString osMainFilename = GetDescription();
871         VSIStatBufL  sStat;
872 
873         const bool bMainFileReal = VSIStatL( osMainFilename, &sStat ) == 0;
874         if (bMainFileReal)
875         {
876             CPLString osShortMainFilename = CPLGetFilename(osMainFilename);
877             CPLString osShortGENFileName = CPLGetFilename(osGENFileName);
878             if( !EQUAL(osShortMainFilename.c_str(),
879                        osShortGENFileName.c_str()) )
880                 papszFileList =
881                     CSLAddString(papszFileList, osGENFileName.c_str());
882         }
883         else
884         {
885             papszFileList = CSLAddString(papszFileList, osGENFileName.c_str());
886         }
887 
888         papszFileList = CSLAddString(papszFileList, osIMGFileName.c_str());
889 
890         if( !osQALFileName.empty() )
891             papszFileList = CSLAddString( papszFileList, osQALFileName );
892     }
893     return papszFileList;
894 }
895 
896 /************************************************************************/
897 /*                           AddSubDataset()                            */
898 /************************************************************************/
899 
AddSubDataset(const char * pszGENFileName,const char * pszIMGFileName)900 void SRPDataset::AddSubDataset( const char* pszGENFileName,
901                                 const char* pszIMGFileName )
902 {
903     const int nCount = CSLCount(papszSubDatasets ) / 2;
904 
905     CPLString osSubDatasetName = "SRP:";
906     osSubDatasetName += pszGENFileName;
907     osSubDatasetName += ",";
908     osSubDatasetName += pszIMGFileName;
909 
910     char szName[80];
911     snprintf(szName, sizeof(szName), "SUBDATASET_%d_NAME", nCount+1 );
912     papszSubDatasets =
913         CSLSetNameValue( papszSubDatasets, szName, osSubDatasetName);
914 
915     snprintf(szName, sizeof(szName), "SUBDATASET_%d_DESC", nCount+1 );
916     papszSubDatasets =
917         CSLSetNameValue( papszSubDatasets, szName, osSubDatasetName);
918 }
919 
920 /************************************************************************/
921 /*                            GetMetadata()                             */
922 /************************************************************************/
923 
GetMetadata(const char * pszDomain)924 char **SRPDataset::GetMetadata( const char *pszDomain )
925 
926 {
927     if( pszDomain != nullptr && EQUAL(pszDomain,"SUBDATASETS") )
928         return papszSubDatasets;
929 
930     return GDALPamDataset::GetMetadata( pszDomain );
931 }
932 
933 /************************************************************************/
934 /*                      FindRecordInGENForIMG()                         */
935 /************************************************************************/
936 
FindRecordInGENForIMG(DDFModule & module,const char * pszGENFileName,const char * pszIMGFileName)937 DDFRecord* SRPDataset::FindRecordInGENForIMG( DDFModule& module,
938                                               const char* pszGENFileName,
939                                               const char* pszIMGFileName )
940 {
941     /* Finds the GEN file corresponding to the IMG file */
942     if (!module.Open(pszGENFileName, TRUE))
943         return nullptr;
944 
945     CPLString osShortIMGFilename = CPLGetFilename(pszIMGFileName);
946 
947     DDFField* field = nullptr;
948     DDFFieldDefn *fieldDefn = nullptr;
949 
950     // Now finds the record.
951     while( true )
952     {
953         CPLPushErrorHandler( CPLQuietErrorHandler );
954         DDFRecord* record = module.ReadRecord();
955         CPLPopErrorHandler();
956         CPLErrorReset();
957         if (record == nullptr)
958             return nullptr;
959 
960         if (record->GetFieldCount() >= 5)
961         {
962             field = record->GetField(0);
963             fieldDefn = field->GetFieldDefn();
964             if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
965                 fieldDefn->GetSubfieldCount() == 2))
966             {
967                 continue;
968             }
969 
970             const char* RTY = record->GetStringSubfield("001", 0, "RTY", 0);
971             if( RTY == nullptr )
972                 continue;
973             /* Ignore overviews */
974             if ( strcmp(RTY, "OVV") == 0 )
975                 continue;
976 
977             if ( strcmp(RTY, "GIN") != 0 )
978                 continue;
979 
980             field = record->GetField(3);
981             fieldDefn = field->GetFieldDefn();
982 
983             if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
984                 fieldDefn->GetSubfieldCount() == 15))
985             {
986                 continue;
987             }
988 
989             const char* pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
990             if( pszBAD == nullptr || strlen(pszBAD) != 12 )
991                 continue;
992             const CPLString osBAD = pszBAD;
993             {
994                 char* c = (char*) strchr(osBAD.c_str(), ' ');
995                 if (c)
996                     *c = 0;
997             }
998 
999             if (EQUAL(osShortIMGFilename.c_str(), osBAD.c_str()))
1000             {
1001                 return record;
1002             }
1003         }
1004     }
1005 }
1006 /************************************************************************/
1007 /*                           OpenDataset()                              */
1008 /************************************************************************/
1009 
OpenDataset(const char * pszGENFileName,const char * pszIMGFileName,DDFRecord * record)1010 SRPDataset* SRPDataset::OpenDataset(
1011     const char* pszGENFileName, const char* pszIMGFileName, DDFRecord* record)
1012 {
1013     DDFModule module; // Don't move this line as it holds ownership of record.
1014 
1015     if (record == nullptr)
1016     {
1017         record = FindRecordInGENForIMG(module, pszGENFileName, pszIMGFileName);
1018         if (record == nullptr)
1019             return nullptr;
1020     }
1021 
1022     DDFField* field = record->GetField(1);
1023     if( field == nullptr )
1024         return nullptr;
1025     DDFFieldDefn *fieldDefn = field->GetFieldDefn();
1026 
1027     if (!(strcmp(fieldDefn->GetName(), "DSI") == 0 &&
1028         fieldDefn->GetSubfieldCount() == 2))
1029     {
1030         return nullptr;
1031     }
1032 
1033     const char* pszPRT = record->GetStringSubfield("DSI", 0, "PRT", 0);
1034     if( pszPRT == nullptr)
1035         return nullptr;
1036 
1037     CPLString osPRT = pszPRT;
1038     osPRT.resize(4);
1039     CPLDebug("SRP", "osPRT=%s", osPRT.c_str());
1040     if( !EQUAL(osPRT,"ASRP") && !EQUAL(osPRT,"USRP") )
1041         return nullptr;
1042 
1043     const char* pszNAM = record->GetStringSubfield("DSI", 0, "NAM", 0);
1044     if( pszNAM == nullptr  )
1045         return nullptr;
1046 
1047     const CPLString osNAM = pszNAM;
1048     CPLDebug("SRP", "osNAM=%s", osNAM.c_str());
1049     if ( strlen(pszNAM) != 8 )
1050     {
1051         CPLDebug("SRP", "Name Size=%d", (int)strlen(pszNAM) );
1052     }
1053 
1054     SRPDataset* poDS = new SRPDataset();
1055 
1056     poDS->osProduct = osPRT;
1057     poDS->osGENFileName = pszGENFileName;
1058     poDS->osIMGFileName = pszIMGFileName;
1059 
1060     poDS->SetMetadataItem( "SRP_NAM", osNAM );
1061     poDS->SetMetadataItem( "SRP_PRODUCT", osPRT );
1062 
1063     if (!poDS->GetFromRecord( pszGENFileName, record ) )
1064     {
1065         delete poDS;
1066         return nullptr;
1067     }
1068 
1069     return poDS;
1070 }
1071 
1072 /************************************************************************/
1073 /*                          GetGENListFromTHF()                         */
1074 /************************************************************************/
1075 
GetGENListFromTHF(const char * pszFileName)1076 char** SRPDataset::GetGENListFromTHF(const char* pszFileName)
1077 {
1078     DDFModule module;
1079     DDFRecord * record = nullptr;
1080     DDFField* field = nullptr;
1081     DDFFieldDefn *fieldDefn = nullptr;
1082     int nFilenames = 0;
1083 
1084     char** papszFileNames = nullptr;
1085     if (!module.Open(pszFileName, TRUE))
1086         return papszFileNames;
1087 
1088     CPLString osDirName(CPLGetDirname(pszFileName));
1089 
1090     while( true )
1091     {
1092         CPLPushErrorHandler( CPLQuietErrorHandler );
1093         record = module.ReadRecord();
1094         CPLPopErrorHandler();
1095         CPLErrorReset();
1096         if (record == nullptr)
1097             break;
1098         if (record->GetFieldCount() > 2)
1099         {
1100             field = record->GetField(0);
1101             fieldDefn = field->GetFieldDefn();
1102             if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
1103                 fieldDefn->GetSubfieldCount() == 2))
1104             {
1105                 continue;
1106             }
1107 
1108             const char* RTY = record->GetStringSubfield("001", 0, "RTY", 0);
1109             if ( RTY == nullptr )
1110             {
1111                 continue;
1112             }
1113 
1114             if ( strcmp(RTY, "THF") == 0 )
1115             {
1116                 field = record->GetField(1);
1117                 fieldDefn = field->GetFieldDefn();
1118                 if (!(strcmp(fieldDefn->GetName(), "VDR") == 0 &&
1119                     fieldDefn->GetSubfieldCount() == 8))
1120                 {
1121                     continue;
1122                 }
1123 
1124                 int iFDRFieldInstance = 0;
1125                 for( int i = 2; i < record->GetFieldCount() ; i++ )
1126                 {
1127                     field = record->GetField(i);
1128                     fieldDefn = field->GetFieldDefn();
1129 
1130                     if (!(strcmp(fieldDefn->GetName(), "FDR") == 0 &&
1131                         fieldDefn->GetSubfieldCount() == 7))
1132                     {
1133                         CPLDebug("SRP", "Record FDR  %d",fieldDefn->GetSubfieldCount());
1134                         continue;
1135                     }
1136 
1137                     const char* pszNAM = record->GetStringSubfield("FDR", iFDRFieldInstance++, "NAM", 0);
1138                     if( pszNAM == nullptr)
1139                         continue;
1140 
1141                     CPLString osName = CPLString(pszNAM);
1142 
1143                     /* Define a subdirectory from Dataset but with only 6 characters */
1144                     CPLString osDirDataset = pszNAM;
1145                     osDirDataset.resize(6);
1146                     CPLString osDatasetDir = CPLFormFilename(osDirName.c_str(), osDirDataset.c_str(), nullptr);
1147 
1148                     CPLString osGENFileName="";
1149 
1150                     int bFound=0;
1151 
1152                     {
1153                         char** papszDirContent = VSIReadDir(osDatasetDir.c_str());
1154                         char** ptrDir = papszDirContent;
1155                         if (ptrDir)
1156                         {
1157                             while(*ptrDir)
1158                             {
1159                                 if ( EQUAL(CPLGetExtension(*ptrDir), "GEN") )
1160                                 {
1161                                     bFound = 1;
1162                                     osGENFileName = CPLFormFilename(osDatasetDir.c_str(), *ptrDir, nullptr);
1163                                     CPLDebug("SRP", "Building GEN full file name : %s", osGENFileName.c_str());
1164                                     break;
1165                                 }
1166                                 ptrDir ++;
1167                             }
1168                             CSLDestroy(papszDirContent);
1169                         }
1170                     }
1171 
1172                     /* If not found in sub directory then search in the same directory of the THF file */
1173                     if (bFound ==0)
1174                     {
1175                         char** papszDirContent = VSIReadDir(osDirName.c_str());
1176                         char** ptrDir = papszDirContent;
1177                         if (ptrDir)
1178                         {
1179                             while(*ptrDir)
1180                             {
1181                                 if ( EQUAL(CPLGetExtension(*ptrDir), "GEN") &&  EQUALN(CPLGetBasename(*ptrDir), osName,6))
1182                                 {
1183                                     bFound = 1;
1184                                     osGENFileName = CPLFormFilename(osDirName.c_str(), *ptrDir, nullptr);
1185                                     CPLDebug("SRP", "Building GEN full file name : %s", osGENFileName.c_str());
1186                                     break;
1187                                 }
1188                                 ptrDir ++;
1189                             }
1190                             CSLDestroy(papszDirContent);
1191                         }
1192                     }
1193 
1194                     if (bFound ==1)
1195                     {
1196                         papszFileNames = (char**)CPLRealloc(papszFileNames, sizeof(char*) * (nFilenames + 2));
1197                         papszFileNames[nFilenames] = CPLStrdup(osGENFileName.c_str());
1198                         papszFileNames[nFilenames + 1] = nullptr;
1199                         nFilenames ++;
1200                     }
1201                 }
1202             }
1203         }
1204     }
1205     return papszFileNames;
1206 }
1207 
1208 /************************************************************************/
1209 /*                          AddMetadatafromFromTHF()                         */
1210 /************************************************************************/
1211 
AddMetadatafromFromTHF(const char * pszFileName)1212 void SRPDataset::AddMetadatafromFromTHF(const char* pszFileName)
1213 {
1214     DDFModule module;
1215     DDFRecord * record = nullptr;
1216     DDFField* field = nullptr;
1217     DDFFieldDefn *fieldDefn = nullptr;
1218 
1219     int bSuccess=0;
1220     if (!module.Open(pszFileName, TRUE))
1221         return ;
1222 
1223     while( true )
1224     {
1225         CPLPushErrorHandler( CPLQuietErrorHandler );
1226         record = module.ReadRecord();
1227         CPLPopErrorHandler();
1228         CPLErrorReset();
1229         if (record == nullptr || record->GetFieldCount() <= 2)
1230             break;
1231 
1232         field = record->GetField(0);
1233         fieldDefn = field->GetFieldDefn();
1234         if (!(strcmp(fieldDefn->GetName(), "001") == 0) ||
1235             fieldDefn->GetSubfieldCount() != 2)
1236             break;
1237 
1238         const char* RTY = record->GetStringSubfield("001", 0, "RTY", 0);
1239         if ( RTY != nullptr &&  strcmp(RTY, "THF") == 0 )
1240         {
1241             field = record->GetField(1);
1242             fieldDefn = field->GetFieldDefn();
1243             if ((strcmp(fieldDefn->GetName(), "VDR") == 0 &&
1244                 fieldDefn->GetSubfieldCount() == 8))
1245             {
1246 
1247                 const char* pszVOO = record->GetStringSubfield("VDR", 0, "VOO", 0);
1248                 if( pszVOO != nullptr )
1249                 {
1250                     CPLDebug("SRP", "Record VOO %s",pszVOO);
1251                     SetMetadataItem( "SRP_VOO", pszVOO );
1252                 }
1253 
1254                 int EDN = record->GetIntSubfield( "VDR", 0, "EDN", 0, &bSuccess );
1255                 if (bSuccess)
1256                 {
1257                     CPLDebug("SRP", "Record EDN %d",EDN);
1258                     char szValue[5];
1259                     snprintf(szValue, sizeof(szValue), "%d", EDN);
1260                     SetMetadataItem( "SRP_EDN", szValue );
1261                 }
1262 
1263                 const char* pszCDV07 = record->GetStringSubfield("VDR", 0, "CDV07", 0);
1264                 if( pszCDV07 != nullptr )
1265                 {
1266                     CPLDebug("SRP", "Record pszCDV07 %s",pszCDV07);
1267                     SetMetadataItem( "SRP_CREATIONDATE", pszCDV07 );
1268                 }
1269                 else
1270                 {  /*USRP1.2*/
1271                     const char* pszDAT = record->GetStringSubfield("VDR", 0, "DAT", 0);
1272                     if( pszDAT != nullptr )
1273                     {
1274                         char dat[9];
1275                         strncpy(dat,pszDAT+4,8);
1276                         dat[8]='\0';
1277                         CPLDebug("SRP", "Record DAT %s",dat);
1278                         SetMetadataItem( "SRP_CREATIONDATE", dat );
1279                     }
1280                 }
1281             }
1282         } /* End of THF part */
1283 
1284         if ( RTY != nullptr && strcmp(RTY, "LCF") == 0 )
1285         {
1286             field = record->GetField(1);
1287             fieldDefn = field->GetFieldDefn();
1288             if ((strcmp(fieldDefn->GetName(), "QSR") == 0 &&
1289                 fieldDefn->GetSubfieldCount() == 4))
1290             {
1291 
1292                 const char* pszQSS = record->GetStringSubfield("QSR", 0, "QSS", 0);
1293                 if( pszQSS != nullptr )
1294                 {
1295                     CPLDebug("SRP", "Record Classification %s",pszQSS);
1296                     SetMetadataItem( "SRP_CLASSIFICATION", pszQSS );
1297                 }
1298             }
1299 
1300             field = record->GetField(2);
1301             fieldDefn = field->GetFieldDefn();
1302             if ((strcmp(fieldDefn->GetName(), "QUV") == 0 &&
1303                 fieldDefn->GetSubfieldCount() == 6))
1304             {
1305                 const char* pszSRC2 = record->GetStringSubfield("QUV", 0, "SRC1", 0);
1306                 if( pszSRC2 != nullptr )
1307                 {
1308                     SetMetadataItem( "SRP_PRODUCTVERSION", pszSRC2 );
1309                 }
1310                 else
1311                 {
1312                     const char* pszSRC = record->GetStringSubfield("QUV", 0, "SRC", 0);
1313                     if( pszSRC != nullptr )
1314                     {
1315                         SetMetadataItem( "SRP_PRODUCTVERSION", pszSRC );
1316                     }
1317                 }
1318             }
1319         }  /* End of LCF part */
1320     }
1321 }
1322 
1323 /************************************************************************/
1324 /*                          GetIMGListFromGEN()                         */
1325 /************************************************************************/
1326 
GetIMGListFromGEN(const char * pszFileName,int * pnRecordIndex)1327 char** SRPDataset::GetIMGListFromGEN(const char* pszFileName,
1328                                     int *pnRecordIndex)
1329 {
1330     DDFRecord * record = nullptr;
1331     DDFField* field = nullptr;
1332     DDFFieldDefn *fieldDefn = nullptr;
1333     int nFilenames = 0;
1334     char** papszFileNames = nullptr;
1335     int nRecordIndex = -1;
1336 
1337     if (pnRecordIndex)
1338         *pnRecordIndex = -1;
1339 
1340     DDFModule module;
1341     if (!module.Open(pszFileName, TRUE))
1342         return nullptr;
1343 
1344     while( true )
1345     {
1346         nRecordIndex ++;
1347 
1348         CPLPushErrorHandler( CPLQuietErrorHandler );
1349         record = module.ReadRecord();
1350         CPLPopErrorHandler();
1351         CPLErrorReset();
1352         if (record == nullptr)
1353             break;
1354 
1355         if (record->GetFieldCount() >= 5)
1356         {
1357             field = record->GetField(0);
1358             fieldDefn = field->GetFieldDefn();
1359             if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
1360                 fieldDefn->GetSubfieldCount() == 2))
1361             {
1362                 continue;
1363             }
1364 
1365             const char* RTY = record->GetStringSubfield("001", 0, "RTY", 0);
1366             if( RTY == nullptr )
1367                 continue;
1368             /* Ignore overviews */
1369             if ( strcmp(RTY, "OVV") == 0 )
1370                 continue;
1371 
1372             if ( strcmp(RTY, "GIN") != 0 )
1373                 continue;
1374 
1375             /* make sure that the GEN file is part of a SRP dataset, not an ADRG dataset, by checking that the GEN field does not contain a NOW subfield */
1376             const char* NWO = record->GetStringSubfield("GEN", 0, "NWO", 0);
1377             if( NWO )
1378             {
1379                 CSLDestroy(papszFileNames);
1380                 return nullptr;
1381             }
1382 
1383             field = record->GetField(3);
1384             if( field == nullptr )
1385                 continue;
1386             fieldDefn = field->GetFieldDefn();
1387 
1388             if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
1389                 fieldDefn->GetSubfieldCount() == 15))
1390             {
1391                 continue;
1392             }
1393 
1394             const char* pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
1395             if( pszBAD == nullptr || strlen(pszBAD) != 12 )
1396                 continue;
1397             CPLString osBAD = pszBAD;
1398             {
1399                 char* c = (char*) strchr(osBAD.c_str(), ' ');
1400                 if (c)
1401                     *c = 0;
1402             }
1403             CPLDebug("SRP", "BAD=%s", osBAD.c_str());
1404 
1405             /* Build full IMG file name from BAD value */
1406             CPLString osGENDir(CPLGetDirname(pszFileName));
1407 
1408             CPLString osFileName = CPLFormFilename(osGENDir.c_str(), osBAD.c_str(), nullptr);
1409             VSIStatBufL sStatBuf;
1410             if( VSIStatL( osFileName, &sStatBuf ) == 0 )
1411             {
1412                 osBAD = osFileName;
1413                 CPLDebug("SRP", "Building IMG full file name : %s", osBAD.c_str());
1414             }
1415             else
1416             {
1417                 char** papszDirContent = nullptr;
1418                 if (strcmp(osGENDir.c_str(), "/vsimem") == 0)
1419                 {
1420                     CPLString osTmp = osGENDir + "/";
1421                     papszDirContent = VSIReadDir(osTmp);
1422                 }
1423                 else
1424                     papszDirContent = VSIReadDir(osGENDir);
1425                 char** ptrDir = papszDirContent;
1426                 while(ptrDir && *ptrDir)
1427                 {
1428                     if (EQUAL(*ptrDir, osBAD.c_str()))
1429                     {
1430                         osBAD = CPLFormFilename(osGENDir.c_str(), *ptrDir, nullptr);
1431                         CPLDebug("SRP", "Building IMG full file name : %s", osBAD.c_str());
1432                         break;
1433                     }
1434                     ptrDir ++;
1435                 }
1436                 CSLDestroy(papszDirContent);
1437             }
1438 
1439             if (nFilenames == 0 && pnRecordIndex)
1440                 *pnRecordIndex = nRecordIndex;
1441 
1442             papszFileNames = (char**)CPLRealloc(papszFileNames, sizeof(char*) * (nFilenames + 2));
1443             papszFileNames[nFilenames] = CPLStrdup(osBAD.c_str());
1444             papszFileNames[nFilenames + 1] = nullptr;
1445             nFilenames ++;
1446         }
1447     }
1448 
1449     return papszFileNames;
1450 }
1451 
1452 /************************************************************************/
1453 /*                                Open()                                */
1454 /************************************************************************/
1455 
Open(GDALOpenInfo * poOpenInfo)1456 GDALDataset *SRPDataset::Open( GDALOpenInfo * poOpenInfo )
1457 {
1458     int nRecordIndex = -1;
1459     CPLString osGENFileName;
1460     CPLString osIMGFileName;
1461     int bFromSubdataset = FALSE;
1462     int bTHFWithSingleGEN = FALSE;
1463 
1464     if( STARTS_WITH_CI(poOpenInfo->pszFilename, "SRP:") )
1465     {
1466         char** papszTokens = CSLTokenizeString2(poOpenInfo->pszFilename + 4, ",", 0);
1467         if (CSLCount(papszTokens) == 2)
1468         {
1469             osGENFileName = papszTokens[0];
1470             osIMGFileName = papszTokens[1];
1471             bFromSubdataset = TRUE;
1472         }
1473         CSLDestroy(papszTokens);
1474     }
1475     else
1476     {
1477         if( poOpenInfo->nHeaderBytes < 500 )
1478             return nullptr;
1479         CPLString osFileName(poOpenInfo->pszFilename);
1480 
1481         if (EQUAL(CPLGetExtension(osFileName.c_str()), "THF"))
1482         {
1483 
1484             CPLDebug("SRP", "Read THF");
1485 
1486             char** papszFileNames = GetGENListFromTHF(osFileName.c_str());
1487             if (papszFileNames == nullptr)
1488                 return nullptr;
1489             if (papszFileNames[1] == nullptr &&
1490                 CPLTestBool(CPLGetConfigOption("SRP_SINGLE_GEN_IN_THF_AS_DATASET", "TRUE")))
1491             {
1492                 osFileName = papszFileNames[0];
1493                 CSLDestroy(papszFileNames);
1494                 bTHFWithSingleGEN = TRUE;
1495             }
1496             else
1497             {
1498                 char** ptr = papszFileNames;
1499                 SRPDataset* poDS = new SRPDataset();
1500                 poDS->AddMetadatafromFromTHF(osFileName.c_str());
1501                 while(*ptr)
1502                 {
1503                     char** papszIMGFileNames = GetIMGListFromGEN(*ptr);
1504                     char** papszIMGIter = papszIMGFileNames;
1505                     while(papszIMGIter && *papszIMGIter)
1506                     {
1507                         poDS->AddSubDataset(*ptr, *papszIMGIter);
1508                         papszIMGIter ++;
1509                     }
1510                     CSLDestroy(papszIMGFileNames);
1511 
1512                     ptr ++;
1513                 }
1514                 CSLDestroy(papszFileNames);
1515                 return poDS;
1516             }
1517         }
1518 
1519         if ( bTHFWithSingleGEN
1520 #ifdef OPEN_GEN
1521                 || EQUAL(CPLGetExtension(osFileName.c_str()), "GEN")
1522 #endif
1523             )
1524         {
1525             osGENFileName = osFileName;
1526 
1527             char** papszFileNames = GetIMGListFromGEN(osFileName.c_str(), &nRecordIndex);
1528             if (papszFileNames == nullptr)
1529                 return nullptr;
1530             if (papszFileNames[1] == nullptr)
1531             {
1532                 osIMGFileName = papszFileNames[0];
1533                 CSLDestroy(papszFileNames);
1534             }
1535             else
1536             {
1537                 char** ptr = papszFileNames;
1538                 SRPDataset* poDS = new SRPDataset();
1539                 while(*ptr)
1540                 {
1541                     poDS->AddSubDataset(osFileName.c_str(), *ptr);
1542                     ptr ++;
1543                 }
1544                 CSLDestroy(papszFileNames);
1545                 return poDS;
1546             }
1547         }
1548 
1549         if (EQUAL(CPLGetExtension(osFileName.c_str()), "IMG"))
1550         {
1551 
1552             osIMGFileName = osFileName;
1553 
1554             constexpr int nLeaderSize = 24;
1555 
1556             int i = 0;  // Used after for.
1557             for( ; i < nLeaderSize; i++ )
1558             {
1559                 if( poOpenInfo->pabyHeader[i] < 32
1560                     || poOpenInfo->pabyHeader[i] > 126 )
1561                     return nullptr;
1562             }
1563 
1564             if( poOpenInfo->pabyHeader[5] != '1'
1565                 && poOpenInfo->pabyHeader[5] != '2'
1566                 && poOpenInfo->pabyHeader[5] != '3' )
1567                 return nullptr;
1568 
1569             if( poOpenInfo->pabyHeader[6] != 'L' )
1570                 return nullptr;
1571             if( poOpenInfo->pabyHeader[8] != '1' && poOpenInfo->pabyHeader[8] != ' ' )
1572                 return nullptr;
1573 
1574             // --------------------------------------------------------------------
1575             //      Find and open the .GEN file.
1576             // --------------------------------------------------------------------
1577             VSIStatBufL sStatBuf;
1578 
1579             CPLString basename = CPLGetBasename( osFileName );
1580             if( basename.size() != 8 )
1581             {
1582                 CPLDebug("SRP", "Invalid basename file");
1583                 return nullptr;
1584             }
1585 
1586             nRecordIndex = static_cast<int>(CPLScanLong( basename + 6, 2 ));
1587 
1588             CPLString path = CPLGetDirname( osFileName );
1589             CPLString basename01 = ResetTo01( basename );
1590             osFileName = CPLFormFilename( path, basename01, ".IMG" );
1591 
1592             osFileName = CPLResetExtension( osFileName, "GEN" );
1593             if( VSIStatL( osFileName, &sStatBuf ) != 0 )
1594             {
1595                 osFileName = CPLResetExtension( osFileName, "gen" );
1596                 if( VSIStatL( osFileName, &sStatBuf ) != 0 )
1597                     return nullptr;
1598             }
1599 
1600             osGENFileName = osFileName;
1601         }
1602     }
1603 
1604     if (!osGENFileName.empty() &&
1605         !osIMGFileName.empty())
1606     {
1607 
1608         if( poOpenInfo->eAccess == GA_Update )
1609         {
1610             CPLError( CE_Failure, CPLE_NotSupported,
1611                 "The SRP driver does not support update access to existing"
1612                 " datasets.\n" );
1613             return nullptr;
1614         }
1615 
1616         DDFModule module;
1617         DDFRecord* record = nullptr;
1618         if (nRecordIndex >= 0 &&
1619             module.Open(osGENFileName.c_str(), TRUE))
1620         {
1621             for( int i = 0; i < nRecordIndex; i++ )
1622             {
1623                 CPLPushErrorHandler( CPLQuietErrorHandler );
1624                 record = module.ReadRecord();
1625                 CPLPopErrorHandler();
1626                 CPLErrorReset();
1627                 if (record == nullptr)
1628                     break;
1629             }
1630         }
1631         SRPDataset* poDS = OpenDataset(osGENFileName.c_str(), osIMGFileName.c_str(), record);
1632 
1633         if (poDS)
1634         {
1635             /* ---------------------------------------------------------- */
1636             /*      Initialize any PAM information.                       */
1637             /* ---------------------------------------------------------- */
1638             poDS->SetDescription( poOpenInfo->pszFilename );
1639             poDS->TryLoadXML();
1640 
1641             /* ---------------------------------------------------------- */
1642             /*      Check for external overviews.                         */
1643             /* ---------------------------------------------------------- */
1644             if( bFromSubdataset )
1645                 poDS->oOvManager.Initialize( poDS, osIMGFileName.c_str() );
1646             else
1647                 poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
1648 
1649             return poDS;
1650         }
1651     }
1652 
1653     return nullptr;
1654 }
1655 
1656 /************************************************************************/
1657 /*                         GDALRegister_SRP()                          */
1658 /************************************************************************/
1659 
GDALRegister_SRP()1660 void GDALRegister_SRP()
1661 
1662 {
1663     if( GDALGetDriverByName( "SRP" ) != nullptr )
1664         return;
1665 
1666     GDALDriver *poDriver = new GDALDriver();
1667 
1668     poDriver->SetDescription( "SRP" );
1669     poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
1670     poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
1671                                "Standard Raster Product (ASRP/USRP)" );
1672     poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "drivers/raster/srp.html" );
1673     poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "img" );
1674     poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" );
1675     poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
1676 
1677     poDriver->pfnOpen = SRPDataset::Open;
1678 
1679     GetGDALDriverManager()->RegisterDriver( poDriver );
1680 }
1681