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

Generated by: LCOV version 1.14