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

Generated by: LCOV version 1.14