Coverage Report

Created: 2026-03-30 09:00

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