LCOV - code coverage report
Current view: top level - frmts/adrg - srpdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 605 782 77.4 %
Date: 2024-05-03 15:49:35 Functions: 21 22 95.5 %

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

Generated by: LCOV version 1.14