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

Generated by: LCOV version 1.14