LCOV - code coverage report
Current view: top level - frmts/adrg - adrgdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 464 590 78.6 %
Date: 2025-10-27 00:14:23 Functions: 18 21 85.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Purpose:  ADRG reader
       4             :  * Author:   Even Rouault, even.rouault at spatialys.com
       5             :  *
       6             :  ******************************************************************************
       7             :  * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
       8             :  *
       9             :  * SPDX-License-Identifier: MIT
      10             :  ****************************************************************************/
      11             : 
      12             : #include "cpl_string.h"
      13             : #include "gdal_pam.h"
      14             : #include "gdal_frmts.h"
      15             : #include "gdal_driver.h"
      16             : #include "gdal_drivermanager.h"
      17             : #include "gdal_openinfo.h"
      18             : #include "gdal_cpp_functions.h"
      19             : #include "iso8211.h"
      20             : #include "ogr_spatialref.h"
      21             : 
      22             : #include <limits>
      23             : #include <new>
      24             : 
      25             : #define N_ELEMENTS(x) (sizeof(x) / sizeof(x[0]))
      26             : 
      27             : #define DIGIT_ZERO '0'
      28             : 
      29             : class ADRGDataset final : public GDALPamDataset
      30             : {
      31             :     friend class ADRGRasterBand;
      32             : 
      33             :     CPLString osGENFileName;
      34             :     CPLString osIMGFileName;
      35             :     OGRSpatialReference m_oSRS{};
      36             : 
      37             :     VSILFILE *fdIMG;
      38             :     int *TILEINDEX;
      39             :     int offsetInIMG;
      40             :     int NFC;
      41             :     int NFL;
      42             :     double LSO;
      43             :     double PSO;
      44             :     int ARV;
      45             :     int BRV;
      46             : 
      47             :     char **papszSubDatasets;
      48             : 
      49             :     GDALGeoTransform m_gt{};
      50             : 
      51             :     static char **GetGENListFromTHF(const char *pszFileName);
      52             :     static char **GetIMGListFromGEN(const char *pszFileName,
      53             :                                     int *pnRecordIndex = nullptr);
      54             :     static ADRGDataset *OpenDataset(const char *pszGENFileName,
      55             :                                     const char *pszIMGFileName,
      56             :                                     DDFRecord *record = nullptr);
      57             :     static DDFRecord *FindRecordInGENForIMG(DDFModule &module,
      58             :                                             const char *pszGENFileName,
      59             :                                             const char *pszIMGFileName);
      60             : 
      61             :   public:
      62             :     ADRGDataset();
      63             :     ~ADRGDataset() override;
      64             : 
      65             :     const OGRSpatialReference *GetSpatialRef() const override;
      66             :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
      67             : 
      68             :     char **GetMetadataDomainList() override;
      69             :     char **GetMetadata(const char *pszDomain = "") override;
      70             : 
      71             :     char **GetFileList() override;
      72             : 
      73             :     void AddSubDataset(const char *pszGENFileName, const char *pszIMGFileName);
      74             : 
      75             :     static GDALDataset *Open(GDALOpenInfo *);
      76             : 
      77             :     static double GetLongitudeFromString(const char *str);
      78             :     static double GetLatitudeFromString(const char *str);
      79             : };
      80             : 
      81             : /************************************************************************/
      82             : /* ==================================================================== */
      83             : /*                            ADRGRasterBand                             */
      84             : /* ==================================================================== */
      85             : /************************************************************************/
      86             : 
      87             : class ADRGRasterBand final : public GDALPamRasterBand
      88             : {
      89             :     friend class ADRGDataset;
      90             : 
      91             :   public:
      92             :     ADRGRasterBand(ADRGDataset *, int);
      93             : 
      94             :     GDALColorInterp GetColorInterpretation() override;
      95             :     CPLErr IReadBlock(int, int, void *) override;
      96             : 
      97             :     double GetNoDataValue(int *pbSuccess = nullptr) override;
      98             : 
      99             :     // virtual int GetOverviewCount();
     100             :     // virtual GDALRasterBand* GetOverview(int i);
     101             : };
     102             : 
     103             : /************************************************************************/
     104             : /*                           ADRGRasterBand()                            */
     105             : /************************************************************************/
     106             : 
     107          24 : ADRGRasterBand::ADRGRasterBand(ADRGDataset *poDSIn, int nBandIn)
     108             : 
     109             : {
     110          24 :     poDS = poDSIn;
     111          24 :     nBand = nBandIn;
     112             : 
     113          24 :     eDataType = GDT_Byte;
     114             : 
     115          24 :     nBlockXSize = 128;
     116          24 :     nBlockYSize = 128;
     117          24 : }
     118             : 
     119             : /************************************************************************/
     120             : /*                            GetNoDataValue()                          */
     121             : /************************************************************************/
     122             : 
     123           0 : double ADRGRasterBand::GetNoDataValue(int *pbSuccess)
     124             : {
     125           0 :     if (pbSuccess)
     126           0 :         *pbSuccess = TRUE;
     127             : 
     128           0 :     return 0.0;
     129             : }
     130             : 
     131             : /************************************************************************/
     132             : /*                       GetColorInterpretation()                       */
     133             : /************************************************************************/
     134             : 
     135           0 : GDALColorInterp ADRGRasterBand::GetColorInterpretation()
     136             : 
     137             : {
     138           0 :     if (nBand == 1)
     139           0 :         return GCI_RedBand;
     140             : 
     141           0 :     else if (nBand == 2)
     142           0 :         return GCI_GreenBand;
     143             : 
     144           0 :     return GCI_BlueBand;
     145             : }
     146             : 
     147             : /************************************************************************/
     148             : /*                             IReadBlock()                             */
     149             : /************************************************************************/
     150             : 
     151           4 : CPLErr ADRGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     152             : 
     153             : {
     154           4 :     ADRGDataset *l_poDS = cpl::down_cast<ADRGDataset *>(poDS);
     155           4 :     int nBlock = nBlockYOff * l_poDS->NFC + nBlockXOff;
     156           4 :     if (nBlockXOff >= l_poDS->NFC || nBlockYOff >= l_poDS->NFL)
     157             :     {
     158           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     159             :                  "nBlockXOff=%d, NFC=%d, nBlockYOff=%d, NFL=%d", nBlockXOff,
     160             :                  l_poDS->NFC, nBlockYOff, l_poDS->NFL);
     161           0 :         return CE_Failure;
     162             :     }
     163           4 :     CPLDebug("ADRG", "(%d,%d) -> nBlock = %d", nBlockXOff, nBlockYOff, nBlock);
     164             : 
     165             :     vsi_l_offset offset;
     166           4 :     if (l_poDS->TILEINDEX)
     167             :     {
     168           4 :         if (l_poDS->TILEINDEX[nBlock] <= 0)
     169             :         {
     170           0 :             memset(pImage, 0, 128 * 128);
     171           0 :             return CE_None;
     172             :         }
     173           4 :         offset = l_poDS->offsetInIMG +
     174           4 :                  static_cast<vsi_l_offset>(l_poDS->TILEINDEX[nBlock] - 1) *
     175           4 :                      128 * 128 * 3 +
     176           4 :                  (nBand - 1) * 128 * 128;
     177             :     }
     178             :     else
     179           0 :         offset = l_poDS->offsetInIMG +
     180           0 :                  static_cast<vsi_l_offset>(nBlock) * 128 * 128 * 3 +
     181           0 :                  (nBand - 1) * 128 * 128;
     182             : 
     183           4 :     if (VSIFSeekL(l_poDS->fdIMG, offset, SEEK_SET) != 0)
     184             :     {
     185           0 :         CPLError(CE_Failure, CPLE_FileIO,
     186             :                  "Cannot seek to offset " CPL_FRMT_GUIB, offset);
     187           0 :         return CE_Failure;
     188             :     }
     189           4 :     if (VSIFReadL(pImage, 1, 128 * 128, l_poDS->fdIMG) != 128 * 128)
     190             :     {
     191           0 :         CPLError(CE_Failure, CPLE_FileIO,
     192             :                  "Cannot read data at offset " CPL_FRMT_GUIB, offset);
     193           0 :         return CE_Failure;
     194             :     }
     195             : 
     196           4 :     return CE_None;
     197             : }
     198             : 
     199             : /************************************************************************/
     200             : /*                          ADRGDataset()                               */
     201             : /************************************************************************/
     202             : 
     203           9 : ADRGDataset::ADRGDataset()
     204             :     : fdIMG(nullptr), TILEINDEX(nullptr), offsetInIMG(0), NFC(0), NFL(0),
     205           9 :       LSO(0.0), PSO(0.0), ARV(0), BRV(0), papszSubDatasets(nullptr)
     206             : {
     207           9 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     208           9 : }
     209             : 
     210             : /************************************************************************/
     211             : /*                          ~ADRGDataset()                              */
     212             : /************************************************************************/
     213             : 
     214          18 : ADRGDataset::~ADRGDataset()
     215             : {
     216           9 :     CSLDestroy(papszSubDatasets);
     217             : 
     218           9 :     if (fdIMG)
     219             :     {
     220           8 :         VSIFCloseL(fdIMG);
     221             :     }
     222             : 
     223           9 :     if (TILEINDEX)
     224             :     {
     225           8 :         delete[] TILEINDEX;
     226             :     }
     227          18 : }
     228             : 
     229             : /************************************************************************/
     230             : /*                            GetFileList()                             */
     231             : /************************************************************************/
     232             : 
     233           3 : char **ADRGDataset::GetFileList()
     234             : {
     235           3 :     char **papszFileList = GDALPamDataset::GetFileList();
     236             : 
     237           3 :     if (!osGENFileName.empty() && !osIMGFileName.empty())
     238             :     {
     239           3 :         CPLString osMainFilename = GetDescription();
     240             :         VSIStatBufL sStat;
     241             : 
     242           3 :         const bool bMainFileReal = VSIStatL(osMainFilename, &sStat) == 0;
     243           3 :         if (bMainFileReal)
     244             :         {
     245           4 :             CPLString osShortMainFilename = CPLGetFilename(osMainFilename);
     246           4 :             CPLString osShortGENFileName = CPLGetFilename(osGENFileName);
     247           2 :             if (!EQUAL(osShortMainFilename.c_str(), osShortGENFileName.c_str()))
     248             :                 papszFileList =
     249           1 :                     CSLAddString(papszFileList, osGENFileName.c_str());
     250             :         }
     251             :         else
     252           1 :             papszFileList = CSLAddString(papszFileList, osGENFileName.c_str());
     253             : 
     254           3 :         papszFileList = CSLAddString(papszFileList, osIMGFileName.c_str());
     255             :     }
     256             : 
     257           3 :     return papszFileList;
     258             : }
     259             : 
     260             : /************************************************************************/
     261             : /*                           AddSubDataset()                            */
     262             : /************************************************************************/
     263             : 
     264           2 : void ADRGDataset::AddSubDataset(const char *pszGENFileName,
     265             :                                 const char *pszIMGFileName)
     266             : {
     267             :     char szName[80];
     268           2 :     int nCount = CSLCount(papszSubDatasets) / 2;
     269             : 
     270           2 :     CPLString osSubDatasetName;
     271           2 :     osSubDatasetName = "ADRG:";
     272           2 :     osSubDatasetName += pszGENFileName;
     273           2 :     osSubDatasetName += ",";
     274           2 :     osSubDatasetName += pszIMGFileName;
     275             : 
     276           2 :     snprintf(szName, sizeof(szName), "SUBDATASET_%d_NAME", nCount + 1);
     277           2 :     papszSubDatasets =
     278           2 :         CSLSetNameValue(papszSubDatasets, szName, osSubDatasetName);
     279             : 
     280           2 :     snprintf(szName, sizeof(szName), "SUBDATASET_%d_DESC", nCount + 1);
     281           2 :     papszSubDatasets =
     282           2 :         CSLSetNameValue(papszSubDatasets, szName, osSubDatasetName);
     283           2 : }
     284             : 
     285             : /************************************************************************/
     286             : /*                      GetMetadataDomainList()                         */
     287             : /************************************************************************/
     288             : 
     289           0 : char **ADRGDataset::GetMetadataDomainList()
     290             : {
     291           0 :     return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
     292           0 :                                    TRUE, "SUBDATASETS", nullptr);
     293             : }
     294             : 
     295             : /************************************************************************/
     296             : /*                            GetMetadata()                             */
     297             : /************************************************************************/
     298             : 
     299           1 : char **ADRGDataset::GetMetadata(const char *pszDomain)
     300             : 
     301             : {
     302           1 :     if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
     303           0 :         return papszSubDatasets;
     304             : 
     305           1 :     return GDALPamDataset::GetMetadata(pszDomain);
     306             : }
     307             : 
     308             : /************************************************************************/
     309             : /*                        GetSpatialRef()                               */
     310             : /************************************************************************/
     311             : 
     312           2 : const OGRSpatialReference *ADRGDataset::GetSpatialRef() const
     313             : {
     314           2 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     315             : }
     316             : 
     317             : /************************************************************************/
     318             : /*                        GetGeoTransform()                             */
     319             : /************************************************************************/
     320             : 
     321           2 : CPLErr ADRGDataset::GetGeoTransform(GDALGeoTransform &gt) const
     322             : {
     323           2 :     if (papszSubDatasets != nullptr)
     324           0 :         return CE_Failure;
     325             : 
     326           2 :     gt = m_gt;
     327             : 
     328           2 :     return CE_None;
     329             : }
     330             : 
     331             : /************************************************************************/
     332             : /*                     GetLongitudeFromString()                         */
     333             : /************************************************************************/
     334             : 
     335           8 : double ADRGDataset::GetLongitudeFromString(const char *str)
     336             : {
     337           8 :     char ddd[3 + 1] = {0};
     338           8 :     char mm[2 + 1] = {0};
     339           8 :     char ssdotss[5 + 1] = {0};
     340           8 :     int sign = (str[0] == '+') ? 1 : -1;
     341           8 :     str++;
     342           8 :     strncpy(ddd, str, 3);
     343           8 :     str += 3;
     344           8 :     strncpy(mm, str, 2);
     345           8 :     str += 2;
     346           8 :     strncpy(ssdotss, str, 5);
     347           8 :     return sign * (CPLAtof(ddd) + CPLAtof(mm) / 60 + CPLAtof(ssdotss) / 3600);
     348             : }
     349             : 
     350             : /************************************************************************/
     351             : /*                      GetLatitudeFromString()                         */
     352             : /************************************************************************/
     353             : 
     354           8 : double ADRGDataset::GetLatitudeFromString(const char *str)
     355             : {
     356           8 :     char ddd[2 + 1] = {0};
     357           8 :     char mm[2 + 1] = {0};
     358           8 :     char ssdotss[5 + 1] = {0};
     359           8 :     int sign = (str[0] == '+') ? 1 : -1;
     360           8 :     str++;
     361           8 :     strncpy(ddd, str, 2);
     362           8 :     str += 2;
     363           8 :     strncpy(mm, str, 2);
     364           8 :     str += 2;
     365           8 :     strncpy(ssdotss, str, 5);
     366           8 :     return sign * (CPLAtof(ddd) + CPLAtof(mm) / 60 + CPLAtof(ssdotss) / 3600);
     367             : }
     368             : 
     369             : /************************************************************************/
     370             : /*                      FindRecordInGENForIMG()                         */
     371             : /************************************************************************/
     372             : 
     373           2 : DDFRecord *ADRGDataset::FindRecordInGENForIMG(DDFModule &module,
     374             :                                               const char *pszGENFileName,
     375             :                                               const char *pszIMGFileName)
     376             : {
     377             :     /* Finds the GEN file corresponding to the IMG file */
     378           2 :     if (!module.Open(pszGENFileName, TRUE))
     379           0 :         return nullptr;
     380             : 
     381           4 :     CPLString osShortIMGFilename = CPLGetFilename(pszIMGFileName);
     382             : 
     383             :     /* Now finds the record */
     384             :     while (true)
     385             :     {
     386           7 :         CPLPushErrorHandler(CPLQuietErrorHandler);
     387           7 :         DDFRecord *record = module.ReadRecord();
     388           7 :         CPLPopErrorHandler();
     389           7 :         CPLErrorReset();
     390           7 :         if (record == nullptr)
     391           0 :             return nullptr;
     392             : 
     393           7 :         if (record->GetFieldCount() >= 5)
     394             :         {
     395           5 :             DDFField *field = record->GetField(0);
     396           5 :             DDFFieldDefn *fieldDefn = field->GetFieldDefn();
     397          10 :             if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
     398           5 :                   fieldDefn->GetSubfieldCount() == 2))
     399             :             {
     400           2 :                 continue;
     401             :             }
     402             : 
     403           5 :             const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
     404           5 :             if (RTY == nullptr)
     405           0 :                 continue;
     406             :             /* Ignore overviews */
     407           5 :             if (strcmp(RTY, "OVV") == 0)
     408           2 :                 continue;
     409             : 
     410           3 :             if (strcmp(RTY, "GIN") != 0)
     411           0 :                 continue;
     412             : 
     413           3 :             field = record->GetField(3);
     414           3 :             fieldDefn = field->GetFieldDefn();
     415             : 
     416           6 :             if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
     417           3 :                   fieldDefn->GetSubfieldCount() == 15))
     418             :             {
     419           0 :                 continue;
     420             :             }
     421             : 
     422           3 :             const char *pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
     423           3 :             if (pszBAD == nullptr || strlen(pszBAD) != 12)
     424           0 :                 continue;
     425           3 :             CPLString osBAD = pszBAD;
     426             :             {
     427           3 :                 char *c = (char *)strchr(osBAD.c_str(), ' ');
     428           3 :                 if (c)
     429           0 :                     *c = 0;
     430             :             }
     431             : 
     432           3 :             if (EQUAL(osShortIMGFilename.c_str(), osBAD.c_str()))
     433             :             {
     434           2 :                 return record;
     435             :             }
     436             :         }
     437           5 :     }
     438             : }
     439             : 
     440             : /************************************************************************/
     441             : /*                           OpenDataset()                              */
     442             : /************************************************************************/
     443             : 
     444           8 : ADRGDataset *ADRGDataset::OpenDataset(const char *pszGENFileName,
     445             :                                       const char *pszIMGFileName,
     446             :                                       DDFRecord *record)
     447             : {
     448          16 :     DDFModule module;
     449             : 
     450           8 :     int SCA = 0;
     451           8 :     int ZNA = 0;
     452             :     double PSP;
     453             :     int ARV;
     454             :     int BRV;
     455             :     double LSO;
     456             :     double PSO;
     457             :     int NFL;
     458             :     int NFC;
     459          16 :     CPLString osBAD;
     460             :     int TIF;
     461           8 :     int *TILEINDEX = nullptr;
     462             : 
     463           8 :     if (record == nullptr)
     464             :     {
     465           2 :         record = FindRecordInGENForIMG(module, pszGENFileName, pszIMGFileName);
     466           2 :         if (record == nullptr)
     467           0 :             return nullptr;
     468             :     }
     469             : 
     470           8 :     DDFField *field = record->GetField(1);
     471           8 :     if (field == nullptr)
     472           0 :         return nullptr;
     473           8 :     DDFFieldDefn *fieldDefn = field->GetFieldDefn();
     474             : 
     475          16 :     if (!(strcmp(fieldDefn->GetName(), "DSI") == 0 &&
     476           8 :           fieldDefn->GetSubfieldCount() == 2))
     477             :     {
     478           0 :         return nullptr;
     479             :     }
     480             : 
     481           8 :     const char *pszPTR = record->GetStringSubfield("DSI", 0, "PRT", 0);
     482           8 :     if (pszPTR == nullptr || !EQUAL(pszPTR, "ADRG"))
     483           0 :         return nullptr;
     484             : 
     485           8 :     const char *pszNAM = record->GetStringSubfield("DSI", 0, "NAM", 0);
     486           8 :     if (pszNAM == nullptr || strlen(pszNAM) != 8)
     487           0 :         return nullptr;
     488          16 :     CPLString osNAM = pszNAM;
     489             : 
     490           8 :     field = record->GetField(2);
     491           8 :     if (field == nullptr)
     492           0 :         return nullptr;
     493           8 :     fieldDefn = field->GetFieldDefn();
     494             : 
     495             :     // TODO: Support on GIN things.  And what is GIN?
     496             :     // GIN might mean general information and might be a typo of GEN.
     497             :     // if( isGIN )
     498             :     {
     499          16 :         if (!(strcmp(fieldDefn->GetName(), "GEN") == 0 &&
     500           8 :               fieldDefn->GetSubfieldCount() == 21))
     501             :         {
     502           0 :             return nullptr;
     503             :         }
     504             : 
     505           8 :         if (record->GetIntSubfield("GEN", 0, "STR", 0) != 3)
     506           0 :             return nullptr;
     507             : 
     508           8 :         SCA = record->GetIntSubfield("GEN", 0, "SCA", 0);
     509           8 :         CPLDebug("ADRG", "SCA=%d", SCA);
     510             : 
     511           8 :         ZNA = record->GetIntSubfield("GEN", 0, "ZNA", 0);
     512           8 :         CPLDebug("ADRG", "ZNA=%d", ZNA);
     513             : 
     514           8 :         PSP = record->GetFloatSubfield("GEN", 0, "PSP", 0);
     515           8 :         CPLDebug("ADRG", "PSP=%f", PSP);
     516             : 
     517           8 :         ARV = record->GetIntSubfield("GEN", 0, "ARV", 0);
     518           8 :         CPLDebug("ADRG", "ARV=%d", ARV);
     519             : 
     520           8 :         BRV = record->GetIntSubfield("GEN", 0, "BRV", 0);
     521           8 :         CPLDebug("ADRG", "BRV=%d", BRV);
     522           8 :         if (ARV <= 0 || (ZNA != 9 && ZNA != 18 && BRV <= 0))
     523           0 :             return nullptr;
     524             : 
     525           8 :         const char *pszLSO = record->GetStringSubfield("GEN", 0, "LSO", 0);
     526           8 :         if (pszLSO == nullptr || strlen(pszLSO) != 11)
     527           0 :             return nullptr;
     528           8 :         LSO = GetLongitudeFromString(pszLSO);
     529           8 :         CPLDebug("ADRG", "LSO=%f", LSO);
     530             : 
     531           8 :         const char *pszPSO = record->GetStringSubfield("GEN", 0, "PSO", 0);
     532           8 :         if (pszPSO == nullptr || strlen(pszPSO) != 10)
     533           0 :             return nullptr;
     534           8 :         PSO = GetLatitudeFromString(pszPSO);
     535           8 :         CPLDebug("ADRG", "PSO=%f", PSO);
     536             :     }
     537             : #if 0
     538             :     else
     539             :     {
     540             :         if( !(strcmp(fieldDefn->GetName(), "OVI") == 0 &&
     541             :                 fieldDefn->GetSubfieldCount() == 5) )
     542             :         {
     543             :             return NULL;
     544             :         }
     545             : 
     546             :         if( record->GetIntSubfield("OVI", 0, "STR", 0) != 3 )
     547             :             return NULL;
     548             : 
     549             :         ARV = record->GetIntSubfield("OVI", 0, "ARV", 0);
     550             :         CPLDebug("ADRG", "ARV=%d", ARV);
     551             : 
     552             :         BRV = record->GetIntSubfield("OVI", 0, "BRV", 0);
     553             :         CPLDebug("ADRG", "BRV=%d", BRV);
     554             : 
     555             :         const char* pszLSO = record->GetStringSubfield("OVI", 0, "LSO", 0);
     556             :         if( pszLSO == NULL || strlen(pszLSO) != 11 )
     557             :             return NULL;
     558             :         LSO = GetLongitudeFromString(pszLSO);
     559             :         CPLDebug("ADRG", "LSO=%f", LSO);
     560             : 
     561             :         const char* pszPSO = record->GetStringSubfield("OVI", 0, "PSO", 0);
     562             :         if( pszPSO == NULL || strlen(pszPSO) != 10 )
     563             :             return NULL;
     564             :         PSO = GetLatitudeFromString(pszPSO);
     565             :         CPLDebug("ADRG", "PSO=%f", PSO);
     566             :     }
     567             : #endif
     568             : 
     569           8 :     field = record->GetField(3);
     570           8 :     if (field == nullptr)
     571           0 :         return nullptr;
     572           8 :     fieldDefn = field->GetFieldDefn();
     573             : 
     574          16 :     if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
     575           8 :           fieldDefn->GetSubfieldCount() == 15))
     576             :     {
     577           0 :         return nullptr;
     578             :     }
     579             : 
     580           8 :     NFL = record->GetIntSubfield("SPR", 0, "NFL", 0);
     581           8 :     CPLDebug("ADRG", "NFL=%d", NFL);
     582             : 
     583           8 :     NFC = record->GetIntSubfield("SPR", 0, "NFC", 0);
     584           8 :     CPLDebug("ADRG", "NFC=%d", NFC);
     585             : 
     586           8 :     const auto knIntMax = std::numeric_limits<int>::max();
     587           8 :     if (NFL <= 0 || NFC <= 0 || NFL > knIntMax / 128 || NFC > knIntMax / 128 ||
     588           8 :         NFL > (knIntMax - 1) / (NFC * 5))
     589             :     {
     590           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid NFL / NFC values");
     591           0 :         return nullptr;
     592             :     }
     593             : 
     594           8 :     int PNC = record->GetIntSubfield("SPR", 0, "PNC", 0);
     595           8 :     CPLDebug("ADRG", "PNC=%d", PNC);
     596           8 :     if (PNC != 128)
     597             :     {
     598           0 :         return nullptr;
     599             :     }
     600             : 
     601           8 :     int PNL = record->GetIntSubfield("SPR", 0, "PNL", 0);
     602           8 :     CPLDebug("ADRG", "PNL=%d", PNL);
     603           8 :     if (PNL != 128)
     604             :     {
     605           0 :         return nullptr;
     606             :     }
     607             : 
     608           8 :     const char *pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
     609           8 :     if (pszBAD == nullptr || strlen(pszBAD) != 12)
     610           0 :         return nullptr;
     611           8 :     osBAD = pszBAD;
     612             :     {
     613           8 :         char *c = (char *)strchr(osBAD.c_str(), ' ');
     614           8 :         if (c)
     615           0 :             *c = 0;
     616             :     }
     617           8 :     CPLDebug("ADRG", "BAD=%s", osBAD.c_str());
     618             : 
     619           8 :     const DDFSubfieldDefn *subfieldDefn = fieldDefn->GetSubfield(14);
     620          16 :     if (!(strcmp(subfieldDefn->GetName(), "TIF") == 0 &&
     621           8 :           (subfieldDefn->GetFormat())[0] == 'A'))
     622             :     {
     623           0 :         return nullptr;
     624             :     }
     625             : 
     626           8 :     const char *pszTIF = record->GetStringSubfield("SPR", 0, "TIF", 0);
     627           8 :     if (pszTIF == nullptr)
     628           0 :         return nullptr;
     629           8 :     TIF = pszTIF[0] == 'Y';
     630           8 :     CPLDebug("ADRG", "TIF=%d", TIF);
     631             : 
     632           8 :     if (TIF)
     633             :     {
     634           8 :         if (record->GetFieldCount() != 6)
     635             :         {
     636           0 :             return nullptr;
     637             :         }
     638             : 
     639           8 :         field = record->GetField(5);
     640           8 :         if (field == nullptr)
     641           0 :             return nullptr;
     642           8 :         fieldDefn = field->GetFieldDefn();
     643             : 
     644           8 :         if (!(strcmp(fieldDefn->GetName(), "TIM") == 0))
     645             :         {
     646           0 :             return nullptr;
     647             :         }
     648             : 
     649           8 :         if (field->GetDataSize() != 5 * NFL * NFC + 1)
     650             :         {
     651           0 :             return nullptr;
     652             :         }
     653             : 
     654             :         try
     655             :         {
     656           8 :             TILEINDEX = new int[NFL * NFC];
     657             :         }
     658           0 :         catch (const std::exception &)
     659             :         {
     660           0 :             return nullptr;
     661             :         }
     662           8 :         const char *ptr = field->GetData();
     663           8 :         char offset[5 + 1] = {0};
     664          16 :         for (int i = 0; i < NFL * NFC; i++)
     665             :         {
     666           8 :             strncpy(offset, ptr, 5);
     667           8 :             ptr += 5;
     668           8 :             TILEINDEX[i] = atoi(offset);
     669             :             // CPLDebug("ADRG", "TSI[%d]=%d", i, TILEINDEX[i]);
     670             :         }
     671             :     }
     672             : 
     673           8 :     VSILFILE *fdIMG = VSIFOpenL(pszIMGFileName, "rb");
     674           8 :     if (fdIMG == nullptr)
     675             :     {
     676           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s\n",
     677             :                  pszIMGFileName);
     678           0 :         delete[] TILEINDEX;
     679           0 :         return nullptr;
     680             :     }
     681             : 
     682             :     /* Skip ISO8211 header of IMG file */
     683           8 :     int offsetInIMG = 0;
     684             :     char c;
     685             :     char recordName[3];
     686           8 :     if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
     687             :     {
     688           0 :         VSIFCloseL(fdIMG);
     689           0 :         delete[] TILEINDEX;
     690           0 :         return nullptr;
     691             :     }
     692        2064 :     while (!VSIFEofL(fdIMG))
     693             :     {
     694        2064 :         if (c == 30)
     695             :         {
     696          48 :             if (VSIFReadL(recordName, 1, 3, fdIMG) != 3)
     697             :             {
     698           0 :                 VSIFCloseL(fdIMG);
     699           0 :                 delete[] TILEINDEX;
     700           0 :                 return nullptr;
     701             :             }
     702          48 :             offsetInIMG += 3;
     703          48 :             if (STARTS_WITH(recordName, "IMG"))
     704             :             {
     705           8 :                 offsetInIMG += 4;
     706          16 :                 if (VSIFSeekL(fdIMG, 3, SEEK_CUR) != 0 ||
     707           8 :                     VSIFReadL(&c, 1, 1, fdIMG) != 1)
     708             :                 {
     709           0 :                     VSIFCloseL(fdIMG);
     710           0 :                     delete[] TILEINDEX;
     711           0 :                     return nullptr;
     712             :                 }
     713       14152 :                 while (c == ' ')
     714             :                 {
     715       14144 :                     offsetInIMG++;
     716       14144 :                     if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
     717             :                     {
     718           0 :                         VSIFCloseL(fdIMG);
     719           0 :                         delete[] TILEINDEX;
     720           0 :                         return nullptr;
     721             :                     }
     722             :                 }
     723           8 :                 offsetInIMG++;
     724           8 :                 break;
     725             :             }
     726             :         }
     727             : 
     728        2056 :         offsetInIMG++;
     729        2056 :         if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
     730             :         {
     731           0 :             VSIFCloseL(fdIMG);
     732           0 :             delete[] TILEINDEX;
     733           0 :             return nullptr;
     734             :         }
     735             :     }
     736             : 
     737           8 :     if (VSIFEofL(fdIMG))
     738             :     {
     739           0 :         VSIFCloseL(fdIMG);
     740           0 :         delete[] TILEINDEX;
     741           0 :         return nullptr;
     742             :     }
     743             : 
     744           8 :     CPLDebug("ADRG", "Img offset data = %d", offsetInIMG);
     745             : 
     746           8 :     ADRGDataset *poDS = new ADRGDataset();
     747             : 
     748           8 :     poDS->osGENFileName = pszGENFileName;
     749           8 :     poDS->osIMGFileName = pszIMGFileName;
     750           8 :     poDS->NFC = NFC;
     751           8 :     poDS->NFL = NFL;
     752           8 :     poDS->nRasterXSize = NFC * 128;
     753           8 :     poDS->nRasterYSize = NFL * 128;
     754           8 :     poDS->LSO = LSO;
     755           8 :     poDS->PSO = PSO;
     756           8 :     poDS->ARV = ARV;
     757           8 :     poDS->BRV = BRV;
     758           8 :     poDS->TILEINDEX = TILEINDEX;
     759           8 :     poDS->fdIMG = fdIMG;
     760           8 :     poDS->offsetInIMG = offsetInIMG;
     761             : 
     762           8 :     if (ZNA == 9)
     763             :     {
     764             :         // North Polar Case
     765           1 :         poDS->m_gt[0] = 111319.4907933 * (90.0 - PSO) * sin(LSO * M_PI / 180.0);
     766           1 :         poDS->m_gt[1] = 40075016.68558 / ARV;
     767           1 :         poDS->m_gt[2] = 0.0;
     768           2 :         poDS->m_gt[3] =
     769           1 :             -111319.4907933 * (90.0 - PSO) * cos(LSO * M_PI / 180.0);
     770           1 :         poDS->m_gt[4] = 0.0;
     771           1 :         poDS->m_gt[5] = -40075016.68558 / ARV;
     772           1 :         poDS->m_oSRS.importFromWkt(
     773             :             "PROJCS[\"ARC_System_Zone_09\",GEOGCS[\"GCS_Sphere\","
     774             :             "DATUM[\"D_Sphere\",SPHEROID[\"Sphere\",6378137.0,0.0]],"
     775             :             "PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],"
     776             :             "PROJECTION[\"Azimuthal_Equidistant\"],"
     777             :             "PARAMETER[\"latitude_of_center\",90],"
     778             :             "PARAMETER[\"longitude_of_center\",0],"
     779             :             "PARAMETER[\"false_easting\",0],"
     780             :             "PARAMETER[\"false_northing\",0],"
     781             :             "UNIT[\"metre\",1]]");
     782             :     }
     783           7 :     else if (ZNA == 18)
     784             :     {
     785             :         // South Polar Case
     786           1 :         poDS->m_gt[0] = 111319.4907933 * (90.0 + PSO) * sin(LSO * M_PI / 180.0);
     787           1 :         poDS->m_gt[1] = 40075016.68558 / ARV;
     788           1 :         poDS->m_gt[2] = 0.0;
     789           1 :         poDS->m_gt[3] = 111319.4907933 * (90.0 + PSO) * cos(LSO * M_PI / 180.0);
     790           1 :         poDS->m_gt[4] = 0.0;
     791           1 :         poDS->m_gt[5] = -40075016.68558 / ARV;
     792           1 :         poDS->m_oSRS.importFromWkt(
     793             :             "PROJCS[\"ARC_System_Zone_18\",GEOGCS[\"GCS_Sphere\","
     794             :             "DATUM[\"D_Sphere\",SPHEROID[\"Sphere\",6378137.0,0.0]],"
     795             :             "PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],"
     796             :             "PROJECTION[\"Azimuthal_Equidistant\"],"
     797             :             "PARAMETER[\"latitude_of_center\",-90],"
     798             :             "PARAMETER[\"longitude_of_center\",0],"
     799             :             "PARAMETER[\"false_easting\",0],"
     800             :             "PARAMETER[\"false_northing\",0],"
     801             :             "UNIT[\"metre\",1]]");
     802             :     }
     803             :     else
     804             :     {
     805           6 :         poDS->m_gt[0] = LSO;
     806           6 :         poDS->m_gt[1] = 360. / ARV;
     807           6 :         poDS->m_gt[2] = 0.0;
     808           6 :         poDS->m_gt[3] = PSO;
     809           6 :         poDS->m_gt[4] = 0.0;
     810           6 :         poDS->m_gt[5] = -360. / BRV;
     811           6 :         poDS->m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
     812             :     }
     813             : 
     814             :     // if( isGIN )
     815             :     {
     816             :         char szValue[32];
     817           8 :         snprintf(szValue, sizeof(szValue), "%d", SCA);
     818           8 :         poDS->SetMetadataItem("ADRG_SCA", szValue);
     819           8 :         snprintf(szValue, sizeof(szValue), "%d", ZNA);
     820           8 :         poDS->SetMetadataItem("ADRG_ZNA", szValue);
     821             :     }
     822             : 
     823           8 :     poDS->SetMetadataItem("ADRG_NAM", osNAM.c_str());
     824             : 
     825           8 :     poDS->nBands = 3;
     826          32 :     for (int i = 0; i < poDS->nBands; i++)
     827          24 :         poDS->SetBand(i + 1, new ADRGRasterBand(poDS, i + 1));
     828             : 
     829           8 :     return poDS;
     830             : }
     831             : 
     832             : /************************************************************************/
     833             : /*                          GetGENListFromTHF()                         */
     834             : /************************************************************************/
     835             : 
     836           7 : char **ADRGDataset::GetGENListFromTHF(const char *pszFileName)
     837             : {
     838          14 :     DDFModule module;
     839           7 :     DDFRecord *record = nullptr;
     840           7 :     int nFilenames = 0;
     841           7 :     char **papszFileNames = nullptr;
     842             : 
     843           7 :     if (!module.Open(pszFileName, TRUE))
     844           0 :         return papszFileNames;
     845             : 
     846             :     while (true)
     847             :     {
     848          27 :         CPLPushErrorHandler(CPLQuietErrorHandler);
     849          27 :         record = module.ReadRecord();
     850          27 :         CPLPopErrorHandler();
     851          27 :         CPLErrorReset();
     852          27 :         if (record == nullptr)
     853           7 :             break;
     854             : 
     855          20 :         if (record->GetFieldCount() >= 2)
     856             :         {
     857          20 :             DDFField *field = record->GetField(0);
     858          20 :             DDFFieldDefn *fieldDefn = field->GetFieldDefn();
     859          40 :             if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
     860          20 :                   fieldDefn->GetSubfieldCount() == 2))
     861             :             {
     862           0 :                 continue;
     863             :             }
     864             : 
     865          20 :             const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
     866          20 :             if (RTY == nullptr || !(strcmp(RTY, "TFN") == 0))
     867             :             {
     868          17 :                 continue;
     869             :             }
     870             : 
     871           3 :             int iVFFFieldInstance = 0;
     872          16 :             for (int i = 1; i < record->GetFieldCount(); i++)
     873             :             {
     874          13 :                 field = record->GetField(i);
     875          13 :                 fieldDefn = field->GetFieldDefn();
     876             : 
     877          26 :                 if (!(strcmp(fieldDefn->GetName(), "VFF") == 0 &&
     878          13 :                       fieldDefn->GetSubfieldCount() == 1))
     879             :                 {
     880           0 :                     continue;
     881             :                 }
     882             : 
     883          13 :                 const char *pszVFF = record->GetStringSubfield(
     884             :                     "VFF", iVFFFieldInstance++, "VFF", 0);
     885          13 :                 if (pszVFF == nullptr)
     886           0 :                     continue;
     887          13 :                 CPLString osSubFileName(pszVFF);
     888          13 :                 char *c = (char *)strchr(osSubFileName.c_str(), ' ');
     889          13 :                 if (c)
     890          13 :                     *c = 0;
     891          13 :                 if (EQUAL(CPLGetExtensionSafe(osSubFileName.c_str()).c_str(),
     892             :                           "GEN"))
     893             :                 {
     894           3 :                     CPLDebug("ADRG", "Found GEN file in THF : %s",
     895             :                              osSubFileName.c_str());
     896           3 :                     CPLString osGENFileName(CPLGetDirnameSafe(pszFileName));
     897             :                     char **tokens =
     898           3 :                         CSLTokenizeString2(osSubFileName.c_str(), "/\"", 0);
     899           3 :                     char **ptr = tokens;
     900           3 :                     if (ptr == nullptr)
     901           0 :                         continue;
     902           6 :                     while (*ptr)
     903             :                     {
     904             :                         char **papszDirContent =
     905           3 :                             VSIReadDir(osGENFileName.c_str());
     906           3 :                         char **ptrDir = papszDirContent;
     907           3 :                         if (ptrDir)
     908             :                         {
     909           8 :                             while (*ptrDir)
     910             :                             {
     911           8 :                                 if (EQUAL(*ptrDir, *ptr))
     912             :                                 {
     913           3 :                                     osGENFileName = CPLFormFilenameSafe(
     914             :                                         osGENFileName.c_str(), *ptrDir,
     915           3 :                                         nullptr);
     916           3 :                                     CPLDebug("ADRG",
     917             :                                              "Building GEN full file name : %s",
     918             :                                              osGENFileName.c_str());
     919           3 :                                     break;
     920             :                                 }
     921           5 :                                 ptrDir++;
     922             :                             }
     923             :                         }
     924           3 :                         if (ptrDir == nullptr)
     925           0 :                             break;
     926           3 :                         CSLDestroy(papszDirContent);
     927           3 :                         ptr++;
     928             :                     }
     929           3 :                     int isNameValid = *ptr == nullptr;
     930           3 :                     CSLDestroy(tokens);
     931           3 :                     if (isNameValid)
     932             :                     {
     933           6 :                         papszFileNames = (char **)CPLRealloc(
     934           3 :                             papszFileNames, sizeof(char *) * (nFilenames + 2));
     935           6 :                         papszFileNames[nFilenames] =
     936           3 :                             CPLStrdup(osGENFileName.c_str());
     937           3 :                         papszFileNames[nFilenames + 1] = nullptr;
     938           3 :                         nFilenames++;
     939             :                     }
     940             :                 }
     941             :             }
     942             :         }
     943          20 :     }
     944           7 :     return papszFileNames;
     945             : }
     946             : 
     947             : /************************************************************************/
     948             : /*                          GetIMGListFromGEN()                         */
     949             : /************************************************************************/
     950             : 
     951           7 : char **ADRGDataset::GetIMGListFromGEN(const char *pszFileName,
     952             :                                       int *pnRecordIndex)
     953             : {
     954           7 :     DDFRecord *record = nullptr;
     955          14 :     CPLStringList aosFilenames;
     956           7 :     int nRecordIndex = -1;
     957             : 
     958           7 :     if (pnRecordIndex)
     959           7 :         *pnRecordIndex = -1;
     960             : 
     961          14 :     DDFModule module;
     962           7 :     if (!module.Open(pszFileName, TRUE))
     963           0 :         return nullptr;
     964             : 
     965             :     while (true)
     966             :     {
     967          29 :         nRecordIndex++;
     968             : 
     969          29 :         CPLPushErrorHandler(CPLQuietErrorHandler);
     970          29 :         record = module.ReadRecord();
     971          29 :         CPLPopErrorHandler();
     972          29 :         CPLErrorReset();
     973          29 :         if (record == nullptr)
     974           7 :             break;
     975             : 
     976          22 :         if (record->GetFieldCount() >= 5)
     977             :         {
     978          15 :             DDFField *field = record->GetField(0);
     979          15 :             DDFFieldDefn *fieldDefn = field->GetFieldDefn();
     980          30 :             if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
     981          15 :                   fieldDefn->GetSubfieldCount() == 2))
     982             :             {
     983           7 :                 continue;
     984             :             }
     985             : 
     986          15 :             const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
     987          15 :             if (RTY == nullptr)
     988           0 :                 continue;
     989             :             /* Ignore overviews */
     990          15 :             if (strcmp(RTY, "OVV") == 0)
     991           7 :                 continue;
     992             : 
     993             :             // TODO: Fix the non-GIN section or remove it.
     994           8 :             if (strcmp(RTY, "GIN") != 0)
     995           0 :                 continue;
     996             : 
     997             :             /* make sure that the GEN file is part of an ADRG dataset, not a SRP
     998             :              * dataset, by checking that the GEN field contains a NWO subfield
     999             :              */
    1000           8 :             const char *NWO = record->GetStringSubfield("GEN", 0, "NWO", 0);
    1001           8 :             if (NWO == nullptr)
    1002             :             {
    1003           0 :                 return nullptr;
    1004             :             }
    1005             : 
    1006           8 :             field = record->GetField(3);
    1007           8 :             if (field == nullptr)
    1008           0 :                 continue;
    1009           8 :             fieldDefn = field->GetFieldDefn();
    1010             : 
    1011          16 :             if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
    1012           8 :                   fieldDefn->GetSubfieldCount() == 15))
    1013             :             {
    1014           0 :                 continue;
    1015             :             }
    1016             : 
    1017           8 :             const char *pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
    1018           8 :             if (pszBAD == nullptr || strlen(pszBAD) != 12)
    1019           0 :                 continue;
    1020           8 :             std::string osBAD = pszBAD;
    1021           8 :             const auto nSpacePos = osBAD.find(' ');
    1022           8 :             if (nSpacePos != std::string::npos)
    1023           0 :                 osBAD.resize(nSpacePos);
    1024           8 :             CPLDebug("ADRG", "BAD=%s", osBAD.c_str());
    1025           8 :             if (CPLHasPathTraversal(osBAD.c_str()))
    1026             :             {
    1027           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1028             :                          "Path traversal detected in %s", osBAD.c_str());
    1029           0 :                 return nullptr;
    1030             :             }
    1031             : 
    1032             :             /* Build full IMG file name from BAD value */
    1033          16 :             CPLString osGENDir(CPLGetDirnameSafe(pszFileName));
    1034             : 
    1035             :             std::string osFileName =
    1036          16 :                 CPLFormFilenameSafe(osGENDir.c_str(), osBAD.c_str(), nullptr);
    1037             :             VSIStatBufL sStatBuf;
    1038           8 :             if (VSIStatL(osFileName.c_str(), &sStatBuf) == 0)
    1039             :             {
    1040           8 :                 osBAD = std::move(osFileName);
    1041           8 :                 CPLDebug("ADRG", "Building IMG full file name : %s",
    1042             :                          osBAD.c_str());
    1043             :             }
    1044             :             else
    1045             :             {
    1046           0 :                 char **papszDirContent = nullptr;
    1047           0 :                 if (strcmp(osGENDir.c_str(), "/vsimem") == 0)
    1048             :                 {
    1049           0 :                     CPLString osTmp = osGENDir + "/";
    1050           0 :                     papszDirContent = VSIReadDir(osTmp);
    1051             :                 }
    1052             :                 else
    1053           0 :                     papszDirContent = VSIReadDir(osGENDir);
    1054           0 :                 char **ptrDir = papszDirContent;
    1055           0 :                 while (ptrDir && *ptrDir)
    1056             :                 {
    1057           0 :                     if (EQUAL(*ptrDir, osBAD.c_str()))
    1058             :                     {
    1059           0 :                         osBAD = CPLFormFilenameSafe(osGENDir.c_str(), *ptrDir,
    1060           0 :                                                     nullptr);
    1061           0 :                         CPLDebug("ADRG", "Building IMG full file name : %s",
    1062             :                                  osBAD.c_str());
    1063           0 :                         break;
    1064             :                     }
    1065           0 :                     ptrDir++;
    1066             :                 }
    1067           0 :                 CSLDestroy(papszDirContent);
    1068             :             }
    1069             : 
    1070           8 :             if (aosFilenames.empty() && pnRecordIndex)
    1071           7 :                 *pnRecordIndex = nRecordIndex;
    1072             : 
    1073           8 :             aosFilenames.AddString(osBAD.c_str());
    1074             :         }
    1075          22 :     }
    1076             : 
    1077           7 :     return aosFilenames.StealList();
    1078             : }
    1079             : 
    1080             : /************************************************************************/
    1081             : /*                                Open()                                */
    1082             : /************************************************************************/
    1083             : 
    1084       33718 : GDALDataset *ADRGDataset::Open(GDALOpenInfo *poOpenInfo)
    1085             : {
    1086       33718 :     int nRecordIndex = -1;
    1087       67432 :     CPLString osGENFileName;
    1088       67429 :     CPLString osIMGFileName;
    1089       33716 :     bool bFromSubdataset = false;
    1090             : 
    1091       33716 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "ADRG:"))
    1092             :     {
    1093             :         char **papszTokens =
    1094           2 :             CSLTokenizeString2(poOpenInfo->pszFilename + 5, ",", 0);
    1095           2 :         if (CSLCount(papszTokens) == 2)
    1096             :         {
    1097           2 :             osGENFileName = papszTokens[0];
    1098           2 :             osIMGFileName = papszTokens[1];
    1099           2 :             bFromSubdataset = true;
    1100             :         }
    1101           2 :         CSLDestroy(papszTokens);
    1102             :     }
    1103             :     else
    1104             :     {
    1105       33714 :         if (poOpenInfo->nHeaderBytes < 500)
    1106       31534 :             return nullptr;
    1107             : 
    1108        2185 :         CPLString osFileName(poOpenInfo->pszFilename);
    1109        2185 :         if (EQUAL(CPLGetExtensionSafe(osFileName.c_str()).c_str(), "THF"))
    1110             :         {
    1111           7 :             char **papszFileNames = GetGENListFromTHF(osFileName.c_str());
    1112           7 :             if (papszFileNames == nullptr)
    1113           4 :                 return nullptr;
    1114           3 :             if (papszFileNames[1] == nullptr)
    1115             :             {
    1116           3 :                 osFileName = papszFileNames[0];
    1117           3 :                 CSLDestroy(papszFileNames);
    1118             :             }
    1119             :             else
    1120             :             {
    1121           0 :                 char **ptr = papszFileNames;
    1122           0 :                 ADRGDataset *poDS = new ADRGDataset();
    1123           0 :                 while (*ptr)
    1124             :                 {
    1125           0 :                     char **papszIMGFileNames = GetIMGListFromGEN(*ptr);
    1126           0 :                     char **papszIMGIter = papszIMGFileNames;
    1127           0 :                     while (papszIMGIter && *papszIMGIter)
    1128             :                     {
    1129           0 :                         poDS->AddSubDataset(*ptr, *papszIMGIter);
    1130           0 :                         papszIMGIter++;
    1131             :                     }
    1132           0 :                     CSLDestroy(papszIMGFileNames);
    1133             : 
    1134           0 :                     ptr++;
    1135             :                 }
    1136           0 :                 CSLDestroy(papszFileNames);
    1137           0 :                 return poDS;
    1138             :             }
    1139             :         }
    1140             : 
    1141        2181 :         if (EQUAL(CPLGetExtensionSafe(osFileName.c_str()).c_str(), "GEN"))
    1142             :         {
    1143           7 :             osGENFileName = osFileName;
    1144             : 
    1145             :             char **papszFileNames =
    1146           7 :                 GetIMGListFromGEN(osFileName.c_str(), &nRecordIndex);
    1147           7 :             if (papszFileNames == nullptr)
    1148           0 :                 return nullptr;
    1149           7 :             if (papszFileNames[1] == nullptr)
    1150             :             {
    1151           6 :                 osIMGFileName = papszFileNames[0];
    1152           6 :                 CSLDestroy(papszFileNames);
    1153             :             }
    1154             :             else
    1155             :             {
    1156           1 :                 char **ptr = papszFileNames;
    1157           1 :                 ADRGDataset *poDS = new ADRGDataset();
    1158           3 :                 while (*ptr)
    1159             :                 {
    1160           2 :                     poDS->AddSubDataset(osFileName.c_str(), *ptr);
    1161           2 :                     ptr++;
    1162             :                 }
    1163           1 :                 CSLDestroy(papszFileNames);
    1164           1 :                 return poDS;
    1165             :             }
    1166             :         }
    1167             :     }
    1168             : 
    1169        2182 :     if (!osGENFileName.empty() && !osIMGFileName.empty())
    1170             :     {
    1171           8 :         if (poOpenInfo->eAccess == GA_Update)
    1172             :         {
    1173           0 :             ReportUpdateNotSupportedByDriver("ADRG");
    1174           8 :             return nullptr;
    1175             :         }
    1176             : 
    1177           8 :         DDFModule module;
    1178           8 :         DDFRecord *record = nullptr;
    1179           8 :         if (nRecordIndex >= 0 && module.Open(osGENFileName.c_str(), TRUE))
    1180             :         {
    1181          24 :             for (int i = 0; i <= nRecordIndex; i++)
    1182             :             {
    1183          18 :                 CPLPushErrorHandler(CPLQuietErrorHandler);
    1184          18 :                 record = module.ReadRecord();
    1185          18 :                 CPLPopErrorHandler();
    1186          18 :                 CPLErrorReset();
    1187          18 :                 if (record == nullptr)
    1188           0 :                     break;
    1189             :             }
    1190             :         }
    1191             : 
    1192             :         ADRGDataset *poDS =
    1193           8 :             OpenDataset(osGENFileName.c_str(), osIMGFileName.c_str(), record);
    1194             : 
    1195           8 :         if (poDS)
    1196             :         {
    1197             :             /* -------------------------------------------------------------- */
    1198             :             /*      Initialize any PAM information.                           */
    1199             :             /* -------------------------------------------------------------- */
    1200           8 :             poDS->SetDescription(poOpenInfo->pszFilename);
    1201           8 :             poDS->TryLoadXML();
    1202             : 
    1203             :             /* -------------------------------------------------------------- */
    1204             :             /*      Check for external overviews.                             */
    1205             :             /* -------------------------------------------------------------- */
    1206           8 :             if (bFromSubdataset)
    1207           2 :                 poDS->oOvManager.Initialize(poDS, osIMGFileName.c_str());
    1208             :             else
    1209           6 :                 poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
    1210             : 
    1211           8 :             return poDS;
    1212             :         }
    1213             :     }
    1214             : 
    1215        2174 :     return nullptr;
    1216             : }
    1217             : 
    1218             : /************************************************************************/
    1219             : /*                         GDALRegister_ADRG()                          */
    1220             : /************************************************************************/
    1221             : 
    1222        2038 : void GDALRegister_ADRG()
    1223             : 
    1224             : {
    1225        2038 :     if (GDALGetDriverByName("ADRG") != nullptr)
    1226         283 :         return;
    1227             : 
    1228        1755 :     GDALDriver *poDriver = new GDALDriver();
    1229             : 
    1230        1755 :     poDriver->SetDescription("ADRG");
    1231        1755 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1232        1755 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
    1233        1755 :                               "ARC Digitized Raster Graphics");
    1234        1755 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/adrg.html");
    1235        1755 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "gen");
    1236        1755 :     poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
    1237        1755 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1238             : 
    1239        1755 :     poDriver->pfnOpen = ADRGDataset::Open;
    1240             : 
    1241        1755 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1242             : }

Generated by: LCOV version 1.14