Coverage Report

Created: 2025-08-11 09:23

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