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

Generated by: LCOV version 1.14