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-09-10 17:48:50 Functions: 21 22 95.5 %

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

Generated by: LCOV version 1.14