LCOV - code coverage report
Current view: top level - frmts/adrg - adrgdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1104 1248 88.5 %
Date: 2024-11-21 22:18:42 Functions: 40 41 97.6 %

          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             :     ADRGDataset *poOverviewDS;
      46             : 
      47             :     /* For creation */
      48             :     int bCreation;
      49             :     VSILFILE *fdGEN;
      50             :     VSILFILE *fdTHF;
      51             :     int bGeoTransformValid;
      52             :     double adfGeoTransform[6];
      53             :     int nNextAvailableBlock;
      54             :     CPLString osBaseFileName;
      55             : 
      56             :     static char **GetGENListFromTHF(const char *pszFileName);
      57             :     static char **GetIMGListFromGEN(const char *pszFileName,
      58             :                                     int *pnRecordIndex = nullptr);
      59             :     static ADRGDataset *OpenDataset(const char *pszGENFileName,
      60             :                                     const char *pszIMGFileName,
      61             :                                     DDFRecord *record = nullptr);
      62             :     static DDFRecord *FindRecordInGENForIMG(DDFModule &module,
      63             :                                             const char *pszGENFileName,
      64             :                                             const char *pszIMGFileName);
      65             : 
      66             :   public:
      67             :     ADRGDataset();
      68             :     ~ADRGDataset() override;
      69             : 
      70             :     const OGRSpatialReference *GetSpatialRef() const override;
      71             :     CPLErr GetGeoTransform(double *padfGeoTransform) override;
      72             :     CPLErr SetGeoTransform(double *padfGeoTransform) override;
      73             : 
      74             :     char **GetMetadataDomainList() override;
      75             :     char **GetMetadata(const char *pszDomain = "") override;
      76             : 
      77             :     char **GetFileList() override;
      78             : 
      79             :     void AddSubDataset(const char *pszGENFileName, const char *pszIMGFileName);
      80             : 
      81             :     static GDALDataset *Open(GDALOpenInfo *);
      82             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
      83             :                                int nBandsIn, GDALDataType eType,
      84             :                                char **papszOptions);
      85             : 
      86             :     static double GetLongitudeFromString(const char *str);
      87             :     static double GetLatitudeFromString(const char *str);
      88             : 
      89             :     void WriteGENFile();
      90             :     void WriteTHFFile();
      91             : };
      92             : 
      93             : /************************************************************************/
      94             : /* ==================================================================== */
      95             : /*                            ADRGRasterBand                             */
      96             : /* ==================================================================== */
      97             : /************************************************************************/
      98             : 
      99             : class ADRGRasterBand final : public GDALPamRasterBand
     100             : {
     101             :     friend class ADRGDataset;
     102             : 
     103             :   public:
     104             :     ADRGRasterBand(ADRGDataset *, int);
     105             : 
     106             :     GDALColorInterp GetColorInterpretation() override;
     107             :     CPLErr IReadBlock(int, int, void *) override;
     108             :     CPLErr IWriteBlock(int, int, void *) override;
     109             : 
     110             :     double GetNoDataValue(int *pbSuccess = nullptr) override;
     111             : 
     112             :     // virtual int GetOverviewCount();
     113             :     // virtual GDALRasterBand* GetOverview(int i);
     114             : };
     115             : 
     116             : /************************************************************************/
     117             : /*                           ADRGRasterBand()                            */
     118             : /************************************************************************/
     119             : 
     120          60 : ADRGRasterBand::ADRGRasterBand(ADRGDataset *poDSIn, int nBandIn)
     121             : 
     122             : {
     123          60 :     poDS = poDSIn;
     124          60 :     nBand = nBandIn;
     125             : 
     126          60 :     eDataType = GDT_Byte;
     127             : 
     128          60 :     nBlockXSize = 128;
     129          60 :     nBlockYSize = 128;
     130          60 : }
     131             : 
     132             : #if 0
     133             : 
     134             : /* We have a problem with the overview. Its geo bounding box doesn't match */
     135             : /* exactly the one of the main image. We should handle the shift between */
     136             : /* the two top level corners... */
     137             : 
     138             : /************************************************************************/
     139             : /*                          GetOverviewCount()                          */
     140             : /************************************************************************/
     141             : 
     142             : int ADRGRasterBand::GetOverviewCount()
     143             : 
     144             : {
     145             :     ADRGDataset* poDS = (ADRGDataset*)this->poDS;
     146             :     if( poDS->poOverviewDS )
     147             :         return 1;
     148             : 
     149             :     return GDALRasterBand::GetOverviewCount();
     150             : }
     151             : 
     152             : /************************************************************************/
     153             : /*                            GetOverview()                             */
     154             : /************************************************************************/
     155             : 
     156             : GDALRasterBand *ADRGRasterBand::GetOverview( int i )
     157             : 
     158             : {
     159             :     ADRGDataset* poDS = (ADRGDataset*)this->poDS;
     160             :     if( poDS->poOverviewDS )
     161             :     {
     162             :         if( i < 0 || i >= 1 )
     163             :             return NULL;
     164             : 
     165             :         return poDS->poOverviewDS->GetRasterBand(nBand);
     166             :     }
     167             : 
     168             :     return GDALRasterBand::GetOverview( i );
     169             : }
     170             : #endif
     171             : 
     172             : /************************************************************************/
     173             : /*                            GetNoDataValue()                          */
     174             : /************************************************************************/
     175             : 
     176          27 : double ADRGRasterBand::GetNoDataValue(int *pbSuccess)
     177             : {
     178          27 :     if (pbSuccess)
     179          18 :         *pbSuccess = TRUE;
     180             : 
     181          27 :     return 0.0;
     182             : }
     183             : 
     184             : /************************************************************************/
     185             : /*                       GetColorInterpretation()                       */
     186             : /************************************************************************/
     187             : 
     188          27 : GDALColorInterp ADRGRasterBand::GetColorInterpretation()
     189             : 
     190             : {
     191          27 :     if (nBand == 1)
     192           9 :         return GCI_RedBand;
     193             : 
     194          18 :     else if (nBand == 2)
     195           9 :         return GCI_GreenBand;
     196             : 
     197           9 :     return GCI_BlueBand;
     198             : }
     199             : 
     200             : /************************************************************************/
     201             : /*                             IReadBlock()                             */
     202             : /************************************************************************/
     203             : 
     204          14 : CPLErr ADRGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     205             : 
     206             : {
     207          14 :     ADRGDataset *l_poDS = (ADRGDataset *)this->poDS;
     208          14 :     int nBlock = nBlockYOff * l_poDS->NFC + nBlockXOff;
     209          14 :     if (nBlockXOff >= l_poDS->NFC || nBlockYOff >= l_poDS->NFL)
     210             :     {
     211           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     212             :                  "nBlockXOff=%d, NFC=%d, nBlockYOff=%d, NFL=%d", nBlockXOff,
     213             :                  l_poDS->NFC, nBlockYOff, l_poDS->NFL);
     214           0 :         return CE_Failure;
     215             :     }
     216          14 :     CPLDebug("ADRG", "(%d,%d) -> nBlock = %d", nBlockXOff, nBlockYOff, nBlock);
     217             : 
     218             :     vsi_l_offset offset;
     219          14 :     if (l_poDS->TILEINDEX)
     220             :     {
     221          14 :         if (l_poDS->TILEINDEX[nBlock] <= 0)
     222             :         {
     223           0 :             memset(pImage, 0, 128 * 128);
     224           0 :             return CE_None;
     225             :         }
     226          14 :         offset = l_poDS->offsetInIMG +
     227          14 :                  static_cast<vsi_l_offset>(l_poDS->TILEINDEX[nBlock] - 1) *
     228          14 :                      128 * 128 * 3 +
     229          14 :                  (nBand - 1) * 128 * 128;
     230             :     }
     231             :     else
     232           0 :         offset = l_poDS->offsetInIMG +
     233           0 :                  static_cast<vsi_l_offset>(nBlock) * 128 * 128 * 3 +
     234           0 :                  (nBand - 1) * 128 * 128;
     235             : 
     236          14 :     if (VSIFSeekL(l_poDS->fdIMG, offset, SEEK_SET) != 0)
     237             :     {
     238           0 :         CPLError(CE_Failure, CPLE_FileIO,
     239             :                  "Cannot seek to offset " CPL_FRMT_GUIB, offset);
     240           0 :         return CE_Failure;
     241             :     }
     242          14 :     if (VSIFReadL(pImage, 1, 128 * 128, l_poDS->fdIMG) != 128 * 128)
     243             :     {
     244           0 :         CPLError(CE_Failure, CPLE_FileIO,
     245             :                  "Cannot read data at offset " CPL_FRMT_GUIB, offset);
     246           0 :         return CE_Failure;
     247             :     }
     248             : 
     249          14 :     return CE_None;
     250             : }
     251             : 
     252             : /************************************************************************/
     253             : /*                            IWriteBlock()                             */
     254             : /************************************************************************/
     255             : 
     256          12 : CPLErr ADRGRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     257             : 
     258             : {
     259          12 :     ADRGDataset *l_poDS = (ADRGDataset *)this->poDS;
     260          12 :     int nBlock = nBlockYOff * l_poDS->NFC + nBlockXOff;
     261          12 :     if (l_poDS->eAccess != GA_Update)
     262             :     {
     263           0 :         return CE_Failure;
     264             :     }
     265          12 :     if (nBlockXOff >= l_poDS->NFC || nBlockYOff >= l_poDS->NFL)
     266             :     {
     267           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     268             :                  "nBlockXOff=%d, NFC=%d, nBlockYOff=%d, NFL=%d", nBlockXOff,
     269             :                  l_poDS->NFC, nBlockYOff, l_poDS->NFL);
     270           0 :         return CE_Failure;
     271             :     }
     272          12 :     CPLDebug("ADRG", "(%d,%d) -> nBlock = %d", nBlockXOff, nBlockYOff, nBlock);
     273             : 
     274          12 :     if (l_poDS->TILEINDEX[nBlock] == 0)
     275             :     {
     276             :         unsigned int i;
     277           4 :         int *pi = (int *)pImage;
     278           4 :         for (i = 0; i < 128 * 128 / sizeof(int); i++)
     279             :         {
     280           4 :             if (pi[i])
     281           4 :                 break;
     282             :         }
     283           4 :         if (i == 128 * 128 / sizeof(int))
     284             :         {
     285           0 :             return CE_None;
     286             :         }
     287             : 
     288           4 :         l_poDS->TILEINDEX[nBlock] = l_poDS->nNextAvailableBlock++;
     289             :     }
     290             : 
     291          12 :     const int offset = l_poDS->offsetInIMG +
     292          12 :                        (l_poDS->TILEINDEX[nBlock] - 1) * 128 * 128 * 3 +
     293          12 :                        (nBand - 1) * 128 * 128;
     294             : 
     295          12 :     if (VSIFSeekL(l_poDS->fdIMG, offset, SEEK_SET) != 0)
     296             :     {
     297           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot seek to offset %d", offset);
     298           0 :         return CE_Failure;
     299             :     }
     300          12 :     if (VSIFWriteL(pImage, 1, 128 * 128, l_poDS->fdIMG) != 128 * 128)
     301             :     {
     302           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot read data at offset %d",
     303             :                  offset);
     304           0 :         return CE_Failure;
     305             :     }
     306             : 
     307          12 :     return CE_None;
     308             : }
     309             : 
     310         330 : static unsigned int WriteSubFieldStr(VSILFILE *fd, const char *pszStr,
     311             :                                      unsigned int size)
     312             : {
     313         330 :     char *str = (char *)CPLMalloc(size + 1);
     314         330 :     memset(str, ' ', size);
     315         330 :     str[size] = 0;
     316         330 :     const size_t nStrLen = strlen(pszStr);
     317         330 :     if (nStrLen > size)
     318             :     {
     319           0 :         CPLError(CE_Failure, CPLE_AppDefined, "strlen(pszStr) > size");
     320           0 :         CPLFree(str);
     321           0 :         return size;
     322             :     }
     323         330 :     memcpy(str, pszStr, nStrLen);
     324         330 :     VSIFWriteL(str, 1, size, fd);
     325         330 :     CPLFree(str);
     326         330 :     return size;
     327             : }
     328             : 
     329        1015 : static unsigned int WriteSubFieldInt(VSILFILE *fd, int val, unsigned int size)
     330             : {
     331        1015 :     char *str = (char *)CPLMalloc(size + 1);
     332             :     char formatStr[32];
     333        1015 :     snprintf(formatStr, sizeof(formatStr), "%%0%ud", size);
     334        1015 :     snprintf(str, size + 1, formatStr, val);
     335        1015 :     VSIFWriteL(str, 1, size, fd);
     336        1015 :     CPLFree(str);
     337        1015 :     return size;
     338             : }
     339             : 
     340         348 : static unsigned int WriteFieldTerminator(VSILFILE *fd)
     341             : {
     342         348 :     char fieldTerminator = 30;
     343         348 :     VSIFWriteL(&fieldTerminator, 1, 1, fd);
     344         348 :     return 1;
     345             : }
     346             : 
     347         210 : static unsigned int WriteUnitTerminator(VSILFILE *fd)
     348             : {
     349         210 :     char fieldTerminator = 31;
     350         210 :     VSIFWriteL(&fieldTerminator, 1, 1, fd);
     351         210 :     return 1;
     352             : }
     353             : 
     354          45 : static unsigned int WriteLongitude(VSILFILE *fd, double val)
     355             : {
     356             :     char str[11 + 1];
     357          45 :     const char sign = (val >= 0) ? '+' : '-';
     358          45 :     if (val < 0)
     359          18 :         val = -val;
     360          45 :     const int ddd = (int)val;
     361          45 :     const int mm = (int)((val - ddd) * 60);
     362          45 :     const double ssdotss = ((val - ddd) * 60 - mm) * 60;
     363          45 :     snprintf(str, sizeof(str), "%c%03d%02d%05.2f", sign, ddd, mm, ssdotss);
     364          45 :     CPLAssert((int)strlen(str) == 11);
     365          45 :     VSIFWriteL(str, 1, 11, fd);
     366          45 :     return 11;
     367             : }
     368             : 
     369          45 : static unsigned int WriteLatitude(VSILFILE *fd, double val)
     370             : {
     371             :     char str[10 + 1];
     372          45 :     const char sign = (val >= 0) ? '+' : '-';
     373          45 :     if (val < 0)
     374           0 :         val = -val;
     375          45 :     const int dd = (int)val;
     376          45 :     const int mm = (int)((val - dd) * 60);
     377          45 :     const double ssdotss = ((val - dd) * 60 - mm) * 60;
     378          45 :     snprintf(str, sizeof(str), "%c%02d%02d%05.2f", sign, dd, mm, ssdotss);
     379          45 :     CPLAssert((int)strlen(str) == 10);
     380          45 :     VSIFWriteL(str, 1, 10, fd);
     381          45 :     return 10;
     382             : }
     383             : 
     384          41 : static int BeginLeader(VSILFILE *fd, int sizeFieldLength, int sizeFieldPos,
     385             :                        int sizeFieldTag, int nFields)
     386             : {
     387          41 :     int pos = (int)VSIFTellL(fd);
     388          41 :     VSIFSeekL(fd,
     389             :               24 +
     390          41 :                   (sizeFieldLength + sizeFieldPos + sizeFieldTag) *
     391          41 :                       (vsi_l_offset)nFields +
     392             :                   1,
     393             :               SEEK_CUR);
     394          41 :     return pos;
     395             : }
     396             : 
     397          41 : static void FinishWriteLeader(VSILFILE *fd, int beginPos, int sizeFieldLength,
     398             :                               int sizeFieldPos, int sizeFieldTag, int nFields,
     399             :                               int *sizeOfFields, const char **nameOfFields)
     400             : {
     401          41 :     const int endPos = (int)VSIFTellL(fd);
     402          41 :     VSIFSeekL(fd, beginPos, SEEK_SET);
     403             : 
     404          41 :     int nLeaderSize = 24;
     405             :     char szLeader[24 + 1];
     406          41 :     memset(szLeader, ' ', nLeaderSize);
     407             : 
     408          41 :     int nDataSize = 0;
     409          41 :     int nFieldOffset = 0;
     410         213 :     for (int i = 0; i < nFields; i++)
     411         172 :         nDataSize += sizeOfFields[i];
     412          41 :     nFieldOffset =
     413          41 :         (sizeFieldLength + sizeFieldPos + sizeFieldTag) * nFields + 1;
     414          41 :     nDataSize += nFieldOffset;
     415             : 
     416          41 :     snprintf(szLeader + 0, sizeof(szLeader) - 0, "%05d",
     417             :              (int)(nDataSize + nLeaderSize));
     418          41 :     szLeader[5] = ' ';
     419          41 :     szLeader[6] = 'D';
     420             : 
     421          41 :     snprintf(szLeader + 12, sizeof(szLeader) - 12, "%05d",
     422             :              (int)(nFieldOffset + nLeaderSize));
     423          41 :     szLeader[17] = ' ';
     424             : 
     425          41 :     szLeader[20] = (char)('0' + sizeFieldLength);
     426          41 :     szLeader[21] = (char)('0' + sizeFieldPos);
     427          41 :     szLeader[22] = '0';
     428          41 :     szLeader[23] = (char)('0' + sizeFieldTag);
     429             : 
     430          41 :     VSIFWriteL(szLeader, 1, nLeaderSize, fd);
     431             : 
     432          41 :     int acc = 0;
     433         213 :     for (int i = 0; i < nFields; i++)
     434             :     {
     435         172 :         VSIFWriteL(nameOfFields[i], 1, sizeFieldTag, fd);
     436         172 :         WriteSubFieldInt(fd, sizeOfFields[i], sizeFieldLength);
     437         172 :         WriteSubFieldInt(fd, acc, sizeFieldPos);
     438         172 :         acc += sizeOfFields[i];
     439             :     }
     440          41 :     WriteFieldTerminator(fd);
     441             : 
     442          41 :     VSIFSeekL(fd, endPos, SEEK_SET);
     443          41 : }
     444             : 
     445          15 : static int BeginHeader(VSILFILE *fd, int sizeFieldLength, int sizeFieldPos,
     446             :                        int sizeFieldTag, int nFields)
     447             : {
     448          15 :     int pos = (int)VSIFTellL(fd);
     449          15 :     VSIFSeekL(
     450          15 :         fd, 24 + (sizeFieldLength + sizeFieldPos + sizeFieldTag) * nFields + 1,
     451             :         SEEK_CUR);
     452          15 :     return pos;
     453             : }
     454             : 
     455          15 : static void FinishWriteHeader(VSILFILE *fd, int beginPos, int sizeFieldLength,
     456             :                               int sizeFieldPos, int sizeFieldTag, int nFields,
     457             :                               int *sizeOfFields, const char **nameOfFields)
     458             : {
     459          15 :     int endPos = (int)VSIFTellL(fd);
     460          15 :     VSIFSeekL(fd, beginPos, SEEK_SET);
     461             : 
     462          15 :     int nLeaderSize = 24;
     463             :     char szLeader[24 + 1];
     464          15 :     memset(szLeader, ' ', nLeaderSize);
     465             : 
     466          15 :     int nDataSize = 0;
     467          15 :     int nFieldOffset = 0;
     468         135 :     for (int i = 0; i < nFields; i++)
     469         120 :         nDataSize += sizeOfFields[i];
     470          15 :     nFieldOffset =
     471          15 :         (sizeFieldLength + sizeFieldPos + sizeFieldTag) * nFields + 1;
     472          15 :     nDataSize += nFieldOffset;
     473             : 
     474          15 :     snprintf(szLeader + 0, sizeof(szLeader) - 0, "%05d",
     475             :              (int)(nDataSize + nLeaderSize));
     476          15 :     szLeader[5] = '2';
     477          15 :     szLeader[6] = 'L';
     478             : 
     479          15 :     szLeader[10] = '0';
     480          15 :     szLeader[11] = '6';
     481          15 :     snprintf(szLeader + 12, sizeof(szLeader) - 12, "%05d",
     482             :              (int)(nFieldOffset + nLeaderSize));
     483          15 :     szLeader[17] = ' ';
     484             : 
     485          15 :     szLeader[20] = (char)('0' + sizeFieldLength);
     486          15 :     szLeader[21] = (char)('0' + sizeFieldPos);
     487          15 :     szLeader[22] = '0';
     488          15 :     szLeader[23] = (char)('0' + sizeFieldTag);
     489             : 
     490          15 :     VSIFWriteL(szLeader, 1, nLeaderSize, fd);
     491             : 
     492          15 :     int acc = 0;
     493         135 :     for (int i = 0; i < nFields; i++)
     494             :     {
     495         120 :         VSIFWriteL(nameOfFields[i], 1, sizeFieldTag, fd);
     496         120 :         WriteSubFieldInt(fd, sizeOfFields[i], sizeFieldLength);
     497         120 :         WriteSubFieldInt(fd, acc, sizeFieldPos);
     498         120 :         acc += sizeOfFields[i];
     499             :     }
     500          15 :     WriteFieldTerminator(fd);
     501             : 
     502          15 :     VSIFSeekL(fd, endPos, SEEK_SET);
     503          15 : }
     504             : 
     505         120 : static int WriteFieldDecl(VSILFILE *fd, char _data_struct_code,
     506             :                           char _data_type_code, const char *_fieldName,
     507             :                           const char *_arrayDescr, const char *_formatControls)
     508             : {
     509         120 :     VSIFWriteL(&_data_struct_code, 1, 1, fd);
     510         120 :     VSIFWriteL(&_data_type_code, 1, 1, fd);
     511         120 :     if (_data_struct_code == ' ')
     512             :     {
     513          15 :         VSIFWriteL("    ", 1, 4, fd);
     514             :     }
     515             :     else
     516             :     {
     517         105 :         VSIFWriteL("00;&", 1, 4, fd);
     518             :     }
     519         120 :     int len = 6;
     520         120 :     VSIFWriteL(_fieldName, 1, strlen(_fieldName), fd);
     521         120 :     len += static_cast<int>(strlen(_fieldName));
     522         120 :     if (_arrayDescr[0])
     523             :     {
     524         105 :         len += WriteUnitTerminator(fd);
     525         105 :         VSIFWriteL(_arrayDescr, 1, strlen(_arrayDescr), fd);
     526         105 :         len += static_cast<int>(strlen(_arrayDescr));
     527             : 
     528         105 :         len += WriteUnitTerminator(fd);
     529         105 :         VSIFWriteL(_formatControls, 1, strlen(_formatControls), fd);
     530         105 :         len += static_cast<int>(strlen(_formatControls));
     531             :     }
     532         120 :     len += WriteFieldTerminator(fd);
     533         120 :     return len;
     534             : }
     535             : 
     536             : /************************************************************************/
     537             : /*                          ADRGDataset()                               */
     538             : /************************************************************************/
     539             : 
     540          21 : ADRGDataset::ADRGDataset()
     541             :     : fdIMG(nullptr), TILEINDEX(nullptr), offsetInIMG(0), NFC(0), NFL(0),
     542             :       LSO(0.0), PSO(0.0), ARV(0), BRV(0), papszSubDatasets(nullptr),
     543             :       poOverviewDS(nullptr), bCreation(FALSE), fdGEN(nullptr), fdTHF(nullptr),
     544          21 :       bGeoTransformValid(0), nNextAvailableBlock(0)
     545             : {
     546          21 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     547          21 :     memset(adfGeoTransform, 0, sizeof(adfGeoTransform));
     548          21 : }
     549             : 
     550             : /************************************************************************/
     551             : /*                          ~ADRGDataset()                              */
     552             : /************************************************************************/
     553             : 
     554          42 : ADRGDataset::~ADRGDataset()
     555             : {
     556          21 :     if (poOverviewDS)
     557             :     {
     558           0 :         delete poOverviewDS;
     559             :     }
     560             : 
     561          21 :     CSLDestroy(papszSubDatasets);
     562             : 
     563          21 :     if (bCreation)
     564             :     {
     565           5 :         GDALPamDataset::FlushCache(true);
     566             : 
     567             :         /* Write header and padding of image */
     568           5 :         VSIFSeekL(fdIMG, 0, SEEK_SET);
     569             :         {
     570           5 :             VSILFILE *fd = fdIMG;
     571             :             {
     572           5 :                 int nFields = 0;
     573           5 :                 int sizeOfFields[] = {0, 0, 0, 0};
     574           5 :                 const char *nameOfFields[] = {"000", "001", "PAD", "SCN"};
     575           5 :                 int pos = BeginHeader(fd, 3, 4, 3, N_ELEMENTS(sizeOfFields));
     576             : 
     577           5 :                 sizeOfFields[nFields++] += WriteFieldDecl(
     578             :                     fd, ' ', ' ', "GEO_DATA_FILE", "", ""); /* 000 */
     579           5 :                 sizeOfFields[nFields++] +=
     580           5 :                     WriteFieldDecl(fd, '1', '0', "RECORD_ID_FIELD", /* 001 */
     581             :                                    "RTY!RID", "(A(3),A(2))");
     582           5 :                 sizeOfFields[nFields++] +=
     583           5 :                     WriteFieldDecl(fd, '1', '0', "PADDING_FIELD", /* PAD */
     584             :                                    "PAD", "(A)");
     585           5 :                 sizeOfFields[nFields++] +=
     586           5 :                     WriteFieldDecl(fd, '2', '0', "PIXEL_FIELD", /* SCN */
     587             :                                    "*PIX", "(A(1))");
     588             : 
     589           5 :                 FinishWriteHeader(fd, pos, 3, 4, 3, N_ELEMENTS(sizeOfFields),
     590             :                                   sizeOfFields, nameOfFields);
     591             :             }
     592             : 
     593             :             /* Write IMAGE_RECORD */
     594             :             {
     595           5 :                 int nFields = 0;
     596           5 :                 int sizeOfFields[] = {0, 0, 0};
     597           5 :                 const char *nameOfFields[] = {"001", "PAD", "SCN"};
     598           5 :                 int pos = BeginLeader(fd, 9, 9, 3, N_ELEMENTS(sizeOfFields));
     599             : 
     600             :                 /* Field 001 */
     601           5 :                 sizeOfFields[nFields] +=
     602           5 :                     WriteSubFieldStr(fd, "IMG", 3); /* RTY */
     603           5 :                 sizeOfFields[nFields] +=
     604           5 :                     WriteSubFieldStr(fd, "01", 2); /* RID */
     605           5 :                 sizeOfFields[nFields] += WriteFieldTerminator(fd);
     606           5 :                 nFields++;
     607             : 
     608             :                 /* Field PAD */
     609           5 :                 int endPos = (int)VSIFTellL(fd);
     610           5 :                 char *pad = (char *)CPLMalloc(2047 - endPos);
     611           5 :                 memset(pad, ' ', 2047 - endPos);
     612           5 :                 VSIFWriteL(pad, 1, 2047 - endPos, fd);
     613           5 :                 CPLFree(pad);
     614           5 :                 WriteFieldTerminator(fd);
     615           5 :                 sizeOfFields[nFields] += 2047 - endPos + 1;
     616           5 :                 nFields++;
     617             : 
     618             :                 /* Field SCN */
     619           5 :                 sizeOfFields[nFields] =
     620           5 :                     (nNextAvailableBlock - 1) * 128 * 128 * 3;
     621             :                 // nFields++;
     622             : 
     623           5 :                 FinishWriteLeader(fd, pos, 9, 9, 3, N_ELEMENTS(sizeOfFields),
     624             :                                   sizeOfFields, nameOfFields);
     625             :             }
     626             :         }
     627             : 
     628             :         /* Write terminal field terminator */
     629           5 :         int offset = offsetInIMG + (nNextAvailableBlock - 1) * 128 * 128 * 3;
     630           5 :         VSIFSeekL(fdIMG, offset, SEEK_SET);
     631           5 :         WriteFieldTerminator(fdIMG);
     632             : 
     633           5 :         WriteGENFile();
     634           5 :         WriteTHFFile();
     635             :     }
     636             : 
     637          21 :     if (fdIMG)
     638             :     {
     639          20 :         VSIFCloseL(fdIMG);
     640             :     }
     641             : 
     642          21 :     if (fdGEN)
     643             :     {
     644           5 :         VSIFCloseL(fdGEN);
     645             :     }
     646          21 :     if (fdTHF)
     647             :     {
     648           5 :         VSIFCloseL(fdTHF);
     649             :     }
     650             : 
     651          21 :     if (TILEINDEX)
     652             :     {
     653          20 :         delete[] TILEINDEX;
     654             :     }
     655          42 : }
     656             : 
     657             : /************************************************************************/
     658             : /*                            GetFileList()                             */
     659             : /************************************************************************/
     660             : 
     661           5 : char **ADRGDataset::GetFileList()
     662             : {
     663           5 :     char **papszFileList = GDALPamDataset::GetFileList();
     664             : 
     665           5 :     if (!osGENFileName.empty() && !osIMGFileName.empty())
     666             :     {
     667           5 :         CPLString osMainFilename = GetDescription();
     668             :         VSIStatBufL sStat;
     669             : 
     670           5 :         const bool bMainFileReal = VSIStatL(osMainFilename, &sStat) == 0;
     671           5 :         if (bMainFileReal)
     672             :         {
     673           8 :             CPLString osShortMainFilename = CPLGetFilename(osMainFilename);
     674           8 :             CPLString osShortGENFileName = CPLGetFilename(osGENFileName);
     675           4 :             if (!EQUAL(osShortMainFilename.c_str(), osShortGENFileName.c_str()))
     676             :                 papszFileList =
     677           1 :                     CSLAddString(papszFileList, osGENFileName.c_str());
     678             :         }
     679             :         else
     680           1 :             papszFileList = CSLAddString(papszFileList, osGENFileName.c_str());
     681             : 
     682           5 :         papszFileList = CSLAddString(papszFileList, osIMGFileName.c_str());
     683             :     }
     684             : 
     685           5 :     return papszFileList;
     686             : }
     687             : 
     688             : /************************************************************************/
     689             : /*                           AddSubDataset()                            */
     690             : /************************************************************************/
     691             : 
     692           2 : void ADRGDataset::AddSubDataset(const char *pszGENFileName,
     693             :                                 const char *pszIMGFileName)
     694             : {
     695             :     char szName[80];
     696           2 :     int nCount = CSLCount(papszSubDatasets) / 2;
     697             : 
     698           2 :     CPLString osSubDatasetName;
     699           2 :     osSubDatasetName = "ADRG:";
     700           2 :     osSubDatasetName += pszGENFileName;
     701           2 :     osSubDatasetName += ",";
     702           2 :     osSubDatasetName += pszIMGFileName;
     703             : 
     704           2 :     snprintf(szName, sizeof(szName), "SUBDATASET_%d_NAME", nCount + 1);
     705           2 :     papszSubDatasets =
     706           2 :         CSLSetNameValue(papszSubDatasets, szName, osSubDatasetName);
     707             : 
     708           2 :     snprintf(szName, sizeof(szName), "SUBDATASET_%d_DESC", nCount + 1);
     709           2 :     papszSubDatasets =
     710           2 :         CSLSetNameValue(papszSubDatasets, szName, osSubDatasetName);
     711           2 : }
     712             : 
     713             : /************************************************************************/
     714             : /*                      GetMetadataDomainList()                         */
     715             : /************************************************************************/
     716             : 
     717           0 : char **ADRGDataset::GetMetadataDomainList()
     718             : {
     719           0 :     return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
     720           0 :                                    TRUE, "SUBDATASETS", nullptr);
     721             : }
     722             : 
     723             : /************************************************************************/
     724             : /*                            GetMetadata()                             */
     725             : /************************************************************************/
     726             : 
     727          20 : char **ADRGDataset::GetMetadata(const char *pszDomain)
     728             : 
     729             : {
     730          20 :     if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
     731           0 :         return papszSubDatasets;
     732             : 
     733          20 :     return GDALPamDataset::GetMetadata(pszDomain);
     734             : }
     735             : 
     736             : /************************************************************************/
     737             : /*                        GetSpatialRef()                               */
     738             : /************************************************************************/
     739             : 
     740           5 : const OGRSpatialReference *ADRGDataset::GetSpatialRef() const
     741             : {
     742           5 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     743             : }
     744             : 
     745             : /************************************************************************/
     746             : /*                        GetGeoTransform()                             */
     747             : /************************************************************************/
     748             : 
     749           6 : CPLErr ADRGDataset::GetGeoTransform(double *padfGeoTransform)
     750             : {
     751           6 :     if (papszSubDatasets != nullptr)
     752           0 :         return CE_Failure;
     753             : 
     754           6 :     memcpy(padfGeoTransform, adfGeoTransform, sizeof(double) * 6);
     755             : 
     756           6 :     return CE_None;
     757             : }
     758             : 
     759             : /************************************************************************/
     760             : /*                          SetGeoTransform()                           */
     761             : /************************************************************************/
     762             : 
     763           5 : CPLErr ADRGDataset::SetGeoTransform(double *padfGeoTransform)
     764             : 
     765             : {
     766           5 :     memcpy(adfGeoTransform, padfGeoTransform, sizeof(double) * 6);
     767           5 :     bGeoTransformValid = TRUE;
     768           5 :     return CE_None;
     769             : }
     770             : 
     771             : /************************************************************************/
     772             : /*                     GetLongitudeFromString()                         */
     773             : /************************************************************************/
     774             : 
     775          15 : double ADRGDataset::GetLongitudeFromString(const char *str)
     776             : {
     777          15 :     char ddd[3 + 1] = {0};
     778          15 :     char mm[2 + 1] = {0};
     779          15 :     char ssdotss[5 + 1] = {0};
     780          15 :     int sign = (str[0] == '+') ? 1 : -1;
     781          15 :     str++;
     782          15 :     strncpy(ddd, str, 3);
     783          15 :     str += 3;
     784          15 :     strncpy(mm, str, 2);
     785          15 :     str += 2;
     786          15 :     strncpy(ssdotss, str, 5);
     787          15 :     return sign * (CPLAtof(ddd) + CPLAtof(mm) / 60 + CPLAtof(ssdotss) / 3600);
     788             : }
     789             : 
     790             : /************************************************************************/
     791             : /*                      GetLatitudeFromString()                         */
     792             : /************************************************************************/
     793             : 
     794          15 : double ADRGDataset::GetLatitudeFromString(const char *str)
     795             : {
     796          15 :     char ddd[2 + 1] = {0};
     797          15 :     char mm[2 + 1] = {0};
     798          15 :     char ssdotss[5 + 1] = {0};
     799          15 :     int sign = (str[0] == '+') ? 1 : -1;
     800          15 :     str++;
     801          15 :     strncpy(ddd, str, 2);
     802          15 :     str += 2;
     803          15 :     strncpy(mm, str, 2);
     804          15 :     str += 2;
     805          15 :     strncpy(ssdotss, str, 5);
     806          15 :     return sign * (CPLAtof(ddd) + CPLAtof(mm) / 60 + CPLAtof(ssdotss) / 3600);
     807             : }
     808             : 
     809             : /************************************************************************/
     810             : /*                      FindRecordInGENForIMG()                         */
     811             : /************************************************************************/
     812             : 
     813           2 : DDFRecord *ADRGDataset::FindRecordInGENForIMG(DDFModule &module,
     814             :                                               const char *pszGENFileName,
     815             :                                               const char *pszIMGFileName)
     816             : {
     817             :     /* Finds the GEN file corresponding to the IMG file */
     818           2 :     if (!module.Open(pszGENFileName, TRUE))
     819           0 :         return nullptr;
     820             : 
     821           4 :     CPLString osShortIMGFilename = CPLGetFilename(pszIMGFileName);
     822             : 
     823             :     /* Now finds the record */
     824             :     while (true)
     825             :     {
     826           7 :         CPLPushErrorHandler(CPLQuietErrorHandler);
     827           7 :         DDFRecord *record = module.ReadRecord();
     828           7 :         CPLPopErrorHandler();
     829           7 :         CPLErrorReset();
     830           7 :         if (record == nullptr)
     831           0 :             return nullptr;
     832             : 
     833           7 :         if (record->GetFieldCount() >= 5)
     834             :         {
     835           5 :             DDFField *field = record->GetField(0);
     836           5 :             DDFFieldDefn *fieldDefn = field->GetFieldDefn();
     837          10 :             if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
     838           5 :                   fieldDefn->GetSubfieldCount() == 2))
     839             :             {
     840           2 :                 continue;
     841             :             }
     842             : 
     843           5 :             const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
     844           5 :             if (RTY == nullptr)
     845           0 :                 continue;
     846             :             /* Ignore overviews */
     847           5 :             if (strcmp(RTY, "OVV") == 0)
     848           2 :                 continue;
     849             : 
     850           3 :             if (strcmp(RTY, "GIN") != 0)
     851           0 :                 continue;
     852             : 
     853           3 :             field = record->GetField(3);
     854           3 :             fieldDefn = field->GetFieldDefn();
     855             : 
     856           6 :             if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
     857           3 :                   fieldDefn->GetSubfieldCount() == 15))
     858             :             {
     859           0 :                 continue;
     860             :             }
     861             : 
     862           3 :             const char *pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
     863           3 :             if (pszBAD == nullptr || strlen(pszBAD) != 12)
     864           0 :                 continue;
     865           3 :             CPLString osBAD = pszBAD;
     866             :             {
     867           3 :                 char *c = (char *)strchr(osBAD.c_str(), ' ');
     868           3 :                 if (c)
     869           0 :                     *c = 0;
     870             :             }
     871             : 
     872           3 :             if (EQUAL(osShortIMGFilename.c_str(), osBAD.c_str()))
     873             :             {
     874           2 :                 return record;
     875             :             }
     876             :         }
     877           5 :     }
     878             : }
     879             : 
     880             : /************************************************************************/
     881             : /*                           OpenDataset()                              */
     882             : /************************************************************************/
     883             : 
     884          15 : ADRGDataset *ADRGDataset::OpenDataset(const char *pszGENFileName,
     885             :                                       const char *pszIMGFileName,
     886             :                                       DDFRecord *record)
     887             : {
     888          30 :     DDFModule module;
     889             : 
     890          15 :     int SCA = 0;
     891          15 :     int ZNA = 0;
     892             :     double PSP;
     893             :     int ARV;
     894             :     int BRV;
     895             :     double LSO;
     896             :     double PSO;
     897             :     int NFL;
     898             :     int NFC;
     899          30 :     CPLString osBAD;
     900             :     int TIF;
     901          15 :     int *TILEINDEX = nullptr;
     902             : 
     903          15 :     if (record == nullptr)
     904             :     {
     905           2 :         record = FindRecordInGENForIMG(module, pszGENFileName, pszIMGFileName);
     906           2 :         if (record == nullptr)
     907           0 :             return nullptr;
     908             :     }
     909             : 
     910          15 :     DDFField *field = record->GetField(1);
     911          15 :     if (field == nullptr)
     912           0 :         return nullptr;
     913          15 :     DDFFieldDefn *fieldDefn = field->GetFieldDefn();
     914             : 
     915          30 :     if (!(strcmp(fieldDefn->GetName(), "DSI") == 0 &&
     916          15 :           fieldDefn->GetSubfieldCount() == 2))
     917             :     {
     918           0 :         return nullptr;
     919             :     }
     920             : 
     921          15 :     const char *pszPTR = record->GetStringSubfield("DSI", 0, "PRT", 0);
     922          15 :     if (pszPTR == nullptr || !EQUAL(pszPTR, "ADRG"))
     923           0 :         return nullptr;
     924             : 
     925          15 :     const char *pszNAM = record->GetStringSubfield("DSI", 0, "NAM", 0);
     926          15 :     if (pszNAM == nullptr || strlen(pszNAM) != 8)
     927           0 :         return nullptr;
     928          30 :     CPLString osNAM = pszNAM;
     929             : 
     930          15 :     field = record->GetField(2);
     931          15 :     if (field == nullptr)
     932           0 :         return nullptr;
     933          15 :     fieldDefn = field->GetFieldDefn();
     934             : 
     935             :     // TODO: Support on GIN things.  And what is GIN?
     936             :     // GIN might mean general information and might be a typo of GEN.
     937             :     // if( isGIN )
     938             :     {
     939          30 :         if (!(strcmp(fieldDefn->GetName(), "GEN") == 0 &&
     940          15 :               fieldDefn->GetSubfieldCount() == 21))
     941             :         {
     942           0 :             return nullptr;
     943             :         }
     944             : 
     945          15 :         if (record->GetIntSubfield("GEN", 0, "STR", 0) != 3)
     946           0 :             return nullptr;
     947             : 
     948          15 :         SCA = record->GetIntSubfield("GEN", 0, "SCA", 0);
     949          15 :         CPLDebug("ADRG", "SCA=%d", SCA);
     950             : 
     951          15 :         ZNA = record->GetIntSubfield("GEN", 0, "ZNA", 0);
     952          15 :         CPLDebug("ADRG", "ZNA=%d", ZNA);
     953             : 
     954          15 :         PSP = record->GetFloatSubfield("GEN", 0, "PSP", 0);
     955          15 :         CPLDebug("ADRG", "PSP=%f", PSP);
     956             : 
     957          15 :         ARV = record->GetIntSubfield("GEN", 0, "ARV", 0);
     958          15 :         CPLDebug("ADRG", "ARV=%d", ARV);
     959             : 
     960          15 :         BRV = record->GetIntSubfield("GEN", 0, "BRV", 0);
     961          15 :         CPLDebug("ADRG", "BRV=%d", BRV);
     962          15 :         if (ARV <= 0 || (ZNA != 9 && ZNA != 18 && BRV <= 0))
     963           0 :             return nullptr;
     964             : 
     965          15 :         const char *pszLSO = record->GetStringSubfield("GEN", 0, "LSO", 0);
     966          15 :         if (pszLSO == nullptr || strlen(pszLSO) != 11)
     967           0 :             return nullptr;
     968          15 :         LSO = GetLongitudeFromString(pszLSO);
     969          15 :         CPLDebug("ADRG", "LSO=%f", LSO);
     970             : 
     971          15 :         const char *pszPSO = record->GetStringSubfield("GEN", 0, "PSO", 0);
     972          15 :         if (pszPSO == nullptr || strlen(pszPSO) != 10)
     973           0 :             return nullptr;
     974          15 :         PSO = GetLatitudeFromString(pszPSO);
     975          15 :         CPLDebug("ADRG", "PSO=%f", PSO);
     976             :     }
     977             : #if 0
     978             :     else
     979             :     {
     980             :         if( !(strcmp(fieldDefn->GetName(), "OVI") == 0 &&
     981             :                 fieldDefn->GetSubfieldCount() == 5) )
     982             :         {
     983             :             return NULL;
     984             :         }
     985             : 
     986             :         if( record->GetIntSubfield("OVI", 0, "STR", 0) != 3 )
     987             :             return NULL;
     988             : 
     989             :         ARV = record->GetIntSubfield("OVI", 0, "ARV", 0);
     990             :         CPLDebug("ADRG", "ARV=%d", ARV);
     991             : 
     992             :         BRV = record->GetIntSubfield("OVI", 0, "BRV", 0);
     993             :         CPLDebug("ADRG", "BRV=%d", BRV);
     994             : 
     995             :         const char* pszLSO = record->GetStringSubfield("OVI", 0, "LSO", 0);
     996             :         if( pszLSO == NULL || strlen(pszLSO) != 11 )
     997             :             return NULL;
     998             :         LSO = GetLongitudeFromString(pszLSO);
     999             :         CPLDebug("ADRG", "LSO=%f", LSO);
    1000             : 
    1001             :         const char* pszPSO = record->GetStringSubfield("OVI", 0, "PSO", 0);
    1002             :         if( pszPSO == NULL || strlen(pszPSO) != 10 )
    1003             :             return NULL;
    1004             :         PSO = GetLatitudeFromString(pszPSO);
    1005             :         CPLDebug("ADRG", "PSO=%f", PSO);
    1006             :     }
    1007             : #endif
    1008             : 
    1009          15 :     field = record->GetField(3);
    1010          15 :     if (field == nullptr)
    1011           0 :         return nullptr;
    1012          15 :     fieldDefn = field->GetFieldDefn();
    1013             : 
    1014          30 :     if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
    1015          15 :           fieldDefn->GetSubfieldCount() == 15))
    1016             :     {
    1017           0 :         return nullptr;
    1018             :     }
    1019             : 
    1020          15 :     NFL = record->GetIntSubfield("SPR", 0, "NFL", 0);
    1021          15 :     CPLDebug("ADRG", "NFL=%d", NFL);
    1022             : 
    1023          15 :     NFC = record->GetIntSubfield("SPR", 0, "NFC", 0);
    1024          15 :     CPLDebug("ADRG", "NFC=%d", NFC);
    1025             : 
    1026          15 :     const auto knIntMax = std::numeric_limits<int>::max();
    1027          15 :     if (NFL <= 0 || NFC <= 0 || NFL > knIntMax / 128 || NFC > knIntMax / 128 ||
    1028          15 :         NFL > (knIntMax - 1) / (NFC * 5))
    1029             :     {
    1030           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid NFL / NFC values");
    1031           0 :         return nullptr;
    1032             :     }
    1033             : 
    1034          15 :     int PNC = record->GetIntSubfield("SPR", 0, "PNC", 0);
    1035          15 :     CPLDebug("ADRG", "PNC=%d", PNC);
    1036          15 :     if (PNC != 128)
    1037             :     {
    1038           0 :         return nullptr;
    1039             :     }
    1040             : 
    1041          15 :     int PNL = record->GetIntSubfield("SPR", 0, "PNL", 0);
    1042          15 :     CPLDebug("ADRG", "PNL=%d", PNL);
    1043          15 :     if (PNL != 128)
    1044             :     {
    1045           0 :         return nullptr;
    1046             :     }
    1047             : 
    1048          15 :     const char *pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
    1049          15 :     if (pszBAD == nullptr || strlen(pszBAD) != 12)
    1050           0 :         return nullptr;
    1051          15 :     osBAD = pszBAD;
    1052             :     {
    1053          15 :         char *c = (char *)strchr(osBAD.c_str(), ' ');
    1054          15 :         if (c)
    1055           0 :             *c = 0;
    1056             :     }
    1057          15 :     CPLDebug("ADRG", "BAD=%s", osBAD.c_str());
    1058             : 
    1059          15 :     DDFSubfieldDefn *subfieldDefn = fieldDefn->GetSubfield(14);
    1060          30 :     if (!(strcmp(subfieldDefn->GetName(), "TIF") == 0 &&
    1061          15 :           (subfieldDefn->GetFormat())[0] == 'A'))
    1062             :     {
    1063           0 :         return nullptr;
    1064             :     }
    1065             : 
    1066          15 :     const char *pszTIF = record->GetStringSubfield("SPR", 0, "TIF", 0);
    1067          15 :     if (pszTIF == nullptr)
    1068           0 :         return nullptr;
    1069          15 :     TIF = pszTIF[0] == 'Y';
    1070          15 :     CPLDebug("ADRG", "TIF=%d", TIF);
    1071             : 
    1072          15 :     if (TIF)
    1073             :     {
    1074          15 :         if (record->GetFieldCount() != 6)
    1075             :         {
    1076           0 :             return nullptr;
    1077             :         }
    1078             : 
    1079          15 :         field = record->GetField(5);
    1080          15 :         if (field == nullptr)
    1081           0 :             return nullptr;
    1082          15 :         fieldDefn = field->GetFieldDefn();
    1083             : 
    1084          15 :         if (!(strcmp(fieldDefn->GetName(), "TIM") == 0))
    1085             :         {
    1086           0 :             return nullptr;
    1087             :         }
    1088             : 
    1089          15 :         if (field->GetDataSize() != 5 * NFL * NFC + 1)
    1090             :         {
    1091           0 :             return nullptr;
    1092             :         }
    1093             : 
    1094             :         try
    1095             :         {
    1096          15 :             TILEINDEX = new int[NFL * NFC];
    1097             :         }
    1098           0 :         catch (const std::exception &)
    1099             :         {
    1100           0 :             return nullptr;
    1101             :         }
    1102          15 :         const char *ptr = field->GetData();
    1103          15 :         char offset[5 + 1] = {0};
    1104          30 :         for (int i = 0; i < NFL * NFC; i++)
    1105             :         {
    1106          15 :             strncpy(offset, ptr, 5);
    1107          15 :             ptr += 5;
    1108          15 :             TILEINDEX[i] = atoi(offset);
    1109             :             // CPLDebug("ADRG", "TSI[%d]=%d", i, TILEINDEX[i]);
    1110             :         }
    1111             :     }
    1112             : 
    1113          15 :     VSILFILE *fdIMG = VSIFOpenL(pszIMGFileName, "rb");
    1114          15 :     if (fdIMG == nullptr)
    1115             :     {
    1116           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s\n",
    1117             :                  pszIMGFileName);
    1118           0 :         delete[] TILEINDEX;
    1119           0 :         return nullptr;
    1120             :     }
    1121             : 
    1122             :     /* Skip ISO8211 header of IMG file */
    1123          15 :     int offsetInIMG = 0;
    1124             :     char c;
    1125             :     char recordName[3];
    1126          15 :     if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
    1127             :     {
    1128           0 :         VSIFCloseL(fdIMG);
    1129           0 :         delete[] TILEINDEX;
    1130           0 :         return nullptr;
    1131             :     }
    1132        3870 :     while (!VSIFEofL(fdIMG))
    1133             :     {
    1134        3870 :         if (c == 30)
    1135             :         {
    1136          90 :             if (VSIFReadL(recordName, 1, 3, fdIMG) != 3)
    1137             :             {
    1138           0 :                 VSIFCloseL(fdIMG);
    1139           0 :                 delete[] TILEINDEX;
    1140           0 :                 return nullptr;
    1141             :             }
    1142          90 :             offsetInIMG += 3;
    1143          90 :             if (STARTS_WITH(recordName, "IMG"))
    1144             :             {
    1145          15 :                 offsetInIMG += 4;
    1146          30 :                 if (VSIFSeekL(fdIMG, 3, SEEK_CUR) != 0 ||
    1147          15 :                     VSIFReadL(&c, 1, 1, fdIMG) != 1)
    1148             :                 {
    1149           0 :                     VSIFCloseL(fdIMG);
    1150           0 :                     delete[] TILEINDEX;
    1151           0 :                     return nullptr;
    1152             :                 }
    1153       26535 :                 while (c == ' ')
    1154             :                 {
    1155       26520 :                     offsetInIMG++;
    1156       26520 :                     if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
    1157             :                     {
    1158           0 :                         VSIFCloseL(fdIMG);
    1159           0 :                         delete[] TILEINDEX;
    1160           0 :                         return nullptr;
    1161             :                     }
    1162             :                 }
    1163          15 :                 offsetInIMG++;
    1164          15 :                 break;
    1165             :             }
    1166             :         }
    1167             : 
    1168        3855 :         offsetInIMG++;
    1169        3855 :         if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
    1170             :         {
    1171           0 :             VSIFCloseL(fdIMG);
    1172           0 :             delete[] TILEINDEX;
    1173           0 :             return nullptr;
    1174             :         }
    1175             :     }
    1176             : 
    1177          15 :     if (VSIFEofL(fdIMG))
    1178             :     {
    1179           0 :         VSIFCloseL(fdIMG);
    1180           0 :         delete[] TILEINDEX;
    1181           0 :         return nullptr;
    1182             :     }
    1183             : 
    1184          15 :     CPLDebug("ADRG", "Img offset data = %d", offsetInIMG);
    1185             : 
    1186          15 :     ADRGDataset *poDS = new ADRGDataset();
    1187             : 
    1188          15 :     poDS->osGENFileName = pszGENFileName;
    1189          15 :     poDS->osIMGFileName = pszIMGFileName;
    1190          15 :     poDS->NFC = NFC;
    1191          15 :     poDS->NFL = NFL;
    1192          15 :     poDS->nRasterXSize = NFC * 128;
    1193          15 :     poDS->nRasterYSize = NFL * 128;
    1194          15 :     poDS->LSO = LSO;
    1195          15 :     poDS->PSO = PSO;
    1196          15 :     poDS->ARV = ARV;
    1197          15 :     poDS->BRV = BRV;
    1198          15 :     poDS->TILEINDEX = TILEINDEX;
    1199          15 :     poDS->fdIMG = fdIMG;
    1200          15 :     poDS->offsetInIMG = offsetInIMG;
    1201          15 :     poDS->poOverviewDS = nullptr;
    1202             : 
    1203          15 :     if (ZNA == 9)
    1204             :     {
    1205             :         // North Polar Case
    1206           1 :         poDS->adfGeoTransform[0] =
    1207           1 :             111319.4907933 * (90.0 - PSO) * sin(LSO * M_PI / 180.0);
    1208           1 :         poDS->adfGeoTransform[1] = 40075016.68558 / ARV;
    1209           1 :         poDS->adfGeoTransform[2] = 0.0;
    1210           1 :         poDS->adfGeoTransform[3] =
    1211           1 :             -111319.4907933 * (90.0 - PSO) * cos(LSO * M_PI / 180.0);
    1212           1 :         poDS->adfGeoTransform[4] = 0.0;
    1213           1 :         poDS->adfGeoTransform[5] = -40075016.68558 / ARV;
    1214           1 :         poDS->m_oSRS.importFromWkt(
    1215             :             "PROJCS[\"ARC_System_Zone_09\",GEOGCS[\"GCS_Sphere\","
    1216             :             "DATUM[\"D_Sphere\",SPHEROID[\"Sphere\",6378137.0,0.0]],"
    1217             :             "PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],"
    1218             :             "PROJECTION[\"Azimuthal_Equidistant\"],"
    1219             :             "PARAMETER[\"latitude_of_center\",90],"
    1220             :             "PARAMETER[\"longitude_of_center\",0],"
    1221             :             "PARAMETER[\"false_easting\",0],"
    1222             :             "PARAMETER[\"false_northing\",0],"
    1223             :             "UNIT[\"metre\",1]]");
    1224             :     }
    1225          14 :     else if (ZNA == 18)
    1226             :     {
    1227             :         // South Polar Case
    1228           1 :         poDS->adfGeoTransform[0] =
    1229           1 :             111319.4907933 * (90.0 + PSO) * sin(LSO * M_PI / 180.0);
    1230           1 :         poDS->adfGeoTransform[1] = 40075016.68558 / ARV;
    1231           1 :         poDS->adfGeoTransform[2] = 0.0;
    1232           1 :         poDS->adfGeoTransform[3] =
    1233           1 :             111319.4907933 * (90.0 + PSO) * cos(LSO * M_PI / 180.0);
    1234           1 :         poDS->adfGeoTransform[4] = 0.0;
    1235           1 :         poDS->adfGeoTransform[5] = -40075016.68558 / ARV;
    1236           1 :         poDS->m_oSRS.importFromWkt(
    1237             :             "PROJCS[\"ARC_System_Zone_18\",GEOGCS[\"GCS_Sphere\","
    1238             :             "DATUM[\"D_Sphere\",SPHEROID[\"Sphere\",6378137.0,0.0]],"
    1239             :             "PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],"
    1240             :             "PROJECTION[\"Azimuthal_Equidistant\"],"
    1241             :             "PARAMETER[\"latitude_of_center\",-90],"
    1242             :             "PARAMETER[\"longitude_of_center\",0],"
    1243             :             "PARAMETER[\"false_easting\",0],"
    1244             :             "PARAMETER[\"false_northing\",0],"
    1245             :             "UNIT[\"metre\",1]]");
    1246             :     }
    1247             :     else
    1248             :     {
    1249          13 :         poDS->adfGeoTransform[0] = LSO;
    1250          13 :         poDS->adfGeoTransform[1] = 360. / ARV;
    1251          13 :         poDS->adfGeoTransform[2] = 0.0;
    1252          13 :         poDS->adfGeoTransform[3] = PSO;
    1253          13 :         poDS->adfGeoTransform[4] = 0.0;
    1254          13 :         poDS->adfGeoTransform[5] = -360. / BRV;
    1255          13 :         poDS->m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
    1256             :     }
    1257             : 
    1258             :     // if( isGIN )
    1259             :     {
    1260             :         char szValue[32];
    1261          15 :         snprintf(szValue, sizeof(szValue), "%d", SCA);
    1262          15 :         poDS->SetMetadataItem("ADRG_SCA", szValue);
    1263          15 :         snprintf(szValue, sizeof(szValue), "%d", ZNA);
    1264          15 :         poDS->SetMetadataItem("ADRG_ZNA", szValue);
    1265             :     }
    1266             : 
    1267          15 :     poDS->SetMetadataItem("ADRG_NAM", osNAM.c_str());
    1268             : 
    1269          15 :     poDS->nBands = 3;
    1270          60 :     for (int i = 0; i < poDS->nBands; i++)
    1271          45 :         poDS->SetBand(i + 1, new ADRGRasterBand(poDS, i + 1));
    1272             : 
    1273          15 :     return poDS;
    1274             : }
    1275             : 
    1276             : /************************************************************************/
    1277             : /*                          GetGENListFromTHF()                         */
    1278             : /************************************************************************/
    1279             : 
    1280           7 : char **ADRGDataset::GetGENListFromTHF(const char *pszFileName)
    1281             : {
    1282          14 :     DDFModule module;
    1283           7 :     DDFRecord *record = nullptr;
    1284           7 :     int nFilenames = 0;
    1285           7 :     char **papszFileNames = nullptr;
    1286             : 
    1287           7 :     if (!module.Open(pszFileName, TRUE))
    1288           0 :         return papszFileNames;
    1289             : 
    1290             :     while (true)
    1291             :     {
    1292          27 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    1293          27 :         record = module.ReadRecord();
    1294          27 :         CPLPopErrorHandler();
    1295          27 :         CPLErrorReset();
    1296          27 :         if (record == nullptr)
    1297           7 :             break;
    1298             : 
    1299          20 :         if (record->GetFieldCount() >= 2)
    1300             :         {
    1301          20 :             DDFField *field = record->GetField(0);
    1302          20 :             DDFFieldDefn *fieldDefn = field->GetFieldDefn();
    1303          40 :             if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
    1304          20 :                   fieldDefn->GetSubfieldCount() == 2))
    1305             :             {
    1306           0 :                 continue;
    1307             :             }
    1308             : 
    1309          20 :             const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
    1310          20 :             if (RTY == nullptr || !(strcmp(RTY, "TFN") == 0))
    1311             :             {
    1312          17 :                 continue;
    1313             :             }
    1314             : 
    1315           3 :             int iVFFFieldInstance = 0;
    1316          16 :             for (int i = 1; i < record->GetFieldCount(); i++)
    1317             :             {
    1318          13 :                 field = record->GetField(i);
    1319          13 :                 fieldDefn = field->GetFieldDefn();
    1320             : 
    1321          26 :                 if (!(strcmp(fieldDefn->GetName(), "VFF") == 0 &&
    1322          13 :                       fieldDefn->GetSubfieldCount() == 1))
    1323             :                 {
    1324           0 :                     continue;
    1325             :                 }
    1326             : 
    1327          13 :                 const char *pszVFF = record->GetStringSubfield(
    1328             :                     "VFF", iVFFFieldInstance++, "VFF", 0);
    1329          13 :                 if (pszVFF == nullptr)
    1330           0 :                     continue;
    1331          13 :                 CPLString osSubFileName(pszVFF);
    1332          13 :                 char *c = (char *)strchr(osSubFileName.c_str(), ' ');
    1333          13 :                 if (c)
    1334          13 :                     *c = 0;
    1335          13 :                 if (EQUAL(CPLGetExtension(osSubFileName.c_str()), "GEN"))
    1336             :                 {
    1337           3 :                     CPLDebug("ADRG", "Found GEN file in THF : %s",
    1338             :                              osSubFileName.c_str());
    1339           3 :                     CPLString osGENFileName(CPLGetDirname(pszFileName));
    1340             :                     char **tokens =
    1341           3 :                         CSLTokenizeString2(osSubFileName.c_str(), "/\"", 0);
    1342           3 :                     char **ptr = tokens;
    1343           3 :                     if (ptr == nullptr)
    1344           0 :                         continue;
    1345           6 :                     while (*ptr)
    1346             :                     {
    1347             :                         char **papszDirContent =
    1348           3 :                             VSIReadDir(osGENFileName.c_str());
    1349           3 :                         char **ptrDir = papszDirContent;
    1350           3 :                         if (ptrDir)
    1351             :                         {
    1352           5 :                             while (*ptrDir)
    1353             :                             {
    1354           5 :                                 if (EQUAL(*ptrDir, *ptr))
    1355             :                                 {
    1356             :                                     osGENFileName =
    1357             :                                         CPLFormFilename(osGENFileName.c_str(),
    1358           3 :                                                         *ptrDir, nullptr);
    1359           3 :                                     CPLDebug("ADRG",
    1360             :                                              "Building GEN full file name : %s",
    1361             :                                              osGENFileName.c_str());
    1362           3 :                                     break;
    1363             :                                 }
    1364           2 :                                 ptrDir++;
    1365             :                             }
    1366             :                         }
    1367           3 :                         if (ptrDir == nullptr)
    1368           0 :                             break;
    1369           3 :                         CSLDestroy(papszDirContent);
    1370           3 :                         ptr++;
    1371             :                     }
    1372           3 :                     int isNameValid = *ptr == nullptr;
    1373           3 :                     CSLDestroy(tokens);
    1374           3 :                     if (isNameValid)
    1375             :                     {
    1376           6 :                         papszFileNames = (char **)CPLRealloc(
    1377           3 :                             papszFileNames, sizeof(char *) * (nFilenames + 2));
    1378           6 :                         papszFileNames[nFilenames] =
    1379           3 :                             CPLStrdup(osGENFileName.c_str());
    1380           3 :                         papszFileNames[nFilenames + 1] = nullptr;
    1381           3 :                         nFilenames++;
    1382             :                     }
    1383             :                 }
    1384             :             }
    1385             :         }
    1386          20 :     }
    1387           7 :     return papszFileNames;
    1388             : }
    1389             : 
    1390             : /************************************************************************/
    1391             : /*                          GetIMGListFromGEN()                         */
    1392             : /************************************************************************/
    1393             : 
    1394          14 : char **ADRGDataset::GetIMGListFromGEN(const char *pszFileName,
    1395             :                                       int *pnRecordIndex)
    1396             : {
    1397          14 :     DDFRecord *record = nullptr;
    1398          14 :     int nFilenames = 0;
    1399          14 :     char **papszFileNames = nullptr;
    1400          14 :     int nRecordIndex = -1;
    1401             : 
    1402          14 :     if (pnRecordIndex)
    1403          14 :         *pnRecordIndex = -1;
    1404             : 
    1405          28 :     DDFModule module;
    1406          14 :     if (!module.Open(pszFileName, TRUE))
    1407           0 :         return nullptr;
    1408             : 
    1409             :     while (true)
    1410             :     {
    1411          57 :         nRecordIndex++;
    1412             : 
    1413          57 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    1414          57 :         record = module.ReadRecord();
    1415          57 :         CPLPopErrorHandler();
    1416          57 :         CPLErrorReset();
    1417          57 :         if (record == nullptr)
    1418          14 :             break;
    1419             : 
    1420          43 :         if (record->GetFieldCount() >= 5)
    1421             :         {
    1422          29 :             DDFField *field = record->GetField(0);
    1423          29 :             DDFFieldDefn *fieldDefn = field->GetFieldDefn();
    1424          58 :             if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
    1425          29 :                   fieldDefn->GetSubfieldCount() == 2))
    1426             :             {
    1427          14 :                 continue;
    1428             :             }
    1429             : 
    1430          29 :             const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
    1431          29 :             if (RTY == nullptr)
    1432           0 :                 continue;
    1433             :             /* Ignore overviews */
    1434          29 :             if (strcmp(RTY, "OVV") == 0)
    1435          14 :                 continue;
    1436             : 
    1437             :             // TODO: Fix the non-GIN section or remove it.
    1438          15 :             if (strcmp(RTY, "GIN") != 0)
    1439           0 :                 continue;
    1440             : 
    1441             :             /* make sure that the GEN file is part of an ADRG dataset, not a SRP
    1442             :              * dataset, by checking that the GEN field contains a NWO subfield
    1443             :              */
    1444          15 :             const char *NWO = record->GetStringSubfield("GEN", 0, "NWO", 0);
    1445          15 :             if (NWO == nullptr)
    1446             :             {
    1447           0 :                 CSLDestroy(papszFileNames);
    1448           0 :                 return nullptr;
    1449             :             }
    1450             : 
    1451          15 :             field = record->GetField(3);
    1452          15 :             if (field == nullptr)
    1453           0 :                 continue;
    1454          15 :             fieldDefn = field->GetFieldDefn();
    1455             : 
    1456          30 :             if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
    1457          15 :                   fieldDefn->GetSubfieldCount() == 15))
    1458             :             {
    1459           0 :                 continue;
    1460             :             }
    1461             : 
    1462          15 :             const char *pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
    1463          15 :             if (pszBAD == nullptr || strlen(pszBAD) != 12)
    1464           0 :                 continue;
    1465          30 :             CPLString osBAD = pszBAD;
    1466             :             {
    1467          15 :                 char *c = (char *)strchr(osBAD.c_str(), ' ');
    1468          15 :                 if (c)
    1469           0 :                     *c = 0;
    1470             :             }
    1471          15 :             CPLDebug("ADRG", "BAD=%s", osBAD.c_str());
    1472             : 
    1473             :             /* Build full IMG file name from BAD value */
    1474          30 :             CPLString osGENDir(CPLGetDirname(pszFileName));
    1475             : 
    1476             :             const CPLString osFileName =
    1477          15 :                 CPLFormFilename(osGENDir.c_str(), osBAD.c_str(), nullptr);
    1478             :             VSIStatBufL sStatBuf;
    1479          15 :             if (VSIStatL(osFileName, &sStatBuf) == 0)
    1480             :             {
    1481          15 :                 osBAD = osFileName;
    1482          15 :                 CPLDebug("ADRG", "Building IMG full file name : %s",
    1483             :                          osBAD.c_str());
    1484             :             }
    1485             :             else
    1486             :             {
    1487           0 :                 char **papszDirContent = nullptr;
    1488           0 :                 if (strcmp(osGENDir.c_str(), "/vsimem") == 0)
    1489             :                 {
    1490           0 :                     CPLString osTmp = osGENDir + "/";
    1491           0 :                     papszDirContent = VSIReadDir(osTmp);
    1492             :                 }
    1493             :                 else
    1494           0 :                     papszDirContent = VSIReadDir(osGENDir);
    1495           0 :                 char **ptrDir = papszDirContent;
    1496           0 :                 while (ptrDir && *ptrDir)
    1497             :                 {
    1498           0 :                     if (EQUAL(*ptrDir, osBAD.c_str()))
    1499             :                     {
    1500             :                         osBAD =
    1501           0 :                             CPLFormFilename(osGENDir.c_str(), *ptrDir, nullptr);
    1502           0 :                         CPLDebug("ADRG", "Building IMG full file name : %s",
    1503             :                                  osBAD.c_str());
    1504           0 :                         break;
    1505             :                     }
    1506           0 :                     ptrDir++;
    1507             :                 }
    1508           0 :                 CSLDestroy(papszDirContent);
    1509             :             }
    1510             : 
    1511          15 :             if (nFilenames == 0 && pnRecordIndex)
    1512          14 :                 *pnRecordIndex = nRecordIndex;
    1513             : 
    1514          30 :             papszFileNames = (char **)CPLRealloc(
    1515          15 :                 papszFileNames, sizeof(char *) * (nFilenames + 2));
    1516          15 :             papszFileNames[nFilenames] = CPLStrdup(osBAD.c_str());
    1517          15 :             papszFileNames[nFilenames + 1] = nullptr;
    1518          15 :             nFilenames++;
    1519             :         }
    1520          43 :     }
    1521             : 
    1522          14 :     return papszFileNames;
    1523             : }
    1524             : 
    1525             : /************************************************************************/
    1526             : /*                                Open()                                */
    1527             : /************************************************************************/
    1528             : 
    1529       29893 : GDALDataset *ADRGDataset::Open(GDALOpenInfo *poOpenInfo)
    1530             : {
    1531       29893 :     int nRecordIndex = -1;
    1532       59779 :     CPLString osGENFileName;
    1533       59767 :     CPLString osIMGFileName;
    1534       29884 :     bool bFromSubdataset = false;
    1535             : 
    1536       29884 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "ADRG:"))
    1537             :     {
    1538             :         char **papszTokens =
    1539           2 :             CSLTokenizeString2(poOpenInfo->pszFilename + 5, ",", 0);
    1540           2 :         if (CSLCount(papszTokens) == 2)
    1541             :         {
    1542           2 :             osGENFileName = papszTokens[0];
    1543           2 :             osIMGFileName = papszTokens[1];
    1544           2 :             bFromSubdataset = true;
    1545             :         }
    1546           2 :         CSLDestroy(papszTokens);
    1547             :     }
    1548             :     else
    1549             :     {
    1550       29882 :         if (poOpenInfo->nHeaderBytes < 500)
    1551       27880 :             return nullptr;
    1552             : 
    1553        2007 :         CPLString osFileName(poOpenInfo->pszFilename);
    1554        2007 :         if (EQUAL(CPLGetExtension(osFileName.c_str()), "THF"))
    1555             :         {
    1556           7 :             char **papszFileNames = GetGENListFromTHF(osFileName.c_str());
    1557           7 :             if (papszFileNames == nullptr)
    1558           4 :                 return nullptr;
    1559           3 :             if (papszFileNames[1] == nullptr)
    1560             :             {
    1561           3 :                 osFileName = papszFileNames[0];
    1562           3 :                 CSLDestroy(papszFileNames);
    1563             :             }
    1564             :             else
    1565             :             {
    1566           0 :                 char **ptr = papszFileNames;
    1567           0 :                 ADRGDataset *poDS = new ADRGDataset();
    1568           0 :                 while (*ptr)
    1569             :                 {
    1570           0 :                     char **papszIMGFileNames = GetIMGListFromGEN(*ptr);
    1571           0 :                     char **papszIMGIter = papszIMGFileNames;
    1572           0 :                     while (papszIMGIter && *papszIMGIter)
    1573             :                     {
    1574           0 :                         poDS->AddSubDataset(*ptr, *papszIMGIter);
    1575           0 :                         papszIMGIter++;
    1576             :                     }
    1577           0 :                     CSLDestroy(papszIMGFileNames);
    1578             : 
    1579           0 :                     ptr++;
    1580             :                 }
    1581           0 :                 CSLDestroy(papszFileNames);
    1582           0 :                 return poDS;
    1583             :             }
    1584             :         }
    1585             : 
    1586        2003 :         if (EQUAL(CPLGetExtension(osFileName.c_str()), "GEN"))
    1587             :         {
    1588          14 :             osGENFileName = osFileName;
    1589             : 
    1590             :             char **papszFileNames =
    1591          14 :                 GetIMGListFromGEN(osFileName.c_str(), &nRecordIndex);
    1592          14 :             if (papszFileNames == nullptr)
    1593           0 :                 return nullptr;
    1594          14 :             if (papszFileNames[1] == nullptr)
    1595             :             {
    1596          13 :                 osIMGFileName = papszFileNames[0];
    1597          13 :                 CSLDestroy(papszFileNames);
    1598             :             }
    1599             :             else
    1600             :             {
    1601           1 :                 char **ptr = papszFileNames;
    1602           1 :                 ADRGDataset *poDS = new ADRGDataset();
    1603           3 :                 while (*ptr)
    1604             :                 {
    1605           2 :                     poDS->AddSubDataset(osFileName.c_str(), *ptr);
    1606           2 :                     ptr++;
    1607             :                 }
    1608           1 :                 CSLDestroy(papszFileNames);
    1609           1 :                 return poDS;
    1610             :             }
    1611             :         }
    1612             :     }
    1613             : 
    1614        2004 :     if (!osGENFileName.empty() && !osIMGFileName.empty())
    1615             :     {
    1616          15 :         if (poOpenInfo->eAccess == GA_Update)
    1617             :         {
    1618           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1619             :                      "The ADRG driver does not support update access to "
    1620             :                      "existing datasets.");
    1621          15 :             return nullptr;
    1622             :         }
    1623             : 
    1624          15 :         DDFModule module;
    1625          15 :         DDFRecord *record = nullptr;
    1626          15 :         if (nRecordIndex >= 0 && module.Open(osGENFileName.c_str(), TRUE))
    1627             :         {
    1628          52 :             for (int i = 0; i <= nRecordIndex; i++)
    1629             :             {
    1630          39 :                 CPLPushErrorHandler(CPLQuietErrorHandler);
    1631          39 :                 record = module.ReadRecord();
    1632          39 :                 CPLPopErrorHandler();
    1633          39 :                 CPLErrorReset();
    1634          39 :                 if (record == nullptr)
    1635           0 :                     break;
    1636             :             }
    1637             :         }
    1638             : 
    1639             :         ADRGDataset *poDS =
    1640          15 :             OpenDataset(osGENFileName.c_str(), osIMGFileName.c_str(), record);
    1641             : 
    1642          15 :         if (poDS)
    1643             :         {
    1644             :             /* -------------------------------------------------------------- */
    1645             :             /*      Initialize any PAM information.                           */
    1646             :             /* -------------------------------------------------------------- */
    1647          15 :             poDS->SetDescription(poOpenInfo->pszFilename);
    1648          15 :             poDS->TryLoadXML();
    1649             : 
    1650             :             /* -------------------------------------------------------------- */
    1651             :             /*      Check for external overviews.                             */
    1652             :             /* -------------------------------------------------------------- */
    1653          15 :             if (bFromSubdataset)
    1654           2 :                 poDS->oOvManager.Initialize(poDS, osIMGFileName.c_str());
    1655             :             else
    1656          13 :                 poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
    1657             : 
    1658          15 :             return poDS;
    1659             :         }
    1660             :     }
    1661             : 
    1662        1989 :     return nullptr;
    1663             : }
    1664             : 
    1665             : /************************************************************************/
    1666             : /*                               Create()                               */
    1667             : /************************************************************************/
    1668             : 
    1669          63 : GDALDataset *ADRGDataset::Create(const char *pszFilename, int nXSize,
    1670             :                                  int nYSize, int nBandsIn, GDALDataType eType,
    1671             :                                  CPL_UNUSED char **papszOptions)
    1672             : {
    1673          63 :     if (eType != GDT_Byte)
    1674             :     {
    1675          36 :         CPLError(CE_Failure, CPLE_AppDefined,
    1676             :                  "Attempt to create ADRG dataset with an illegal "
    1677             :                  "data type (%s), only Byte supported by the format.",
    1678             :                  GDALGetDataTypeName(eType));
    1679             : 
    1680          36 :         return nullptr;
    1681             :     }
    1682             : 
    1683          27 :     if (nBandsIn != 3)
    1684             :     {
    1685           9 :         CPLError(CE_Failure, CPLE_NotSupported,
    1686             :                  "ADRG driver doesn't support %d bands. "
    1687             :                  "Must be 3 (rgb) bands.",
    1688             :                  nBandsIn);
    1689           9 :         return nullptr;
    1690             :     }
    1691             : 
    1692          18 :     if (nXSize < 1 || nYSize < 1)
    1693             :     {
    1694           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1695             :                  "Specified pixel dimensions (% d x %d) are bad.", nXSize,
    1696             :                  nYSize);
    1697             :     }
    1698             : 
    1699          18 :     if (!EQUAL(CPLGetExtension(pszFilename), "gen"))
    1700             :     {
    1701           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1702             :                  "Invalid filename. Must be ABCDEF01.GEN");
    1703           0 :         return nullptr;
    1704             :     }
    1705             : 
    1706          36 :     CPLString osBaseFileName(CPLGetBasename(pszFilename));
    1707          26 :     if (osBaseFileName.size() != 8 || osBaseFileName[6] != DIGIT_ZERO ||
    1708           8 :         osBaseFileName[7] != '1')
    1709             :     {
    1710          10 :         CPLError(CE_Failure, CPLE_NotSupported,
    1711             :                  "Invalid filename. "
    1712             :                  "Must be xxxxxx01.GEN where x is between A and Z");
    1713          10 :         return nullptr;
    1714             :     }
    1715             : 
    1716          56 :     for (int i = 0; i < 6; i++)
    1717             :     {
    1718          48 :         if (!(osBaseFileName[i] >= 'A' && osBaseFileName[i] <= 'Z'))
    1719             :         {
    1720           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1721             :                      "Invalid filename. "
    1722             :                      "Must be xxxxxx01.GEN where x is between A and Z");
    1723           0 :             return nullptr;
    1724             :         }
    1725             :     }
    1726             : 
    1727           8 :     VSILFILE *fdGEN = VSIFOpenL(pszFilename, "wb");
    1728           8 :     if (fdGEN == nullptr)
    1729             :     {
    1730           3 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot create GEN file : %s.\n",
    1731             :                  pszFilename);
    1732           3 :         return nullptr;
    1733             :     }
    1734             : 
    1735          10 :     CPLString osDirname(CPLGetDirname(pszFilename));
    1736             :     CPLString osTransh01THF(
    1737          10 :         CPLFormFilename(osDirname.c_str(), "TRANSH01.THF", nullptr));
    1738           5 :     VSILFILE *fdTHF = VSIFOpenL(osTransh01THF.c_str(), "wb");
    1739           5 :     if (fdTHF == nullptr)
    1740             :     {
    1741           0 :         VSIFCloseL(fdGEN);
    1742           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot create THF file : %s.\n",
    1743             :                  osTransh01THF.c_str());
    1744           0 :         return nullptr;
    1745             :     }
    1746             : 
    1747          10 :     CPLString osImgFilename = CPLResetExtension(pszFilename, "IMG");
    1748           5 :     VSILFILE *fdIMG = VSIFOpenL(osImgFilename.c_str(), "w+b");
    1749           5 :     if (fdIMG == nullptr)
    1750             :     {
    1751           0 :         VSIFCloseL(fdGEN);
    1752           0 :         VSIFCloseL(fdTHF);
    1753           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot create image file : %s.\n",
    1754             :                  osImgFilename.c_str());
    1755           0 :         return nullptr;
    1756             :     }
    1757             : 
    1758           5 :     ADRGDataset *poDS = new ADRGDataset();
    1759             : 
    1760           5 :     poDS->eAccess = GA_Update;
    1761             : 
    1762           5 :     poDS->fdGEN = fdGEN;
    1763           5 :     poDS->fdIMG = fdIMG;
    1764           5 :     poDS->fdTHF = fdTHF;
    1765             : 
    1766           5 :     poDS->osBaseFileName = std::move(osBaseFileName);
    1767           5 :     poDS->bCreation = TRUE;
    1768           5 :     poDS->nNextAvailableBlock = 1;
    1769           5 :     poDS->NFC = (nXSize + 127) / 128;
    1770           5 :     poDS->NFL = (nYSize + 127) / 128;
    1771           5 :     poDS->nRasterXSize = nXSize;
    1772           5 :     poDS->nRasterYSize = nYSize;
    1773           5 :     poDS->bGeoTransformValid = FALSE;
    1774           5 :     poDS->TILEINDEX = new int[poDS->NFC * poDS->NFL];
    1775           5 :     memset(poDS->TILEINDEX, 0, sizeof(int) * poDS->NFC * poDS->NFL);
    1776           5 :     poDS->offsetInIMG = 2048;
    1777           5 :     poDS->poOverviewDS = nullptr;
    1778             : 
    1779           5 :     poDS->nBands = 3;
    1780          20 :     for (int i = 0; i < poDS->nBands; i++)
    1781          15 :         poDS->SetBand(i + 1, new ADRGRasterBand(poDS, i + 1));
    1782             : 
    1783           5 :     return poDS;
    1784             : }
    1785             : 
    1786             : /************************************************************************/
    1787             : /*                  WriteGENFile_Header()                               */
    1788             : /************************************************************************/
    1789             : 
    1790           5 : static void WriteGENFile_Header(VSILFILE *fd)
    1791             : {
    1792           5 :     int nFields = 0;
    1793           5 :     int sizeOfFields[] = {
    1794             :         0, 0, 0, 0, 0, 0, 0, 0, 0,
    1795             :     };
    1796           5 :     const char *nameOfFields[] = {"000", "001", "DRF", "DSI", "OVI",
    1797             :                                   "GEN", "SPR", "BDF", "TIM"};
    1798           5 :     const int pos = BeginHeader(fd, 3, 4, 3, N_ELEMENTS(sizeOfFields));
    1799             : 
    1800           5 :     sizeOfFields[nFields++] += WriteFieldDecl(
    1801             :         fd, ' ', ' ', "GENERAL_INFORMATION_FILE", "", ""); /* 000 */
    1802           5 :     sizeOfFields[nFields++] +=
    1803           5 :         WriteFieldDecl(fd, '1', '0', "RECORD_ID_FIELD", /* 001 */
    1804             :                        "RTY!RID", "(A(3),A(2))");
    1805           5 :     sizeOfFields[nFields++] +=
    1806           5 :         WriteFieldDecl(fd, '1', '1', "DATA_SET_DESCRIPTION_FIELD", /* DRF */
    1807             :                        "NSH!NSV!NOZ!NOS", "(4I(2))");
    1808           5 :     sizeOfFields[nFields++] +=
    1809           5 :         WriteFieldDecl(fd, '1', '0', "DATA_SET-ID_FIELD", /* DSI */
    1810             :                        "PRT!NAM", "(A(4),A(8))");
    1811           5 :     sizeOfFields[nFields++] +=
    1812           5 :         WriteFieldDecl(fd, '1', '6', "OVERVIEW_INFORMATION_FIELD", /* OVI */
    1813             :                        "STR!ARV!BRV!LSO!PSO", "(I(1),I(8),I(8),A(11),A(10))");
    1814           5 :     sizeOfFields[nFields++] += WriteFieldDecl(
    1815             :         fd, '1', '6', "GENERAL_INFORMATION_FIELD", /* GEN */
    1816             :         "STR!LOD!LAD!UNIloa!SWO!SWA!NWO!NWA!NEO!NEA!SEO!SEA!SCA!ZNA!PSP!IMR!"
    1817             :         "ARV!BRV!LSO!PSO!TXT",
    1818             :         "(I(1),2R(6),I(3),A(11),A(10),A(11),A(10),A(11),A(10),A(11),A(10),I(9),"
    1819             :         "I(2),R(5),A(1),2I(8),A(11),A(10),A(64))");
    1820           5 :     sizeOfFields[nFields++] += WriteFieldDecl(
    1821             :         fd, '1', '6', "DATA_SET_PARAMETERS_FIELD", /* SPR */
    1822             :         "NUL!NUS!NLL!NLS!NFL!NFC!PNC!PNL!COD!ROD!POR!PCB!PVB!BAD!TIF",
    1823             :         "(4I(6),2I(3),2I(6),5I(1),A(12),A(1))");
    1824           5 :     sizeOfFields[nFields++] +=
    1825           5 :         WriteFieldDecl(fd, '2', '6', "BAND_ID_FIELD", /* BDF */
    1826             :                        "*BID!WS1!WS2", "(A(5),I(5),I(5))");
    1827           5 :     sizeOfFields[nFields++] +=
    1828           5 :         WriteFieldDecl(fd, '2', '1', "TILE_INDEX_MAP_FIELD", /* TIM */
    1829             :                        "*TSI", "(I(5))");
    1830             : 
    1831           5 :     FinishWriteHeader(fd, pos, 3, 4, 3, N_ELEMENTS(sizeOfFields), sizeOfFields,
    1832             :                       nameOfFields);
    1833           5 : }
    1834             : 
    1835             : /************************************************************************/
    1836             : /*            WriteGENFile_DataSetDescriptionRecord()                   */
    1837             : /************************************************************************/
    1838             : 
    1839             : /* Write DATA_SET_DESCRIPTION_RECORD */
    1840           5 : static void WriteGENFile_DataSetDescriptionRecord(VSILFILE *fd)
    1841             : {
    1842           5 :     int nFields = 0;
    1843           5 :     int sizeOfFields[] = {0, 0};
    1844           5 :     const char *nameOfFields[] = {"001", "DRF"};
    1845           5 :     const int pos = BeginLeader(fd, 3, 4, 3, N_ELEMENTS(sizeOfFields));
    1846             : 
    1847             :     /* Field 001 */
    1848           5 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "DSS", 3); /* RTY */
    1849           5 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "01", 2);  /* RID */
    1850           5 :     sizeOfFields[nFields] += WriteFieldTerminator(fd);
    1851           5 :     nFields++;
    1852             : 
    1853             :     /* Field DRF */
    1854           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 1, 2); /* NSH */
    1855           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 1, 2); /* NSV */
    1856           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 1, 2); /* NOZ */
    1857           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 1, 2); /* NOS */
    1858           5 :     sizeOfFields[nFields] += WriteFieldTerminator(fd);
    1859             :     /* nFields++; */
    1860             : 
    1861           5 :     FinishWriteLeader(fd, pos, 3, 4, 3, N_ELEMENTS(sizeOfFields), sizeOfFields,
    1862             :                       nameOfFields);
    1863           5 : }
    1864             : 
    1865             : /************************************************************************/
    1866             : /*                    WriteGENFile_OverviewRecord()                     */
    1867             : /************************************************************************/
    1868             : 
    1869             : /* Write OVERVIEW_RECORD */
    1870           5 : static void WriteGENFile_OverviewRecord(VSILFILE *fd, CPLString &osBaseFileName,
    1871             :                                         int ARV, int BRV, double LSO,
    1872             :                                         double PSO, int nOvSizeX, int nOvSizeY,
    1873             :                                         int NFL, int NFC, int *TILEINDEX)
    1874             : {
    1875           5 :     int nFields = 0;
    1876           5 :     int sizeOfFields[] = {0, 0, 0, 0, 0, 0};
    1877           5 :     const char *nameOfFields[] = {"001", "DSI", "OVI", "SPR", "BDF", "TIM"};
    1878           5 :     const int pos = BeginLeader(fd, 9, 9, 3, N_ELEMENTS(sizeOfFields));
    1879             : 
    1880             :     /* Field 001 */
    1881           5 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "OVV", 3); /* RTY */
    1882           5 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "01", 2);  /* RID */
    1883           5 :     sizeOfFields[nFields] += WriteFieldTerminator(fd);
    1884           5 :     nFields++;
    1885             : 
    1886             :     /* Field DSI */
    1887           5 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "ADRG", 4);         /* PRT */
    1888           5 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, osBaseFileName, 8); /* NAM */
    1889           5 :     sizeOfFields[nFields] += WriteFieldTerminator(fd);
    1890           5 :     nFields++;
    1891             : 
    1892             :     /* Field OVI */
    1893           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 3, 1);        /* STR */
    1894           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, ARV, 8);      // ARV - FIXME
    1895           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, BRV, 8);      // BRV - FIXME
    1896           5 :     sizeOfFields[nFields] += WriteLongitude(fd, LSO); /* LSO */ /* FIXME */
    1897           5 :     sizeOfFields[nFields] += WriteLatitude(fd, PSO); /* PSO */  /* FIXME */
    1898           5 :     sizeOfFields[nFields] += WriteFieldTerminator(fd);
    1899           5 :     nFields++;
    1900             : 
    1901             :     /* Field SPR */
    1902           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 6);            /* NUL */
    1903           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, nOvSizeX - 1, 6); /* NUS */
    1904           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, nOvSizeY - 1, 6); /* NLL */
    1905           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 6);            /* NLS */
    1906           5 :     sizeOfFields[nFields] +=
    1907           5 :         WriteSubFieldInt(fd, (nOvSizeY + 127) / 128, 3); /* NFL */
    1908           5 :     sizeOfFields[nFields] +=
    1909           5 :         WriteSubFieldInt(fd, (nOvSizeX + 127) / 128, 3);   /* NFC */
    1910           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 128, 6); /* PNC */
    1911           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 128, 6); /* PNL */
    1912           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 1);   /* COD */
    1913           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 1, 1);   /* ROD */
    1914           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 1);   /* POR */
    1915           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 1);   /* PCB */
    1916           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 8, 1);   /* PVB */
    1917             :     char tmp[12 + 1];
    1918           5 :     snprintf(tmp, sizeof(tmp), "%s.IMG", osBaseFileName.c_str()); /* FIXME */
    1919           5 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, tmp, 12);       /* BAD */
    1920           5 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "Y", 1);        /* TIF */
    1921           5 :     sizeOfFields[nFields] += WriteFieldTerminator(fd);
    1922           5 :     nFields++;
    1923             : 
    1924             :     /* Field BDF */
    1925           5 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "Red", 5);   /* BID */
    1926           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS1 */
    1927           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS2 */
    1928           5 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "Green", 5); /* BID */
    1929           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS1 */
    1930           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS2 */
    1931           5 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "Blue", 5);  /* BID */
    1932           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS1 */
    1933           5 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS2 */
    1934           5 :     sizeOfFields[nFields] += WriteFieldTerminator(fd);
    1935           5 :     nFields++;
    1936             : 
    1937             :     /* Field TIM */
    1938          10 :     for (int i = 0; i < NFL * NFC; i++)
    1939             :     {
    1940           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, TILEINDEX[i], 5);  // TSI
    1941             :     }
    1942           5 :     sizeOfFields[nFields] += WriteFieldTerminator(fd);
    1943             :     /* nFields++; */
    1944             : 
    1945           5 :     FinishWriteLeader(fd, pos, 9, 9, 3, N_ELEMENTS(sizeOfFields), sizeOfFields,
    1946             :                       nameOfFields);
    1947           5 : }
    1948             : 
    1949             : /************************************************************************/
    1950             : /*              WriteGENFile_GeneralInformationRecord()                 */
    1951             : /************************************************************************/
    1952             : 
    1953             : /* Write GENERAL_INFORMATION_RECORD */
    1954           6 : static void WriteGENFile_GeneralInformationRecord(
    1955             :     VSILFILE *fd, CPLString &osNAM, CPLString &osBAD, int ARV, int BRV,
    1956             :     double LSO, double PSO, double *adfGeoTransform, int SCA, int nRasterXSize,
    1957             :     int nRasterYSize, int NFL, int NFC, int *TILEINDEX)
    1958             : 
    1959             : {
    1960           6 :     int nFields = 0;
    1961           6 :     int sizeOfFields[] = {0, 0, 0, 0, 0, 0};
    1962           6 :     const char *nameOfFields[] = {"001", "DSI", "GEN", "SPR", "BDF", "TIM"};
    1963           6 :     int pos = BeginLeader(fd, 9, 9, 3, N_ELEMENTS(sizeOfFields));
    1964             : 
    1965             :     /* Field 001 */
    1966           6 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "GIN", 3); /* RTY */
    1967           6 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "01", 2);  /* RID */
    1968           6 :     sizeOfFields[nFields] += WriteFieldTerminator(fd);
    1969           6 :     nFields++;
    1970             : 
    1971             :     /* Field DSI */
    1972           6 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "ADRG", 4);        /* PRT */
    1973           6 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, osNAM.c_str(), 8); /* NAM */
    1974           6 :     sizeOfFields[nFields] += WriteFieldTerminator(fd);
    1975           6 :     nFields++;
    1976             : 
    1977             :     /* Field `GEN */
    1978           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 3, 1);         /* STR */
    1979           6 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "0099.9", 6);  // LOD - FIXME
    1980           6 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "0099.9", 6);  // LAD - FIXME
    1981           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 16, 3);  // UNIloa - FIXME
    1982           6 :     sizeOfFields[nFields] += WriteLongitude(fd, LSO);      /* SWO */
    1983           6 :     sizeOfFields[nFields] +=
    1984           6 :         WriteLatitude(fd, PSO + nRasterYSize * adfGeoTransform[5]); /* SWA */
    1985           6 :     sizeOfFields[nFields] += WriteLongitude(fd, LSO);               /* NWO */
    1986           6 :     sizeOfFields[nFields] += WriteLatitude(fd, PSO);                /* NWA */
    1987           6 :     sizeOfFields[nFields] +=
    1988           6 :         WriteLongitude(fd, LSO + nRasterXSize * adfGeoTransform[1]); /* NEO */
    1989           6 :     sizeOfFields[nFields] += WriteLatitude(fd, PSO);                 /* NEA */
    1990           6 :     sizeOfFields[nFields] +=
    1991           6 :         WriteLongitude(fd, LSO + nRasterXSize * adfGeoTransform[1]); /* SEO */
    1992           6 :     sizeOfFields[nFields] +=
    1993           6 :         WriteLatitude(fd, PSO + nRasterYSize * adfGeoTransform[5]); /* SEA */
    1994           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, SCA, 9);          /* SCA */
    1995           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 1, 2); /* ZNA */  /* FIXME */
    1996           6 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "100.0", 5);      /* PSP */
    1997           6 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "N", 1);          /* IMR */
    1998           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, ARV, 8);          /* ARV */
    1999           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, BRV, 8);          /* BRV */
    2000           6 :     sizeOfFields[nFields] += WriteLongitude(fd, LSO);               /* LSO */
    2001           6 :     sizeOfFields[nFields] += WriteLatitude(fd, PSO);                /* PSO */
    2002           6 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "", 64);          /* TXT */
    2003           6 :     sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2004           6 :     nFields++;
    2005             : 
    2006             :     /* Field SPR */
    2007           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 6); /* NUL */
    2008           6 :     sizeOfFields[nFields] +=
    2009           6 :         WriteSubFieldInt(fd, nRasterXSize - 1, 6); /* NUS */
    2010           6 :     sizeOfFields[nFields] +=
    2011           6 :         WriteSubFieldInt(fd, nRasterYSize - 1, 6);       /* NLL */
    2012           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 6); /* NLS */
    2013           6 :     sizeOfFields[nFields] +=
    2014           6 :         WriteSubFieldInt(fd, (nRasterYSize + 127) / 128, 3); /* NFL */
    2015           6 :     sizeOfFields[nFields] +=
    2016           6 :         WriteSubFieldInt(fd, (nRasterXSize + 127) / 128, 3);          /* NFC */
    2017           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 128, 6);            /* PNC */
    2018           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 128, 6);            /* PNL */
    2019           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 1);              /* COD */
    2020           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 1, 1);              /* ROD */
    2021           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 1);              /* POR */
    2022           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 1);              /* PCB */
    2023           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 8, 1);              /* PVB */
    2024           6 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, osBAD.c_str(), 12); /* BAD */
    2025           6 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "Y", 1);            /* TIF */
    2026           6 :     sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2027           6 :     nFields++;
    2028             : 
    2029             :     /* Field BDF */
    2030           6 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "Red", 5);   /* BID */
    2031           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS1 */
    2032           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS2 */
    2033           6 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "Green", 5); /* BID */
    2034           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS1 */
    2035           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS2 */
    2036           6 :     sizeOfFields[nFields] += WriteSubFieldStr(fd, "Blue", 5);  /* BID */
    2037           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS1 */
    2038           6 :     sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS2 */
    2039           6 :     sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2040           6 :     nFields++;
    2041             : 
    2042             :     /* Field TIM */
    2043          12 :     for (int i = 0; i < NFL * NFC; i++)
    2044             :     {
    2045           6 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, TILEINDEX[i], 5);  // TSI
    2046             :     }
    2047           6 :     sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2048             :     /* nFields++; */
    2049             : 
    2050           6 :     FinishWriteLeader(fd, pos, 9, 9, 3, N_ELEMENTS(sizeOfFields), sizeOfFields,
    2051             :                       nameOfFields);
    2052           6 : }
    2053             : 
    2054             : /************************************************************************/
    2055             : /*                        WriteGENFile()                                */
    2056             : /************************************************************************/
    2057             : 
    2058           5 : void ADRGDataset::WriteGENFile()
    2059             : {
    2060           5 :     if (!bGeoTransformValid)
    2061             :     {
    2062           0 :         CPLError(CE_Failure, CPLE_AppDefined, "No geo transform available !");
    2063           0 :         adfGeoTransform[0] = 0;
    2064           0 :         adfGeoTransform[3] = 0;
    2065           0 :         adfGeoTransform[1] = 1;
    2066           0 :         adfGeoTransform[5] = 1;
    2067             :     }
    2068             : 
    2069           5 :     LSO = adfGeoTransform[0];
    2070           5 :     PSO = adfGeoTransform[3];
    2071           5 :     ARV = (int)floor(360. / adfGeoTransform[1] + .5);
    2072           5 :     BRV = (int)floor(-360. / adfGeoTransform[5] + .5);
    2073             : 
    2074             :     /*ARV = ((ARV + 255) / 512) * 512;
    2075             :     BRV = ((BRV + 255) / 512) * 512;*/
    2076             : 
    2077           5 :     const int SCA = (int)floor(1000000. * 400384 / BRV + 0.5);
    2078             : 
    2079           5 :     const int nOvSizeX = nRasterXSize;  // FIXME
    2080           5 :     const int nOvSizeY = nRasterYSize;  // FIXME
    2081             : 
    2082             :     /* Write header */
    2083           5 :     WriteGENFile_Header(fdGEN);
    2084             : 
    2085             :     /* Write DATA_SET_DESCRIPTION_RECORD */
    2086           5 :     WriteGENFile_DataSetDescriptionRecord(fdGEN);
    2087             : 
    2088             :     /* Write OVERVIEW_RECORD */
    2089           5 :     WriteGENFile_OverviewRecord(fdGEN, osBaseFileName, ARV, BRV, LSO, PSO,
    2090             :                                 nOvSizeX, nOvSizeY, NFL, NFC, TILEINDEX);
    2091             : 
    2092             :     /* Write GENERAL_INFORMATION_RECORD */
    2093          10 :     CPLString osNAM = osBaseFileName;
    2094           5 :     char tmp[12 + 1] = {};
    2095           5 :     snprintf(tmp, sizeof(tmp), "%s.IMG", osNAM.c_str());
    2096          10 :     CPLString osBAD = tmp;
    2097           5 :     WriteGENFile_GeneralInformationRecord(
    2098           5 :         fdGEN, osNAM, osBAD, ARV, BRV, LSO, PSO, adfGeoTransform, SCA,
    2099             :         nRasterXSize, nRasterYSize, NFL, NFC, TILEINDEX);
    2100             : 
    2101           5 :     if (CPLTestBool(CPLGetConfigOption("ADRG_SIMULATE_MULTI_IMG", "OFF")))
    2102             :     {
    2103           1 :         strncpy(tmp, osBaseFileName.c_str(), 6);
    2104           1 :         tmp[6] = '\0';
    2105           1 :         strcat(tmp, "02");
    2106           1 :         osNAM = tmp;
    2107           1 :         snprintf(tmp, sizeof(tmp), "%s.IMG", osNAM.c_str());
    2108           1 :         osBAD = tmp;
    2109           1 :         WriteGENFile_GeneralInformationRecord(
    2110           1 :             fdGEN, osNAM, osBAD, ARV, BRV, LSO, PSO, adfGeoTransform, SCA,
    2111             :             nRasterXSize, nRasterYSize, NFL, NFC, TILEINDEX);
    2112             :     }
    2113           5 : }
    2114             : 
    2115             : /************************************************************************/
    2116             : /*                        WriteTHFFile()                                */
    2117             : /************************************************************************/
    2118             : 
    2119           5 : void ADRGDataset::WriteTHFFile()
    2120             : {
    2121           5 :     VSILFILE *fd = fdTHF;
    2122             : 
    2123             :     /* Write header */
    2124             :     {
    2125           5 :         int nFields = 0;
    2126           5 :         int sizeOfFields[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    2127           5 :         const char *nameOfFields[] = {"000", "001", "VDR", "FDR", "QSR", "QUV",
    2128             :                                       "CPS", "CPT", "SPR", "BDF", "VFF"};
    2129           5 :         const int pos = BeginHeader(fd, 3, 4, 3, N_ELEMENTS(sizeOfFields));
    2130             : 
    2131           5 :         sizeOfFields[nFields++] += WriteFieldDecl(
    2132             :             fd, ' ', ' ', "TRANSMITTAL_HEADER_FILE", "", ""); /* 000 */
    2133           5 :         sizeOfFields[nFields++] +=
    2134           5 :             WriteFieldDecl(fd, '1', '0', "RECORD_ID_FIELD", /* 001 */
    2135             :                            "RTY!RID", "(A(3),A(2))");
    2136           5 :         sizeOfFields[nFields++] += WriteFieldDecl(
    2137             :             fd, '1', '6', "TRANSMITTAL_HEADER_FIELD", /* VDR */
    2138             :             "MSD!VOO!ADR!NOV!SQN!NOF!URF!END!DAT",
    2139             :             "(A(1),A(200),A(1),I(1),I(1),I(3),A(16),I(3),A(12))");
    2140           5 :         sizeOfFields[nFields++] +=
    2141           5 :             WriteFieldDecl(fd, '1', '6', "DATA_SET_DESCRIPTION_FIELD", /* FDR */
    2142             :                            "NAM!STR!PRT!SWO!SWA!NEO!NEA",
    2143             :                            "(A(8),I(1),A(4),A(11),A(10),A(11),A(10))");
    2144           5 :         sizeOfFields[nFields++] +=
    2145           5 :             WriteFieldDecl(fd, '1', '0', "SECURITY_AND_RELEASE_FIELD", /* QSR */
    2146             :                            "QSS!QOD!DAT!QLE", "(A(1),A(1),A(12),A(200))");
    2147           5 :         sizeOfFields[nFields++] += WriteFieldDecl(
    2148             :             fd, '1', '0', "VOLUME_UP_TO_DATENESS_FIELD", /* QUV */
    2149             :             "SRC!DAT!SPA", "(A(100),A(12),A(20))");
    2150           5 :         sizeOfFields[nFields++] += WriteFieldDecl(
    2151             :             fd, '1', '6', "TEST_PATCH_IDENTIFIER_FIELD", /* CPS */
    2152             :             "PNM!DWV!REF!PUR!PIR!PIG!PIB",
    2153             :             "(A(7),I(6),R(5),R(5),I(3),I(3),I(3))");
    2154           5 :         sizeOfFields[nFields++] += WriteFieldDecl(
    2155             :             fd, '1', '6', "TEST_PATCH_INFORMATION_FIELD", /* CPT */
    2156             :             "STR!SCR", "(I(1),A(100))");
    2157           5 :         sizeOfFields[nFields++] += WriteFieldDecl(
    2158             :             fd, '1', '6', "DATA_SET_PARAMETERS_FIELD", /* SPR */
    2159             :             "NUL!NUS!NLL!NLS!NFL!NFC!PNC!PNL!COD!ROD!POR!PCB!PVB!BAD!TIF",
    2160             :             "(I(6),I(6),I(6),I(6),I(3),I(3),I(6),I(6),I(1),I(1),I(1),I(1),"
    2161             :             "I(1),A(12),A(1))");
    2162           5 :         sizeOfFields[nFields++] +=
    2163           5 :             WriteFieldDecl(fd, '2', '6', "BAND_ID_FIELD", /* BDF */
    2164             :                            "*BID!WS1!WS2", "(A(5),I(5),I(5))");
    2165           5 :         sizeOfFields[nFields++] += WriteFieldDecl(
    2166             :             fd, '1', '0', "TRANSMITTAL_FILENAMES_FIELD", "VFF", "(A(51))");
    2167             : 
    2168           5 :         FinishWriteHeader(fd, pos, 3, 4, 3, N_ELEMENTS(sizeOfFields),
    2169             :                           sizeOfFields, nameOfFields);
    2170             :     }
    2171             : 
    2172             :     /* Write TRANSMITTAL_DESCRIPTION_RECORD */
    2173             :     {
    2174           5 :         int nFields = 0;
    2175           5 :         int sizeOfFields[] = {0, 0, 0};
    2176           5 :         const char *nameOfFields[] = {"001", "VDR", "FDR"};
    2177           5 :         int pos = BeginLeader(fd, 3, 4, 3, N_ELEMENTS(sizeOfFields));
    2178             : 
    2179             :         /* Field 001 */
    2180           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "VTH", 3); /* RTY */
    2181           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "01", 2);  /* RID */
    2182           5 :         sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2183           5 :         nFields++;
    2184             : 
    2185             :         /* Field VDR */
    2186           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, " ", 1); /* MSD */
    2187             :         // VOO - Title and address of originator
    2188           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "", 200);
    2189           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, " ", 1); /* ADR */
    2190           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 1, 1);   /* NOV */
    2191           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 1, 1);   /* SQN */
    2192           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 1, 3);   /* NOF */
    2193             :         // URF - DMA stock number for this CDROM
    2194           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "", 16);
    2195           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 1, 3); /* END */
    2196           5 :         sizeOfFields[nFields] +=
    2197           5 :             WriteSubFieldStr(fd, "017,19940101", 12);  // DAT - Publication date
    2198           5 :         sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2199           5 :         nFields++;
    2200             : 
    2201             :         /* Field FDR */
    2202           5 :         sizeOfFields[nFields] +=
    2203           5 :             WriteSubFieldStr(fd, osBaseFileName, 8);              // NAM
    2204           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 3, 1);      /* STR */
    2205           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "ADRG", 4); /* PRT */
    2206           5 :         sizeOfFields[nFields] += WriteLongitude(fd, LSO);         /* SWO */
    2207           5 :         sizeOfFields[nFields] +=
    2208           5 :             WriteLatitude(fd, PSO + nRasterYSize * adfGeoTransform[5]);  // SWA
    2209           5 :         sizeOfFields[nFields] +=
    2210           5 :             WriteLongitude(fd, LSO + nRasterXSize * adfGeoTransform[1]);  // NEO
    2211           5 :         sizeOfFields[nFields] += WriteLatitude(fd, PSO); /* NEA */
    2212           5 :         sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2213             :         /* nFields++; */
    2214             : 
    2215           5 :         FinishWriteLeader(fd, pos, 3, 4, 3, N_ELEMENTS(sizeOfFields),
    2216             :                           sizeOfFields, nameOfFields);
    2217             :     }
    2218             : 
    2219             :     /* Write SECURITY_AND_UPDATE_RECORD */
    2220             :     {
    2221           5 :         int nFields = 0;
    2222           5 :         int sizeOfFields[] = {0, 0, 0};
    2223           5 :         const char *nameOfFields[] = {"001", "QSR", "QUV"};
    2224           5 :         int pos = BeginLeader(fd, 3, 4, 3, N_ELEMENTS(sizeOfFields));
    2225             : 
    2226             :         /* Field 001 */
    2227           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "LCF", 3); /* RTY */
    2228           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "01", 2);  /* RID */
    2229           5 :         sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2230           5 :         nFields++;
    2231             : 
    2232             :         /* Field VDR */
    2233           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "U", 1);  /* QSS */
    2234           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "N", 1);  /* QOD */
    2235           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "", 12);  /* DAT */
    2236           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "", 200); /* QLE */
    2237           5 :         sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2238           5 :         nFields++;
    2239             : 
    2240             :         /* Field FDR */
    2241           5 :         sizeOfFields[nFields] += WriteSubFieldStr(
    2242             :             fd, "MILITARY SPECIFICATION ARC DIGITIZED RASTER GRAPHICS (ADRG)",
    2243             :             100); /* SRC */
    2244           5 :         sizeOfFields[nFields] +=
    2245           5 :             WriteSubFieldStr(fd, "022,19900222", 12); /* DAT */
    2246           5 :         sizeOfFields[nFields] +=
    2247           5 :             WriteSubFieldStr(fd, "MIL-A-89007", 20); /* SPA */
    2248           5 :         sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2249             :         /* nFields++; */
    2250             : 
    2251           5 :         FinishWriteLeader(fd, pos, 3, 4, 3, N_ELEMENTS(sizeOfFields),
    2252             :                           sizeOfFields, nameOfFields);
    2253             :     }
    2254             : 
    2255             :     /* Write TEST_PATCH_DATA_RECORD */
    2256             :     {
    2257           5 :         int nFields = 0;
    2258           5 :         int sizeOfFields[] = {0, 0, 0, 0, 0};
    2259           5 :         const char *nameOfFields[] = {"001", "CPS", "CPT", "SPR", "BDF"};
    2260           5 :         const int pos = BeginLeader(fd, 3, 4, 3, N_ELEMENTS(sizeOfFields));
    2261             : 
    2262             :         /* Field 001 */
    2263           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "TPA", 3); /* RTY */
    2264           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "01", 2);  /* RID */
    2265           5 :         sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2266           5 :         nFields++;
    2267             : 
    2268             :         /* Field CPS */
    2269           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "Black", 7); /* PNM */
    2270           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "", 6);      /* DMV */
    2271           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "", 5);      /* REF */
    2272           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "", 5);      /* PUR */
    2273           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 3);       /* PIR */
    2274           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 3);       /* PIG */
    2275           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 3);       /* PIB */
    2276           5 :         sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2277           5 :         nFields++;
    2278             : 
    2279             :         /* Field CPT */
    2280           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 3, 1);    /* STR */
    2281           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "", 100); /* SCR */
    2282           5 :         sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2283           5 :         nFields++;
    2284             : 
    2285           5 :         const int nPatchXSize = 512;
    2286           5 :         const int nPatchYSize = 512;
    2287             : 
    2288             :         /* Field SPR */
    2289           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 6); /* NUL */
    2290           5 :         sizeOfFields[nFields] +=
    2291           5 :             WriteSubFieldInt(fd, nPatchXSize - 1, 6); /* NUS */
    2292           5 :         sizeOfFields[nFields] +=
    2293           5 :             WriteSubFieldInt(fd, nPatchYSize - 1, 6);        /* NLL */
    2294           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 6); /* NLS */
    2295           5 :         sizeOfFields[nFields] +=
    2296           5 :             WriteSubFieldInt(fd, (nPatchYSize + 127) / 128, 3); /* NFL */
    2297           5 :         sizeOfFields[nFields] +=
    2298           5 :             WriteSubFieldInt(fd, (nPatchXSize + 127) / 128, 3); /* NFC */
    2299           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 128, 6);  /* PNC */
    2300           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 128, 6);  /* PNL */
    2301           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 1);    /* COD */
    2302           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 1, 1);    /* ROD */
    2303           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 1);    /* POR */
    2304           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 1);    /* PCB */
    2305           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 8, 1);    /* PVB */
    2306             :         // BAD
    2307           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "TESTPA01.CPH", 12);
    2308           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "N", 1); /* TIF */
    2309           5 :         sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2310           5 :         nFields++;
    2311             : 
    2312             :         /* Field BDF */
    2313           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "Red", 5);   /* BID */
    2314           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS1 */
    2315           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS2 */
    2316           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "Green", 5); /* BID */
    2317           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS1 */
    2318           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS2 */
    2319           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "Blue", 5);  /* BID */
    2320           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS1 */
    2321           5 :         sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5);       /* WS2 */
    2322           5 :         sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2323             :         /* nFields++; */
    2324             : 
    2325           5 :         FinishWriteLeader(fd, pos, 3, 4, 3, N_ELEMENTS(sizeOfFields),
    2326             :                           sizeOfFields, nameOfFields);
    2327             :     }
    2328             : 
    2329             :     /* Write TRANSMITTAL_FILENAMES_RECORD */
    2330             :     {
    2331           5 :         int nFields = 0;
    2332           5 :         int sizeOfFields[] = {0, 0, 0, 0, 0, 0, 0};
    2333             : 
    2334             :         /* Debug option to simulate ADRG datasets made of several images */
    2335             :         int nTotalFields =
    2336           5 :             CPLTestBool(CPLGetConfigOption("ADRG_SIMULATE_MULTI_IMG", "OFF"))
    2337           5 :                 ? 6
    2338           5 :                 : 5;
    2339             : 
    2340           5 :         const char *nameOfFields[] = {"001", "VFF", "VFF", "VFF",
    2341             :                                       "VFF", "VFF", "VFF"};
    2342           5 :         const int pos = BeginLeader(fd, 9, 9, 3, nTotalFields);
    2343             : 
    2344             :         /* Field 001 */
    2345           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "TFN", 3); /* RTY */
    2346           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "01", 2);  /* RID */
    2347           5 :         sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2348           5 :         nFields++;
    2349             : 
    2350             :         /* Field VFF */
    2351           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "TRANSH01.THF", 51);
    2352           5 :         sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2353           5 :         nFields++;
    2354             : 
    2355             :         /* Field VFF */
    2356           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, "TESTPA01.CPH", 51);
    2357           5 :         sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2358           5 :         nFields++;
    2359             : 
    2360             :         /* Field VFF */
    2361           5 :         char tmp[12 + 1] = {};
    2362           5 :         snprintf(tmp, sizeof(tmp), "%s.GEN", osBaseFileName.c_str());
    2363           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, tmp, 51); /* VFF */
    2364           5 :         sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2365           5 :         nFields++;
    2366             : 
    2367             :         /* Field VFF */
    2368           5 :         snprintf(tmp, sizeof(tmp), "%s.IMG", osBaseFileName.c_str());
    2369           5 :         sizeOfFields[nFields] += WriteSubFieldStr(fd, tmp, 51); /* VFF */
    2370           5 :         sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2371           5 :         nFields++;
    2372             : 
    2373           5 :         if (nTotalFields == 6)
    2374             :         {
    2375             :             /* Field VFF */
    2376           1 :             strncpy(tmp, osBaseFileName.c_str(), 6);
    2377           1 :             tmp[6] = '\0';
    2378           1 :             strcat(tmp, "02.IMG");
    2379           1 :             sizeOfFields[nFields] += WriteSubFieldStr(fd, tmp, 51); /* VFF */
    2380           1 :             sizeOfFields[nFields] += WriteFieldTerminator(fd);
    2381             :             /* nFields++; */
    2382             :         }
    2383             : 
    2384           5 :         FinishWriteLeader(fd, pos, 9, 9, 3, nTotalFields, sizeOfFields,
    2385             :                           nameOfFields);
    2386             :     }
    2387           5 : }
    2388             : 
    2389             : /************************************************************************/
    2390             : /*                         GDALRegister_ADRG()                          */
    2391             : /************************************************************************/
    2392             : 
    2393        1595 : void GDALRegister_ADRG()
    2394             : 
    2395             : {
    2396        1595 :     if (GDALGetDriverByName("ADRG") != nullptr)
    2397         302 :         return;
    2398             : 
    2399        1293 :     GDALDriver *poDriver = new GDALDriver();
    2400             : 
    2401        1293 :     poDriver->SetDescription("ADRG");
    2402        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    2403        1293 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
    2404        1293 :                               "ARC Digitized Raster Graphics");
    2405        1293 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/adrg.html");
    2406        1293 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "gen");
    2407        1293 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte");
    2408        1293 :     poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
    2409        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    2410             : 
    2411        1293 :     poDriver->pfnOpen = ADRGDataset::Open;
    2412        1293 :     poDriver->pfnCreate = ADRGDataset::Create;
    2413             : 
    2414        1293 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    2415             : }

Generated by: LCOV version 1.14