Coverage Report

Created: 2025-06-09 08:44

/src/gdal/frmts/adrg/srpdataset.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 * Purpose:  ASRP/USRP Reader
3
 * Author:   Frank Warmerdam (warmerdam@pobox.com)
4
 *
5
 * Derived from ADRG driver by Even Rouault, even.rouault at spatialys.com.
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
9
 * Copyright (c) 2009, Frank Warmerdam
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_string.h"
15
#include "gdal_pam.h"
16
#include "gdal_frmts.h"
17
#include "iso8211.h"
18
#include "ogr_spatialref.h"
19
20
#include <cstdlib>
21
#include <algorithm>
22
#include <limits>
23
24
// Uncomment to recognize also .gen files in addition to .img files
25
// #define OPEN_GEN
26
27
class SRPDataset final : public GDALPamDataset
28
{
29
    friend class SRPRasterBand;
30
31
    static CPLString ResetTo01(const char *str);
32
33
    VSILFILE *fdIMG;
34
    int *TILEINDEX;
35
    int offsetInIMG;
36
    CPLString osProduct;
37
    OGRSpatialReference m_oSRS{};
38
    CPLString osGENFileName;
39
    CPLString osQALFileName;
40
    CPLString osIMGFileName;
41
    int NFC;
42
    int NFL;
43
    int ZNA;
44
    double LSO;
45
    double PSO;
46
    double LOD;
47
    double LAD;
48
    int ARV;
49
    int BRV;
50
    int PCB;
51
    int PVB;
52
53
    char **papszSubDatasets;
54
55
    GDALColorTable oCT;
56
57
    static char **GetGENListFromTHF(const char *pszFileName);
58
    static char **GetIMGListFromGEN(const char *pszFileName,
59
                                    int *pnRecordIndex = nullptr);
60
    static SRPDataset *OpenDataset(const char *pszGENFileName,
61
                                   const char *pszIMGFileName,
62
                                   DDFRecord *record = nullptr);
63
    static DDFRecord *FindRecordInGENForIMG(DDFModule &module,
64
                                            const char *pszGENFileName,
65
                                            const char *pszIMGFileName);
66
67
  public:
68
    SRPDataset();
69
    ~SRPDataset() override;
70
71
    const OGRSpatialReference *GetSpatialRef() const override;
72
    CPLErr GetGeoTransform(double *padfGeoTransform) override;
73
74
    char **GetMetadata(const char *pszDomain = "") override;
75
76
    char **GetFileList() override;
77
78
    bool GetFromRecord(const char *pszFileName, DDFRecord *record);
79
    void AddSubDataset(const char *pszGENFileName, const char *pszIMGFileName);
80
    void AddMetadatafromFromTHF(const char *pszFileName);
81
82
    static GDALDataset *Open(GDALOpenInfo *);
83
};
84
85
/************************************************************************/
86
/* ==================================================================== */
87
/*                            SRPRasterBand                            */
88
/* ==================================================================== */
89
/************************************************************************/
90
91
class SRPRasterBand final : public GDALPamRasterBand
92
{
93
    friend class SRPDataset;
94
95
  public:
96
    SRPRasterBand(SRPDataset *, int);
97
98
    CPLErr IReadBlock(int, int, void *) override;
99
100
    double GetNoDataValue(int *pbSuccess = nullptr) override;
101
102
    GDALColorInterp GetColorInterpretation() override;
103
    GDALColorTable *GetColorTable() override;
104
};
105
106
/************************************************************************/
107
/*                           SRPRasterBand()                            */
108
/************************************************************************/
109
110
SRPRasterBand::SRPRasterBand(SRPDataset *poDSIn, int nBandIn)
111
112
10.2k
{
113
10.2k
    poDS = poDSIn;
114
10.2k
    nBand = nBandIn;
115
116
10.2k
    eDataType = GDT_Byte;
117
118
10.2k
    nBlockXSize = 128;
119
10.2k
    nBlockYSize = 128;
120
10.2k
}
121
122
/************************************************************************/
123
/*                            GetNoDataValue()                          */
124
/************************************************************************/
125
126
double SRPRasterBand::GetNoDataValue(int *pbSuccess)
127
40.9k
{
128
40.9k
    if (pbSuccess)
129
30.6k
        *pbSuccess = TRUE;
130
131
40.9k
    return 0;
132
40.9k
}
133
134
/************************************************************************/
135
/*                       GetColorInterpretation()                       */
136
/************************************************************************/
137
138
GDALColorInterp SRPRasterBand::GetColorInterpretation()
139
140
0
{
141
0
    SRPDataset *l_poDS = cpl::down_cast<SRPDataset *>(poDS);
142
143
0
    if (l_poDS->oCT.GetColorEntryCount() > 0)
144
0
        return GCI_PaletteIndex;
145
0
    else
146
0
        return GCI_GrayIndex;
147
0
}
148
149
/************************************************************************/
150
/*                           GetColorTable()                            */
151
/************************************************************************/
152
153
GDALColorTable *SRPRasterBand::GetColorTable()
154
155
0
{
156
0
    SRPDataset *l_poDS = cpl::down_cast<SRPDataset *>(poDS);
157
158
0
    if (l_poDS->oCT.GetColorEntryCount() > 0)
159
0
        return &(l_poDS->oCT);
160
0
    else
161
0
        return nullptr;
162
0
}
163
164
/************************************************************************/
165
/*                             IReadBlock()                             */
166
/************************************************************************/
167
168
CPLErr SRPRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
169
170
12.0k
{
171
12.0k
    SRPDataset *l_poDS = cpl::down_cast<SRPDataset *>(poDS);
172
12.0k
    vsi_l_offset offset;
173
12.0k
    int nBlock = nBlockYOff * l_poDS->NFC + nBlockXOff;
174
12.0k
    if (nBlockXOff >= l_poDS->NFC || nBlockYOff >= l_poDS->NFL)
175
0
    {
176
0
        CPLError(CE_Failure, CPLE_AppDefined,
177
0
                 "nBlockXOff=%d, NFC=%d, nBlockYOff=%d, NFL=%d", nBlockXOff,
178
0
                 l_poDS->NFC, nBlockYOff, l_poDS->NFL);
179
0
        return CE_Failure;
180
0
    }
181
182
    /* -------------------------------------------------------------------- */
183
    /*      Is this a null block?                                           */
184
    /* -------------------------------------------------------------------- */
185
12.0k
    if (l_poDS->TILEINDEX && l_poDS->TILEINDEX[nBlock] <= 0)
186
87
    {
187
87
        memset(pImage, 0, 128 * 128);
188
87
        return CE_None;
189
87
    }
190
191
    /* -------------------------------------------------------------------- */
192
    /*      Compute the offset to the block.                                */
193
    /* -------------------------------------------------------------------- */
194
11.9k
    if (l_poDS->TILEINDEX)
195
43
    {
196
43
        if (l_poDS->PCB == 0)  // uncompressed
197
43
            offset = l_poDS->offsetInIMG +
198
43
                     static_cast<vsi_l_offset>(l_poDS->TILEINDEX[nBlock] - 1) *
199
43
                         128 * 128;
200
0
        else  // compressed
201
0
            offset = l_poDS->offsetInIMG +
202
0
                     static_cast<vsi_l_offset>(l_poDS->TILEINDEX[nBlock] - 1);
203
43
    }
204
11.9k
    else
205
11.9k
        offset =
206
11.9k
            l_poDS->offsetInIMG + static_cast<vsi_l_offset>(nBlock) * 128 * 128;
207
208
    /* -------------------------------------------------------------------- */
209
    /*      Seek to target location.                                        */
210
    /* -------------------------------------------------------------------- */
211
11.9k
    if (VSIFSeekL(l_poDS->fdIMG, offset, SEEK_SET) != 0)
212
0
    {
213
0
        CPLError(CE_Failure, CPLE_FileIO,
214
0
                 "Cannot seek to offset " CPL_FRMT_GUIB, offset);
215
0
        return CE_Failure;
216
0
    }
217
218
    /* -------------------------------------------------------------------- */
219
    /*      For uncompressed case we read the 128x128 and return with no    */
220
    /*      further processing.                                             */
221
    /* -------------------------------------------------------------------- */
222
11.9k
    if (l_poDS->PCB == 0)
223
10.8k
    {
224
10.8k
        if (VSIFReadL(pImage, 1, 128 * 128, l_poDS->fdIMG) != 128 * 128)
225
9.62k
        {
226
9.62k
            CPLError(CE_Failure, CPLE_FileIO,
227
9.62k
                     "Cannot read data at offset " CPL_FRMT_GUIB, offset);
228
9.62k
            return CE_Failure;
229
9.62k
        }
230
10.8k
    }
231
232
    /* -------------------------------------------------------------------- */
233
    /*      If this is compressed data, we read a goodly chunk of data      */
234
    /*      and then decode it.                                             */
235
    /* -------------------------------------------------------------------- */
236
1.08k
    else
237
1.08k
    {
238
1.08k
        const int nBufSize = 128 * 128 * 2;
239
1.08k
        GByte *pabyCData = (GByte *)CPLCalloc(nBufSize, 1);
240
241
1.08k
        const int nBytesRead =
242
1.08k
            static_cast<int>(VSIFReadL(pabyCData, 1, nBufSize, l_poDS->fdIMG));
243
1.08k
        if (nBytesRead == 0)
244
38
        {
245
38
            CPLError(CE_Failure, CPLE_FileIO,
246
38
                     "Cannot read data at offset " CPL_FRMT_GUIB, offset);
247
38
            CPLFree(pabyCData);
248
38
            return CE_Failure;
249
38
        }
250
251
1.05k
        CPLAssert(l_poDS->PVB == 8);
252
1.05k
        CPLAssert(l_poDS->PCB == 4 || l_poDS->PCB == 8);
253
254
1.05k
        bool bHalfByteUsed = false;
255
1.19M
        for (int iSrc = 0, iPixel = 0; iPixel < 128 * 128;)
256
1.18M
        {
257
1.18M
            if (iSrc + 2 > nBytesRead)
258
513
            {
259
513
                CPLFree(pabyCData);
260
513
                CPLError(CE_Failure, CPLE_AppDefined,
261
513
                         "Out of data decoding image block, only %d available.",
262
513
                         iSrc);
263
513
                return CE_Failure;
264
513
            }
265
266
1.18M
            int nCount = 0;
267
1.18M
            int nValue = 0;
268
269
1.18M
            if (l_poDS->PCB == 8)
270
8.28k
            {
271
8.28k
                nCount = pabyCData[iSrc++];
272
8.28k
                nValue = pabyCData[iSrc++];
273
8.28k
            }
274
1.18M
            else if (l_poDS->PCB == 4)
275
1.18M
            {
276
1.18M
                if ((iPixel % 128) == 0 && bHalfByteUsed)
277
59.4k
                {
278
59.4k
                    iSrc++;
279
59.4k
                    bHalfByteUsed = false;
280
59.4k
                    continue;
281
59.4k
                }
282
283
1.12M
                if (bHalfByteUsed)
284
530k
                {
285
530k
                    nCount = pabyCData[iSrc++] & 0xf;
286
530k
                    nValue = pabyCData[iSrc++];
287
530k
                    bHalfByteUsed = false;
288
530k
                }
289
590k
                else
290
590k
                {
291
590k
                    nCount = pabyCData[iSrc] >> 4;
292
590k
                    nValue = ((pabyCData[iSrc] & 0xf) << 4) +
293
590k
                             (pabyCData[iSrc + 1] >> 4);
294
590k
                    bHalfByteUsed = true;
295
590k
                    iSrc++;
296
590k
                }
297
1.12M
            }
298
299
1.12M
            if (iPixel + nCount > 128 * 128)
300
41
            {
301
41
                CPLFree(pabyCData);
302
41
                CPLError(CE_Failure, CPLE_AppDefined,
303
41
                         "Too much data decoding image block, likely corrupt.");
304
41
                return CE_Failure;
305
41
            }
306
307
10.6M
            while (nCount > 0)
308
9.51M
            {
309
9.51M
                ((GByte *)pImage)[iPixel++] = (GByte)nValue;
310
9.51M
                nCount--;
311
9.51M
            }
312
1.12M
        }
313
314
496
        CPLFree(pabyCData);
315
496
    }
316
317
1.75k
    return CE_None;
318
11.9k
}
319
320
/************************************************************************/
321
/*                          SRPDataset()                               */
322
/************************************************************************/
323
324
SRPDataset::SRPDataset()
325
10.7k
    : fdIMG(nullptr), TILEINDEX(nullptr), offsetInIMG(0), NFC(0), NFL(0),
326
10.7k
      ZNA(0), LSO(0.0), PSO(0.0), LOD(0.0), LAD(0.0), ARV(0), BRV(0), PCB(0),
327
10.7k
      PVB(0), papszSubDatasets(nullptr)
328
10.7k
{
329
10.7k
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
330
10.7k
}
331
332
/************************************************************************/
333
/*                          ~SRPDataset()                              */
334
/************************************************************************/
335
336
SRPDataset::~SRPDataset()
337
10.7k
{
338
10.7k
    CSLDestroy(papszSubDatasets);
339
340
10.7k
    if (fdIMG)
341
10.2k
    {
342
10.2k
        VSIFCloseL(fdIMG);
343
10.2k
    }
344
345
10.7k
    if (TILEINDEX)
346
38
    {
347
38
        delete[] TILEINDEX;
348
38
    }
349
10.7k
}
350
351
/************************************************************************/
352
/*                          ResetTo01()                                 */
353
/* Replace the DD in ZZZZZZDD.XXX with 01.                              */
354
/************************************************************************/
355
356
CPLString SRPDataset::ResetTo01(const char *str)
357
11.5k
{
358
11.5k
    CPLString osResult = str;
359
360
11.5k
    osResult[6] = '0';
361
11.5k
    osResult[7] = '1';
362
363
11.5k
    return osResult;
364
11.5k
}
365
366
/************************************************************************/
367
/*                        GetSpatialRef()                               */
368
/************************************************************************/
369
370
const OGRSpatialReference *SRPDataset::GetSpatialRef() const
371
10.2k
{
372
10.2k
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
373
10.2k
}
374
375
/************************************************************************/
376
/*                        GetGeoTransform()                             */
377
/************************************************************************/
378
379
CPLErr SRPDataset::GetGeoTransform(double *padfGeoTransform)
380
10.2k
{
381
10.2k
    if (EQUAL(osProduct, "ASRP"))
382
638
    {
383
638
        if (ARV == 0)
384
638
            return CE_Failure;
385
0
        if (ZNA == 9)
386
0
        {
387
            // North Polar Case
388
0
            padfGeoTransform[0] = 111319.4907933 * (90.0 - PSO / 3600.0) *
389
0
                                  sin(LSO * M_PI / 648000.0);
390
0
            padfGeoTransform[1] = 40075016.68558 / ARV;
391
0
            padfGeoTransform[2] = 0.0;
392
0
            padfGeoTransform[3] = -111319.4907933 * (90.0 - PSO / 3600.0) *
393
0
                                  cos(LSO * M_PI / 648000.0);
394
0
            padfGeoTransform[4] = 0.0;
395
0
            padfGeoTransform[5] = -40075016.68558 / ARV;
396
0
        }
397
0
        else if (ZNA == 18)
398
0
        {
399
            // South Polar Case
400
0
            padfGeoTransform[0] = 111319.4907933 * (90.0 + PSO / 3600.0) *
401
0
                                  sin(LSO * M_PI / 648000.0);
402
0
            padfGeoTransform[1] = 40075016.68558 / ARV;
403
0
            padfGeoTransform[2] = 0.0;
404
0
            padfGeoTransform[3] = 111319.4907933 * (90.0 + PSO / 3600.0) *
405
0
                                  cos(LSO * M_PI / 648000.0);
406
0
            padfGeoTransform[4] = 0.0;
407
0
            padfGeoTransform[5] = -40075016.68558 / ARV;
408
0
        }
409
0
        else
410
0
        {
411
0
            if (BRV == 0)
412
0
                return CE_Failure;
413
0
            padfGeoTransform[0] = LSO / 3600.0;
414
0
            padfGeoTransform[1] = 360. / ARV;
415
0
            padfGeoTransform[2] = 0.0;
416
0
            padfGeoTransform[3] = PSO / 3600.0;
417
0
            padfGeoTransform[4] = 0.0;
418
0
            padfGeoTransform[5] = -360. / BRV;
419
0
        }
420
421
0
        return CE_None;
422
0
    }
423
9.59k
    else if (EQUAL(osProduct, "USRP"))
424
9.59k
    {
425
9.59k
        padfGeoTransform[0] = LSO;
426
9.59k
        padfGeoTransform[1] = LOD;
427
9.59k
        padfGeoTransform[2] = 0.0;
428
9.59k
        padfGeoTransform[3] = PSO;
429
9.59k
        padfGeoTransform[4] = 0.0;
430
9.59k
        padfGeoTransform[5] = -LAD;
431
9.59k
        return CE_None;
432
9.59k
    }
433
434
0
    return CE_Failure;
435
10.2k
}
436
437
/************************************************************************/
438
/*                           GetFromRecord()                            */
439
/************************************************************************/
440
441
bool SRPDataset::GetFromRecord(const char *pszFileName, DDFRecord *record)
442
10.7k
{
443
10.7k
    int bSuccess;
444
445
    /* -------------------------------------------------------------------- */
446
    /*      Read a variety of header fields of interest from the .GEN       */
447
    /*      file.                                                           */
448
    /* -------------------------------------------------------------------- */
449
10.7k
    const int nSTR = record->GetIntSubfield("GEN", 0, "STR", 0, &bSuccess);
450
10.7k
    if (!bSuccess || nSTR != 4)
451
14
    {
452
14
        CPLDebug("SRP", "Failed to extract STR, or not 4.");
453
14
        return false;
454
14
    }
455
456
10.7k
    const int SCA = record->GetIntSubfield("GEN", 0, "SCA", 0, &bSuccess);
457
10.7k
    CPLDebug("SRP", "SCA=%d", SCA);
458
459
10.7k
    ZNA = record->GetIntSubfield("GEN", 0, "ZNA", 0, &bSuccess);
460
10.7k
    CPLDebug("SRP", "ZNA=%d", ZNA);
461
462
10.7k
    const double PSP = record->GetFloatSubfield("GEN", 0, "PSP", 0, &bSuccess);
463
10.7k
    CPLDebug("SRP", "PSP=%f", PSP);
464
465
10.7k
    ARV = record->GetIntSubfield("GEN", 0, "ARV", 0, &bSuccess);
466
10.7k
    CPLDebug("SRP", "ARV=%d", ARV);
467
468
10.7k
    BRV = record->GetIntSubfield("GEN", 0, "BRV", 0, &bSuccess);
469
10.7k
    CPLDebug("SRP", "BRV=%d", BRV);
470
471
10.7k
    LSO = record->GetFloatSubfield("GEN", 0, "LSO", 0, &bSuccess);
472
10.7k
    CPLDebug("SRP", "LSO=%f", LSO);
473
474
10.7k
    PSO = record->GetFloatSubfield("GEN", 0, "PSO", 0, &bSuccess);
475
10.7k
    CPLDebug("SRP", "PSO=%f", PSO);
476
477
10.7k
    LAD = record->GetFloatSubfield("GEN", 0, "LAD", 0);
478
10.7k
    LOD = record->GetFloatSubfield("GEN", 0, "LOD", 0);
479
480
10.7k
    NFL = record->GetIntSubfield("SPR", 0, "NFL", 0, &bSuccess);
481
10.7k
    CPLDebug("SRP", "NFL=%d", NFL);
482
483
10.7k
    NFC = record->GetIntSubfield("SPR", 0, "NFC", 0, &bSuccess);
484
10.7k
    CPLDebug("SRP", "NFC=%d", NFC);
485
486
10.7k
    const auto knIntMax = std::numeric_limits<int>::max();
487
10.7k
    if (NFL <= 0 || NFC <= 0 || NFL > knIntMax / 128 || NFC > knIntMax / 128 ||
488
10.7k
        NFL > knIntMax / NFC)
489
301
    {
490
301
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid NFL / NFC values");
491
301
        return false;
492
301
    }
493
494
10.4k
    const int PNC = record->GetIntSubfield("SPR", 0, "PNC", 0, &bSuccess);
495
10.4k
    CPLDebug("SRP", "PNC=%d", PNC);
496
497
10.4k
    const int PNL = record->GetIntSubfield("SPR", 0, "PNL", 0, &bSuccess);
498
10.4k
    CPLDebug("SRP", "PNL=%d", PNL);
499
500
10.4k
    if (PNL != 128 || PNC != 128)
501
25
    {
502
25
        CPLError(CE_Failure, CPLE_AppDefined, "Unsupported PNL or PNC value.");
503
25
        return false;
504
25
    }
505
506
10.3k
    PCB = record->GetIntSubfield("SPR", 0, "PCB", 0);
507
10.3k
    PVB = record->GetIntSubfield("SPR", 0, "PVB", 0);
508
10.3k
    if ((PCB != 8 && PCB != 4 && PCB != 0) || PVB != 8)
509
7
    {
510
7
        CPLError(CE_Failure, CPLE_AppDefined,
511
7
                 "PCB(%d) or PVB(%d) value unsupported.", PCB, PVB);
512
7
        return false;
513
7
    }
514
515
10.3k
    const char *pszBAD =
516
10.3k
        record->GetStringSubfield("SPR", 0, "BAD", 0, &bSuccess);
517
10.3k
    if (pszBAD == nullptr)
518
1
        return false;
519
10.3k
    const CPLString osBAD = pszBAD;
520
10.3k
    {
521
10.3k
        char *c = (char *)strchr(osBAD, ' ');
522
10.3k
        if (c)
523
2
            *c = 0;
524
10.3k
    }
525
10.3k
    CPLDebug("SRP", "BAD=%s", osBAD.c_str());
526
527
    /* -------------------------------------------------------------------- */
528
    /*      Read the tile map if available.                                 */
529
    /* -------------------------------------------------------------------- */
530
10.3k
    const char *pszTIF = record->GetStringSubfield("SPR", 0, "TIF", 0);
531
10.3k
    const bool TIF = pszTIF != nullptr && EQUAL(pszTIF, "Y");
532
10.3k
    CPLDebug("SRP", "TIF=%s", TIF ? "true" : "false");
533
534
10.3k
    if (TIF)
535
42
    {
536
42
        DDFField *field = record->FindField("TIM");
537
42
        if (field == nullptr)
538
0
            return false;
539
540
42
        const DDFFieldDefn *fieldDefn = field->GetFieldDefn();
541
42
        const DDFSubfieldDefn *subfieldDefn =
542
42
            fieldDefn->FindSubfieldDefn("TSI");
543
42
        if (subfieldDefn == nullptr)
544
1
            return false;
545
546
41
        const int nIndexValueWidth = subfieldDefn->GetWidth();
547
548
41
        char offset[30] = {0};
549
        /* Should be strict comparison, but apparently a few datasets */
550
        /* have GetDataSize() greater than the required minimum (#3862) */
551
41
        if (nIndexValueWidth <= 0 ||
552
41
            static_cast<size_t>(nIndexValueWidth) >= sizeof(offset) ||
553
41
            nIndexValueWidth > (INT_MAX - 1) / (NFL * NFC) ||
554
41
            field->GetDataSize() < nIndexValueWidth * NFL * NFC + 1)
555
3
        {
556
3
            return false;
557
3
        }
558
559
38
        try
560
38
        {
561
38
            TILEINDEX = new int[NFL * NFC];
562
38
        }
563
38
        catch (const std::exception &)
564
38
        {
565
0
            return false;
566
0
        }
567
38
        const char *ptr = field->GetData();
568
38
        offset[nIndexValueWidth] = '\0';
569
570
188
        for (int i = 0; i < NFL * NFC; i++)
571
150
        {
572
150
            strncpy(offset, ptr, nIndexValueWidth);
573
150
            ptr += nIndexValueWidth;
574
150
            TILEINDEX[i] = atoi(offset);
575
            // CPLDebug("SRP", "TSI[%d]=%d", i, TILEINDEX[i]);
576
150
        }
577
38
    }
578
579
    /* -------------------------------------------------------------------- */
580
    /*      Open the .IMG file.  Try to recover gracefully if the case      */
581
    /*      of the filename is wrong.                                       */
582
    /* -------------------------------------------------------------------- */
583
10.3k
    const CPLString osDirname = CPLGetDirnameSafe(pszFileName);
584
10.3k
    const CPLString osImgName =
585
10.3k
        CPLFormCIFilenameSafe(osDirname, osBAD, nullptr);
586
587
10.3k
    fdIMG = VSIFOpenL(osImgName, "rb");
588
10.3k
    if (fdIMG == nullptr)
589
92
    {
590
92
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s",
591
92
                 osImgName.c_str());
592
92
        return false;
593
92
    }
594
595
    /* -------------------------------------------------------------------- */
596
    /*      Establish the offset to the first byte of actual image data     */
597
    /*      in the IMG file, skipping the ISO8211 header.                   */
598
    /*                                                                      */
599
    /*      This code is awfully fragile!                                   */
600
    /* -------------------------------------------------------------------- */
601
10.2k
    char c;
602
10.2k
    if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
603
0
    {
604
0
        return false;
605
0
    }
606
4.37M
    while (!VSIFEofL(fdIMG))
607
4.37M
    {
608
4.37M
        if (c == 30)
609
26.8k
        {
610
26.8k
            char recordName[3] = {};
611
26.8k
            if (VSIFReadL(recordName, 1, 3, fdIMG) != 3)
612
3
            {
613
3
                return false;
614
3
            }
615
26.8k
            offsetInIMG += 3;
616
26.8k
            if (STARTS_WITH(recordName, "IMG"))
617
10.2k
            {
618
10.2k
                offsetInIMG += 4;
619
10.2k
                if (VSIFSeekL(fdIMG, 3, SEEK_CUR) != 0)
620
0
                {
621
0
                    return false;
622
0
                }
623
10.2k
                if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
624
1
                {
625
1
                    return false;
626
1
                }
627
2.76M
                while (c != 30)
628
2.75M
                {
629
2.75M
                    offsetInIMG++;
630
2.75M
                    if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
631
13
                    {
632
13
                        return false;
633
13
                    }
634
2.75M
                }
635
10.2k
                offsetInIMG++;
636
10.2k
                break;
637
10.2k
            }
638
26.8k
        }
639
640
4.36M
        offsetInIMG++;
641
4.36M
        if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
642
31
        {
643
31
            return false;
644
31
        }
645
4.36M
    }
646
647
10.2k
    if (VSIFEofL(fdIMG))
648
0
    {
649
0
        return false;
650
0
    }
651
652
10.2k
    CPLDebug("SRP", "Img offset data = %d", offsetInIMG);
653
654
    /* -------------------------------------------------------------------- */
655
    /*      Establish the SRP Dataset.                                     */
656
    /* -------------------------------------------------------------------- */
657
10.2k
    nRasterXSize = NFC * 128;
658
10.2k
    nRasterYSize = NFL * 128;
659
660
10.2k
    char szValue[32] = {};
661
10.2k
    snprintf(szValue, sizeof(szValue), "%d", SCA);
662
10.2k
    SetMetadataItem("SRP_SCA", szValue);
663
664
    // PSP Pixel Spacing, Microns at capture stage {000.0 - 100.0}
665
10.2k
    snprintf(szValue, sizeof(szValue), "%3.1f", PSP);
666
10.2k
    SetMetadataItem("SRP_PSP", szValue);
667
668
10.2k
    nBands = 1;
669
20.4k
    for (int i = 0; i < nBands; i++)
670
10.2k
        SetBand(i + 1, new SRPRasterBand(this, i + 1));
671
672
    /* -------------------------------------------------------------------- */
673
    /*      Try to collect a color map from the .QAL file.                  */
674
    /* -------------------------------------------------------------------- */
675
10.2k
    const CPLString osBasename = CPLGetBasenameSafe(pszFileName);
676
10.2k
    osQALFileName = CPLFormCIFilenameSafe(osDirname, osBasename, "QAL");
677
678
10.2k
    DDFModule oQALModule;
679
680
10.2k
    if (oQALModule.Open(osQALFileName, TRUE))
681
378
    {
682
154k
        while ((record = oQALModule.ReadRecord()) != nullptr)
683
154k
        {
684
154k
            if (record->FindField("COL") != nullptr)
685
136k
            {
686
136k
                const int nColorCount =
687
136k
                    std::min(256, record->FindField("COL")->GetRepeatCount());
688
689
156k
                for (int iColor = 0; iColor < nColorCount; iColor++)
690
124k
                {
691
124k
                    const int nCCD = record->GetIntSubfield("COL", 0, "CCD",
692
124k
                                                            iColor, &bSuccess);
693
124k
                    if (!bSuccess || nCCD < 0 || nCCD > 255)
694
104k
                        break;
695
696
19.9k
                    int nNSR = record->GetIntSubfield("COL", 0, "NSR", iColor);
697
19.9k
                    int nNSG = record->GetIntSubfield("COL", 0, "NSG", iColor);
698
19.9k
                    int nNSB = record->GetIntSubfield("COL", 0, "NSB", iColor);
699
700
19.9k
                    GDALColorEntry sEntry = {static_cast<short>(nNSR),
701
19.9k
                                             static_cast<short>(nNSG),
702
19.9k
                                             static_cast<short>(nNSB), 255};
703
704
19.9k
                    oCT.SetColorEntry(nCCD, &sEntry);
705
19.9k
                }
706
136k
            }
707
708
154k
            if (record->FindField("QUV") != nullptr)
709
141k
            {
710
                // TODO: Translate to English or state why this should not be in
711
                // English.
712
                // Date de production du produit : QAL.QUV.DAT1
713
                // Numero d'edition  du produit : QAL.QUV.EDN
714
715
141k
                const int EDN =
716
141k
                    record->GetIntSubfield("QUV", 0, "EDN", 0, &bSuccess);
717
141k
                if (bSuccess)
718
51.2k
                {
719
51.2k
                    CPLDebug("SRP", "EDN=%d", EDN);
720
51.2k
                    snprintf(szValue, sizeof(szValue), "%d", EDN);
721
51.2k
                    SetMetadataItem("SRP_EDN", szValue);
722
51.2k
                }
723
724
141k
                const char *pszCDV07 =
725
141k
                    record->GetStringSubfield("QUV", 0, "CDV07", 0);
726
141k
                if (pszCDV07 != nullptr)
727
1.63k
                    SetMetadataItem("SRP_CREATIONDATE", pszCDV07);
728
139k
                else
729
139k
                { /*USRP1.2*/
730
139k
                    const char *pszDAT =
731
139k
                        record->GetStringSubfield("QUV", 0, "DAT1", 0);
732
139k
                    if (pszDAT != nullptr && strlen(pszDAT) >= 12)
733
3.21k
                    {
734
3.21k
                        char dat[9];
735
3.21k
                        strncpy(dat, pszDAT + 4, 8);
736
3.21k
                        dat[8] = '\0';
737
3.21k
                        CPLDebug("SRP", "Record DAT %s", dat);
738
3.21k
                        SetMetadataItem("SRP_CREATIONDATE", dat);
739
3.21k
                    }
740
139k
                }
741
742
141k
                const char *pszCDV24 =
743
141k
                    record->GetStringSubfield("QUV", 0, "CDV24", 0);
744
141k
                if (pszCDV24 != nullptr)
745
927
                {
746
927
                    SetMetadataItem("SRP_REVISIONDATE", pszCDV24);
747
927
                }
748
140k
                else
749
140k
                { /*USRP1.2*/
750
140k
                    const char *pszDAT =
751
140k
                        record->GetStringSubfield("QUV", 0, "DAT2", 0);
752
140k
                    if (pszDAT != nullptr && strlen(pszDAT) >= 12)
753
963
                    {
754
963
                        char dat[9];
755
963
                        strncpy(dat, pszDAT + 4, 8);
756
963
                        dat[8] = '\0';
757
963
                        CPLDebug("SRP", "Record DAT %s", dat);
758
963
                        SetMetadataItem("SRP_REVISIONDATE", dat);
759
963
                    }
760
140k
                }
761
762
141k
                const char *pszQSS =
763
141k
                    record->GetStringSubfield("QSR", 0, "QSS", 0);
764
141k
                if (pszQSS != nullptr)
765
564
                    SetMetadataItem("SRP_CLASSIFICATION", pszQSS);
766
141k
            }
767
154k
        }
768
378
    }
769
9.85k
    else
770
9.85k
    {
771
9.85k
        osQALFileName = "";
772
9.85k
        CPLError(CE_Warning, CPLE_AppDefined,
773
9.85k
                 "Unable to find .QAL file, no color table applied.");
774
9.85k
    }
775
776
    /* -------------------------------------------------------------------- */
777
    /*      Derive the coordinate system.                                   */
778
    /* -------------------------------------------------------------------- */
779
10.2k
    if (EQUAL(osProduct, "ASRP"))
780
638
    {
781
638
        m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
782
783
638
        if (ZNA == 9)
784
39
        {
785
39
            m_oSRS.importFromWkt(
786
39
                "PROJCS[\"ARC_System_Zone_09\",GEOGCS[\"GCS_Sphere\","
787
39
                "DATUM[\"D_Sphere\",SPHEROID[\"Sphere\",6378137.0,0.0]],"
788
39
                "PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],"
789
39
                "PROJECTION[\"Azimuthal_Equidistant\"],"
790
39
                "PARAMETER[\"latitude_of_center\",90],"
791
39
                "PARAMETER[\"longitude_of_center\",0],"
792
39
                "PARAMETER[\"false_easting\",0],"
793
39
                "PARAMETER[\"false_northing\",0],"
794
39
                "UNIT[\"metre\",1]]");
795
39
        }
796
797
638
        if (ZNA == 18)
798
0
        {
799
0
            m_oSRS.importFromWkt(
800
0
                "PROJCS[\"ARC_System_Zone_18\",GEOGCS[\"GCS_Sphere\","
801
0
                "DATUM[\"D_Sphere\",SPHEROID[\"Sphere\",6378137.0,0.0]],"
802
0
                "PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],"
803
0
                "PROJECTION[\"Azimuthal_Equidistant\"],"
804
0
                "PARAMETER[\"latitude_of_center\",-90],"
805
0
                "PARAMETER[\"longitude_of_center\",0],"
806
0
                "PARAMETER[\"false_easting\",0],"
807
0
                "PARAMETER[\"false_northing\",0],"
808
0
                "UNIT[\"metre\",1]]");
809
0
        }
810
638
    }
811
9.59k
    else
812
9.59k
    {
813
9.59k
        if (ZNA >= -60 && ZNA <= 60 && ZNA != 0)
814
5.11k
        {
815
5.11k
            m_oSRS.SetUTM(std::abs(ZNA), ZNA > 0);
816
5.11k
            m_oSRS.SetWellKnownGeogCS("WGS84");
817
5.11k
        }
818
4.47k
        else if (ZNA == 61)
819
3
        {
820
3
            m_oSRS.importFromEPSG(32661);  // WGS84 UPS North
821
3
        }
822
4.47k
        else if (ZNA == -61)
823
71
        {
824
71
            m_oSRS.importFromEPSG(32761);  // WGS84 UPS South
825
71
        }
826
9.59k
    }
827
828
10.2k
    snprintf(szValue, sizeof(szValue), "%d", ZNA);
829
10.2k
    SetMetadataItem("SRP_ZNA", szValue);
830
831
10.2k
    return true;
832
10.2k
}
833
834
/************************************************************************/
835
/*                            GetFileList()                             */
836
/************************************************************************/
837
838
char **SRPDataset::GetFileList()
839
840
20.4k
{
841
20.4k
    char **papszFileList = GDALPamDataset::GetFileList();
842
20.4k
    if (!osGENFileName.empty() && !osIMGFileName.empty())
843
20.4k
    {
844
20.4k
        CPLString osMainFilename = GetDescription();
845
20.4k
        VSIStatBufL sStat;
846
847
20.4k
        const bool bMainFileReal = VSIStatL(osMainFilename, &sStat) == 0;
848
20.4k
        if (bMainFileReal)
849
20.4k
        {
850
20.4k
            CPLString osShortMainFilename = CPLGetFilename(osMainFilename);
851
20.4k
            CPLString osShortGENFileName = CPLGetFilename(osGENFileName);
852
20.4k
            if (!EQUAL(osShortMainFilename.c_str(), osShortGENFileName.c_str()))
853
20.4k
                papszFileList =
854
20.4k
                    CSLAddString(papszFileList, osGENFileName.c_str());
855
20.4k
        }
856
0
        else
857
0
        {
858
0
            papszFileList = CSLAddString(papszFileList, osGENFileName.c_str());
859
0
        }
860
861
20.4k
        papszFileList = CSLAddString(papszFileList, osIMGFileName.c_str());
862
863
20.4k
        if (!osQALFileName.empty())
864
756
            papszFileList = CSLAddString(papszFileList, osQALFileName);
865
20.4k
    }
866
20.4k
    return papszFileList;
867
20.4k
}
868
869
/************************************************************************/
870
/*                           AddSubDataset()                            */
871
/************************************************************************/
872
873
void SRPDataset::AddSubDataset(const char *pszGENFileName,
874
                               const char *pszIMGFileName)
875
0
{
876
0
    const int nCount = CSLCount(papszSubDatasets) / 2;
877
878
0
    CPLString osSubDatasetName = "SRP:";
879
0
    osSubDatasetName += pszGENFileName;
880
0
    osSubDatasetName += ",";
881
0
    osSubDatasetName += pszIMGFileName;
882
883
0
    char szName[80];
884
0
    snprintf(szName, sizeof(szName), "SUBDATASET_%d_NAME", nCount + 1);
885
0
    papszSubDatasets =
886
0
        CSLSetNameValue(papszSubDatasets, szName, osSubDatasetName);
887
888
0
    snprintf(szName, sizeof(szName), "SUBDATASET_%d_DESC", nCount + 1);
889
0
    papszSubDatasets =
890
0
        CSLSetNameValue(papszSubDatasets, szName, osSubDatasetName);
891
0
}
892
893
/************************************************************************/
894
/*                            GetMetadata()                             */
895
/************************************************************************/
896
897
char **SRPDataset::GetMetadata(const char *pszDomain)
898
899
10.2k
{
900
10.2k
    if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
901
0
        return papszSubDatasets;
902
903
10.2k
    return GDALPamDataset::GetMetadata(pszDomain);
904
10.2k
}
905
906
/************************************************************************/
907
/*                      FindRecordInGENForIMG()                         */
908
/************************************************************************/
909
910
DDFRecord *SRPDataset::FindRecordInGENForIMG(DDFModule &module,
911
                                             const char *pszGENFileName,
912
                                             const char *pszIMGFileName)
913
702
{
914
    /* Finds the GEN file corresponding to the IMG file */
915
702
    if (!module.Open(pszGENFileName, TRUE))
916
378
        return nullptr;
917
918
324
    CPLString osShortIMGFilename = CPLGetFilename(pszIMGFileName);
919
920
324
    DDFField *field = nullptr;
921
324
    DDFFieldDefn *fieldDefn = nullptr;
922
923
    // Now finds the record.
924
324
    while (true)
925
324
    {
926
324
        CPLPushErrorHandler(CPLQuietErrorHandler);
927
324
        DDFRecord *record = module.ReadRecord();
928
324
        CPLPopErrorHandler();
929
324
        CPLErrorReset();
930
324
        if (record == nullptr)
931
324
            return nullptr;
932
933
0
        if (record->GetFieldCount() >= 5)
934
0
        {
935
0
            field = record->GetField(0);
936
0
            fieldDefn = field->GetFieldDefn();
937
0
            if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
938
0
                  fieldDefn->GetSubfieldCount() == 2))
939
0
            {
940
0
                continue;
941
0
            }
942
943
0
            const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
944
0
            if (RTY == nullptr)
945
0
                continue;
946
            /* Ignore overviews */
947
0
            if (strcmp(RTY, "OVV") == 0)
948
0
                continue;
949
950
0
            if (strcmp(RTY, "GIN") != 0)
951
0
                continue;
952
953
0
            field = record->GetField(3);
954
0
            fieldDefn = field->GetFieldDefn();
955
956
0
            if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
957
0
                  fieldDefn->GetSubfieldCount() == 15))
958
0
            {
959
0
                continue;
960
0
            }
961
962
0
            const char *pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
963
0
            if (pszBAD == nullptr || strlen(pszBAD) != 12)
964
0
                continue;
965
0
            const CPLString osBAD = pszBAD;
966
0
            {
967
0
                char *c = (char *)strchr(osBAD.c_str(), ' ');
968
0
                if (c)
969
0
                    *c = 0;
970
0
            }
971
972
0
            if (EQUAL(osShortIMGFilename.c_str(), osBAD.c_str()))
973
0
            {
974
0
                return record;
975
0
            }
976
0
        }
977
0
    }
978
324
}
979
980
/************************************************************************/
981
/*                           OpenDataset()                              */
982
/************************************************************************/
983
984
SRPDataset *SRPDataset::OpenDataset(const char *pszGENFileName,
985
                                    const char *pszIMGFileName,
986
                                    DDFRecord *record)
987
11.5k
{
988
11.5k
    DDFModule module;  // Don't move this line as it holds ownership of record.
989
990
11.5k
    if (record == nullptr)
991
702
    {
992
702
        record = FindRecordInGENForIMG(module, pszGENFileName, pszIMGFileName);
993
702
        if (record == nullptr)
994
702
            return nullptr;
995
702
    }
996
997
10.8k
    DDFField *field = record->GetField(1);
998
10.8k
    if (field == nullptr)
999
24
        return nullptr;
1000
10.8k
    DDFFieldDefn *fieldDefn = field->GetFieldDefn();
1001
1002
10.8k
    if (!(strcmp(fieldDefn->GetName(), "DSI") == 0 &&
1003
10.8k
          fieldDefn->GetSubfieldCount() == 2))
1004
39
    {
1005
39
        return nullptr;
1006
39
    }
1007
1008
10.7k
    const char *pszPRT = record->GetStringSubfield("DSI", 0, "PRT", 0);
1009
10.7k
    if (pszPRT == nullptr)
1010
2
        return nullptr;
1011
1012
10.7k
    CPLString osPRT = pszPRT;
1013
10.7k
    osPRT.resize(4);
1014
10.7k
    CPLDebug("SRP", "osPRT=%s", osPRT.c_str());
1015
10.7k
    if (!EQUAL(osPRT, "ASRP") && !EQUAL(osPRT, "USRP"))
1016
49
        return nullptr;
1017
1018
10.7k
    const char *pszNAM = record->GetStringSubfield("DSI", 0, "NAM", 0);
1019
10.7k
    if (pszNAM == nullptr)
1020
3
        return nullptr;
1021
1022
10.7k
    const CPLString osNAM = pszNAM;
1023
10.7k
    CPLDebug("SRP", "osNAM=%s", osNAM.c_str());
1024
10.7k
    if (strlen(pszNAM) != 8)
1025
10.7k
    {
1026
10.7k
        CPLDebug("SRP", "Name Size=%d", (int)strlen(pszNAM));
1027
10.7k
    }
1028
1029
10.7k
    SRPDataset *poDS = new SRPDataset();
1030
1031
10.7k
    poDS->osProduct = osPRT;
1032
10.7k
    poDS->osGENFileName = pszGENFileName;
1033
10.7k
    poDS->osIMGFileName = pszIMGFileName;
1034
1035
10.7k
    poDS->SetMetadataItem("SRP_NAM", osNAM);
1036
10.7k
    poDS->SetMetadataItem("SRP_PRODUCT", osPRT);
1037
1038
10.7k
    if (!poDS->GetFromRecord(pszGENFileName, record))
1039
492
    {
1040
492
        delete poDS;
1041
492
        return nullptr;
1042
492
    }
1043
1044
10.2k
    return poDS;
1045
10.7k
}
1046
1047
/************************************************************************/
1048
/*                          GetGENListFromTHF()                         */
1049
/************************************************************************/
1050
1051
char **SRPDataset::GetGENListFromTHF(const char *pszFileName)
1052
142
{
1053
142
    DDFModule module;
1054
142
    DDFRecord *record = nullptr;
1055
142
    DDFField *field = nullptr;
1056
142
    DDFFieldDefn *fieldDefn = nullptr;
1057
142
    int nFilenames = 0;
1058
1059
142
    char **papszFileNames = nullptr;
1060
142
    if (!module.Open(pszFileName, TRUE))
1061
104
        return papszFileNames;
1062
1063
38
    CPLString osDirName(CPLGetDirnameSafe(pszFileName));
1064
1065
38
    while (true)
1066
38
    {
1067
38
        CPLPushErrorHandler(CPLQuietErrorHandler);
1068
38
        record = module.ReadRecord();
1069
38
        CPLPopErrorHandler();
1070
38
        CPLErrorReset();
1071
38
        if (record == nullptr)
1072
38
            break;
1073
0
        if (record->GetFieldCount() > 2)
1074
0
        {
1075
0
            field = record->GetField(0);
1076
0
            fieldDefn = field->GetFieldDefn();
1077
0
            if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
1078
0
                  fieldDefn->GetSubfieldCount() == 2))
1079
0
            {
1080
0
                continue;
1081
0
            }
1082
1083
0
            const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
1084
0
            if (RTY == nullptr)
1085
0
            {
1086
0
                continue;
1087
0
            }
1088
1089
0
            if (strcmp(RTY, "THF") == 0)
1090
0
            {
1091
0
                field = record->GetField(1);
1092
0
                fieldDefn = field->GetFieldDefn();
1093
0
                if (!(strcmp(fieldDefn->GetName(), "VDR") == 0 &&
1094
0
                      fieldDefn->GetSubfieldCount() == 8))
1095
0
                {
1096
0
                    continue;
1097
0
                }
1098
1099
0
                int iFDRFieldInstance = 0;
1100
0
                for (int i = 2; i < record->GetFieldCount(); i++)
1101
0
                {
1102
0
                    field = record->GetField(i);
1103
0
                    fieldDefn = field->GetFieldDefn();
1104
1105
0
                    if (!(strcmp(fieldDefn->GetName(), "FDR") == 0 &&
1106
0
                          fieldDefn->GetSubfieldCount() == 7))
1107
0
                    {
1108
0
                        CPLDebug("SRP", "Record FDR  %d",
1109
0
                                 fieldDefn->GetSubfieldCount());
1110
0
                        continue;
1111
0
                    }
1112
1113
0
                    const char *pszNAM = record->GetStringSubfield(
1114
0
                        "FDR", iFDRFieldInstance++, "NAM", 0);
1115
0
                    if (pszNAM == nullptr)
1116
0
                        continue;
1117
1118
0
                    CPLString osName = CPLString(pszNAM);
1119
1120
                    /* Define a subdirectory from Dataset but with only 6
1121
                     * characters */
1122
0
                    CPLString osDirDataset = pszNAM;
1123
0
                    osDirDataset.resize(6);
1124
0
                    CPLString osDatasetDir = CPLFormFilenameSafe(
1125
0
                        osDirName.c_str(), osDirDataset.c_str(), nullptr);
1126
1127
0
                    CPLString osGENFileName = "";
1128
1129
0
                    int bFound = 0;
1130
1131
0
                    {
1132
0
                        char **papszDirContent =
1133
0
                            VSIReadDir(osDatasetDir.c_str());
1134
0
                        char **ptrDir = papszDirContent;
1135
0
                        if (ptrDir)
1136
0
                        {
1137
0
                            while (*ptrDir)
1138
0
                            {
1139
0
                                if (EQUAL(CPLGetExtensionSafe(*ptrDir).c_str(),
1140
0
                                          "GEN"))
1141
0
                                {
1142
0
                                    bFound = 1;
1143
0
                                    osGENFileName = CPLFormFilenameSafe(
1144
0
                                        osDatasetDir.c_str(), *ptrDir, nullptr);
1145
0
                                    CPLDebug("SRP",
1146
0
                                             "Building GEN full file name : %s",
1147
0
                                             osGENFileName.c_str());
1148
0
                                    break;
1149
0
                                }
1150
0
                                ptrDir++;
1151
0
                            }
1152
0
                            CSLDestroy(papszDirContent);
1153
0
                        }
1154
0
                    }
1155
1156
                    /* If not found in sub directory then search in the same
1157
                     * directory of the THF file */
1158
0
                    if (bFound == 0)
1159
0
                    {
1160
0
                        char **papszDirContent = VSIReadDir(osDirName.c_str());
1161
0
                        char **ptrDir = papszDirContent;
1162
0
                        if (ptrDir)
1163
0
                        {
1164
0
                            while (*ptrDir)
1165
0
                            {
1166
0
                                if (EQUAL(CPLGetExtensionSafe(*ptrDir).c_str(),
1167
0
                                          "GEN") &&
1168
0
                                    EQUALN(CPLGetBasenameSafe(*ptrDir).c_str(),
1169
0
                                           osName, 6))
1170
0
                                {
1171
0
                                    bFound = 1;
1172
0
                                    osGENFileName = CPLFormFilenameSafe(
1173
0
                                        osDirName.c_str(), *ptrDir, nullptr);
1174
0
                                    CPLDebug("SRP",
1175
0
                                             "Building GEN full file name : %s",
1176
0
                                             osGENFileName.c_str());
1177
0
                                    break;
1178
0
                                }
1179
0
                                ptrDir++;
1180
0
                            }
1181
0
                            CSLDestroy(papszDirContent);
1182
0
                        }
1183
0
                    }
1184
1185
0
                    if (bFound == 1)
1186
0
                    {
1187
0
                        papszFileNames = (char **)CPLRealloc(
1188
0
                            papszFileNames, sizeof(char *) * (nFilenames + 2));
1189
0
                        papszFileNames[nFilenames] =
1190
0
                            CPLStrdup(osGENFileName.c_str());
1191
0
                        papszFileNames[nFilenames + 1] = nullptr;
1192
0
                        nFilenames++;
1193
0
                    }
1194
0
                }
1195
0
            }
1196
0
        }
1197
0
    }
1198
38
    return papszFileNames;
1199
142
}
1200
1201
/************************************************************************/
1202
/*                          AddMetadatafromFromTHF()                         */
1203
/************************************************************************/
1204
1205
void SRPDataset::AddMetadatafromFromTHF(const char *pszFileName)
1206
0
{
1207
0
    DDFModule module;
1208
0
    DDFRecord *record = nullptr;
1209
0
    DDFField *field = nullptr;
1210
0
    DDFFieldDefn *fieldDefn = nullptr;
1211
1212
0
    int bSuccess = 0;
1213
0
    if (!module.Open(pszFileName, TRUE))
1214
0
        return;
1215
1216
0
    while (true)
1217
0
    {
1218
0
        CPLPushErrorHandler(CPLQuietErrorHandler);
1219
0
        record = module.ReadRecord();
1220
0
        CPLPopErrorHandler();
1221
0
        CPLErrorReset();
1222
0
        if (record == nullptr || record->GetFieldCount() <= 2)
1223
0
            break;
1224
1225
0
        field = record->GetField(0);
1226
0
        fieldDefn = field->GetFieldDefn();
1227
0
        if (!(strcmp(fieldDefn->GetName(), "001") == 0) ||
1228
0
            fieldDefn->GetSubfieldCount() != 2)
1229
0
            break;
1230
1231
0
        const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
1232
0
        if (RTY != nullptr && strcmp(RTY, "THF") == 0)
1233
0
        {
1234
0
            field = record->GetField(1);
1235
0
            fieldDefn = field->GetFieldDefn();
1236
0
            if ((strcmp(fieldDefn->GetName(), "VDR") == 0 &&
1237
0
                 fieldDefn->GetSubfieldCount() == 8))
1238
0
            {
1239
1240
0
                const char *pszVOO =
1241
0
                    record->GetStringSubfield("VDR", 0, "VOO", 0);
1242
0
                if (pszVOO != nullptr)
1243
0
                {
1244
0
                    CPLDebug("SRP", "Record VOO %s", pszVOO);
1245
0
                    SetMetadataItem("SRP_VOO", pszVOO);
1246
0
                }
1247
1248
0
                int EDN = record->GetIntSubfield("VDR", 0, "EDN", 0, &bSuccess);
1249
0
                if (bSuccess)
1250
0
                {
1251
0
                    CPLDebug("SRP", "Record EDN %d", EDN);
1252
0
                    char szValue[5];
1253
0
                    snprintf(szValue, sizeof(szValue), "%d", EDN);
1254
0
                    SetMetadataItem("SRP_EDN", szValue);
1255
0
                }
1256
1257
0
                const char *pszCDV07 =
1258
0
                    record->GetStringSubfield("VDR", 0, "CDV07", 0);
1259
0
                if (pszCDV07 != nullptr)
1260
0
                {
1261
0
                    CPLDebug("SRP", "Record pszCDV07 %s", pszCDV07);
1262
0
                    SetMetadataItem("SRP_CREATIONDATE", pszCDV07);
1263
0
                }
1264
0
                else
1265
0
                { /*USRP1.2*/
1266
0
                    const char *pszDAT =
1267
0
                        record->GetStringSubfield("VDR", 0, "DAT", 0);
1268
0
                    if (pszDAT != nullptr)
1269
0
                    {
1270
0
                        char dat[9];
1271
0
                        strncpy(dat, pszDAT + 4, 8);
1272
0
                        dat[8] = '\0';
1273
0
                        CPLDebug("SRP", "Record DAT %s", dat);
1274
0
                        SetMetadataItem("SRP_CREATIONDATE", dat);
1275
0
                    }
1276
0
                }
1277
0
            }
1278
0
        } /* End of THF part */
1279
1280
0
        if (RTY != nullptr && strcmp(RTY, "LCF") == 0)
1281
0
        {
1282
0
            field = record->GetField(1);
1283
0
            fieldDefn = field->GetFieldDefn();
1284
0
            if ((strcmp(fieldDefn->GetName(), "QSR") == 0 &&
1285
0
                 fieldDefn->GetSubfieldCount() == 4))
1286
0
            {
1287
1288
0
                const char *pszQSS =
1289
0
                    record->GetStringSubfield("QSR", 0, "QSS", 0);
1290
0
                if (pszQSS != nullptr)
1291
0
                {
1292
0
                    CPLDebug("SRP", "Record Classification %s", pszQSS);
1293
0
                    SetMetadataItem("SRP_CLASSIFICATION", pszQSS);
1294
0
                }
1295
0
            }
1296
1297
0
            field = record->GetField(2);
1298
0
            fieldDefn = field->GetFieldDefn();
1299
0
            if ((strcmp(fieldDefn->GetName(), "QUV") == 0 &&
1300
0
                 fieldDefn->GetSubfieldCount() == 6))
1301
0
            {
1302
0
                const char *pszSRC2 =
1303
0
                    record->GetStringSubfield("QUV", 0, "SRC1", 0);
1304
0
                if (pszSRC2 != nullptr)
1305
0
                {
1306
0
                    SetMetadataItem("SRP_PRODUCTVERSION", pszSRC2);
1307
0
                }
1308
0
                else
1309
0
                {
1310
0
                    const char *pszSRC =
1311
0
                        record->GetStringSubfield("QUV", 0, "SRC", 0);
1312
0
                    if (pszSRC != nullptr)
1313
0
                    {
1314
0
                        SetMetadataItem("SRP_PRODUCTVERSION", pszSRC);
1315
0
                    }
1316
0
                }
1317
0
            }
1318
0
        } /* End of LCF part */
1319
0
    }
1320
0
}
1321
1322
/************************************************************************/
1323
/*                          GetIMGListFromGEN()                         */
1324
/************************************************************************/
1325
1326
char **SRPDataset::GetIMGListFromGEN(const char *pszFileName,
1327
                                     int *pnRecordIndex)
1328
0
{
1329
0
    DDFRecord *record = nullptr;
1330
0
    DDFField *field = nullptr;
1331
0
    DDFFieldDefn *fieldDefn = nullptr;
1332
0
    int nFilenames = 0;
1333
0
    char **papszFileNames = nullptr;
1334
0
    int nRecordIndex = -1;
1335
1336
0
    if (pnRecordIndex)
1337
0
        *pnRecordIndex = -1;
1338
1339
0
    DDFModule module;
1340
0
    if (!module.Open(pszFileName, TRUE))
1341
0
        return nullptr;
1342
1343
0
    while (true)
1344
0
    {
1345
0
        nRecordIndex++;
1346
1347
0
        CPLPushErrorHandler(CPLQuietErrorHandler);
1348
0
        record = module.ReadRecord();
1349
0
        CPLPopErrorHandler();
1350
0
        CPLErrorReset();
1351
0
        if (record == nullptr)
1352
0
            break;
1353
1354
0
        if (record->GetFieldCount() >= 5)
1355
0
        {
1356
0
            field = record->GetField(0);
1357
0
            fieldDefn = field->GetFieldDefn();
1358
0
            if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
1359
0
                  fieldDefn->GetSubfieldCount() == 2))
1360
0
            {
1361
0
                continue;
1362
0
            }
1363
1364
0
            const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
1365
0
            if (RTY == nullptr)
1366
0
                continue;
1367
            /* Ignore overviews */
1368
0
            if (strcmp(RTY, "OVV") == 0)
1369
0
                continue;
1370
1371
0
            if (strcmp(RTY, "GIN") != 0)
1372
0
                continue;
1373
1374
            /* make sure that the GEN file is part of a SRP dataset, not an ADRG
1375
             * dataset, by checking that the GEN field does not contain a NOW
1376
             * subfield */
1377
0
            const char *NWO = record->GetStringSubfield("GEN", 0, "NWO", 0);
1378
0
            if (NWO)
1379
0
            {
1380
0
                CSLDestroy(papszFileNames);
1381
0
                return nullptr;
1382
0
            }
1383
1384
0
            field = record->GetField(3);
1385
0
            if (field == nullptr)
1386
0
                continue;
1387
0
            fieldDefn = field->GetFieldDefn();
1388
1389
0
            if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
1390
0
                  fieldDefn->GetSubfieldCount() == 15))
1391
0
            {
1392
0
                continue;
1393
0
            }
1394
1395
0
            const char *pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
1396
0
            if (pszBAD == nullptr || strlen(pszBAD) != 12)
1397
0
                continue;
1398
0
            std::string osBAD = pszBAD;
1399
0
            {
1400
0
                char *c = (char *)strchr(osBAD.c_str(), ' ');
1401
0
                if (c)
1402
0
                    *c = 0;
1403
0
            }
1404
0
            CPLDebug("SRP", "BAD=%s", osBAD.c_str());
1405
1406
            /* Build full IMG file name from BAD value */
1407
0
            const CPLString osGENDir(CPLGetDirnameSafe(pszFileName));
1408
1409
0
            std::string osFileName =
1410
0
                CPLFormFilenameSafe(osGENDir.c_str(), osBAD.c_str(), nullptr);
1411
0
            VSIStatBufL sStatBuf;
1412
0
            if (VSIStatL(osFileName.c_str(), &sStatBuf) == 0)
1413
0
            {
1414
0
                osBAD = std::move(osFileName);
1415
0
                CPLDebug("SRP", "Building IMG full file name : %s",
1416
0
                         osBAD.c_str());
1417
0
            }
1418
0
            else
1419
0
            {
1420
0
                char **papszDirContent = nullptr;
1421
0
                if (strcmp(osGENDir.c_str(), "/vsimem") == 0)
1422
0
                {
1423
0
                    CPLString osTmp = osGENDir + "/";
1424
0
                    papszDirContent = VSIReadDir(osTmp);
1425
0
                }
1426
0
                else
1427
0
                    papszDirContent = VSIReadDir(osGENDir);
1428
0
                char **ptrDir = papszDirContent;
1429
0
                while (ptrDir && *ptrDir)
1430
0
                {
1431
0
                    if (EQUAL(*ptrDir, osBAD.c_str()))
1432
0
                    {
1433
0
                        osBAD = CPLFormFilenameSafe(osGENDir.c_str(), *ptrDir,
1434
0
                                                    nullptr);
1435
0
                        CPLDebug("SRP", "Building IMG full file name : %s",
1436
0
                                 osBAD.c_str());
1437
0
                        break;
1438
0
                    }
1439
0
                    ptrDir++;
1440
0
                }
1441
0
                CSLDestroy(papszDirContent);
1442
0
            }
1443
1444
0
            if (nFilenames == 0 && pnRecordIndex)
1445
0
                *pnRecordIndex = nRecordIndex;
1446
1447
0
            papszFileNames = (char **)CPLRealloc(
1448
0
                papszFileNames, sizeof(char *) * (nFilenames + 2));
1449
0
            papszFileNames[nFilenames] = CPLStrdup(osBAD.c_str());
1450
0
            papszFileNames[nFilenames + 1] = nullptr;
1451
0
            nFilenames++;
1452
0
        }
1453
0
    }
1454
1455
0
    return papszFileNames;
1456
0
}
1457
1458
/************************************************************************/
1459
/*                                Open()                                */
1460
/************************************************************************/
1461
1462
GDALDataset *SRPDataset::Open(GDALOpenInfo *poOpenInfo)
1463
846k
{
1464
846k
    int nRecordIndex = -1;
1465
846k
    CPLString osGENFileName;
1466
846k
    CPLString osIMGFileName;
1467
846k
    int bFromSubdataset = FALSE;
1468
846k
    int bTHFWithSingleGEN = FALSE;
1469
1470
846k
    if (STARTS_WITH_CI(poOpenInfo->pszFilename, "SRP:"))
1471
0
    {
1472
0
        char **papszTokens =
1473
0
            CSLTokenizeString2(poOpenInfo->pszFilename + 4, ",", 0);
1474
0
        if (CSLCount(papszTokens) == 2)
1475
0
        {
1476
0
            osGENFileName = papszTokens[0];
1477
0
            osIMGFileName = papszTokens[1];
1478
0
            bFromSubdataset = TRUE;
1479
0
        }
1480
0
        CSLDestroy(papszTokens);
1481
0
    }
1482
846k
    else
1483
846k
    {
1484
846k
        if (poOpenInfo->nHeaderBytes < 500)
1485
784k
            return nullptr;
1486
61.7k
        CPLString osFileName(poOpenInfo->pszFilename);
1487
1488
61.7k
        if (EQUAL(CPLGetExtensionSafe(osFileName.c_str()).c_str(), "THF"))
1489
142
        {
1490
1491
142
            CPLDebug("SRP", "Read THF");
1492
1493
142
            char **papszFileNames = GetGENListFromTHF(osFileName.c_str());
1494
142
            if (papszFileNames == nullptr)
1495
142
                return nullptr;
1496
0
            if (papszFileNames[1] == nullptr &&
1497
0
                CPLTestBool(CPLGetConfigOption(
1498
0
                    "SRP_SINGLE_GEN_IN_THF_AS_DATASET", "TRUE")))
1499
0
            {
1500
0
                osFileName = papszFileNames[0];
1501
0
                CSLDestroy(papszFileNames);
1502
0
                bTHFWithSingleGEN = TRUE;
1503
0
            }
1504
0
            else
1505
0
            {
1506
0
                char **ptr = papszFileNames;
1507
0
                SRPDataset *poDS = new SRPDataset();
1508
0
                poDS->AddMetadatafromFromTHF(osFileName.c_str());
1509
0
                while (*ptr)
1510
0
                {
1511
0
                    char **papszIMGFileNames = GetIMGListFromGEN(*ptr);
1512
0
                    char **papszIMGIter = papszIMGFileNames;
1513
0
                    while (papszIMGIter && *papszIMGIter)
1514
0
                    {
1515
0
                        poDS->AddSubDataset(*ptr, *papszIMGIter);
1516
0
                        papszIMGIter++;
1517
0
                    }
1518
0
                    CSLDestroy(papszIMGFileNames);
1519
1520
0
                    ptr++;
1521
0
                }
1522
0
                CSLDestroy(papszFileNames);
1523
0
                return poDS;
1524
0
            }
1525
0
        }
1526
1527
61.6k
        if (bTHFWithSingleGEN
1528
#ifdef OPEN_GEN
1529
            || EQUAL(CPLGetExtensionSafe(osFileName.c_str()).c_str(), "GEN")
1530
#endif
1531
61.6k
        )
1532
0
        {
1533
0
            osGENFileName = osFileName;
1534
1535
0
            char **papszFileNames =
1536
0
                GetIMGListFromGEN(osFileName.c_str(), &nRecordIndex);
1537
0
            if (papszFileNames == nullptr)
1538
0
                return nullptr;
1539
0
            if (papszFileNames[1] == nullptr)
1540
0
            {
1541
0
                osIMGFileName = papszFileNames[0];
1542
0
                CSLDestroy(papszFileNames);
1543
0
            }
1544
0
            else
1545
0
            {
1546
0
                char **ptr = papszFileNames;
1547
0
                SRPDataset *poDS = new SRPDataset();
1548
0
                while (*ptr)
1549
0
                {
1550
0
                    poDS->AddSubDataset(osFileName.c_str(), *ptr);
1551
0
                    ptr++;
1552
0
                }
1553
0
                CSLDestroy(papszFileNames);
1554
0
                return poDS;
1555
0
            }
1556
0
        }
1557
1558
61.6k
        if (EQUAL(CPLGetExtensionSafe(osFileName.c_str()).c_str(), "IMG"))
1559
11.6k
        {
1560
1561
11.6k
            osIMGFileName = osFileName;
1562
1563
11.6k
            constexpr int nLeaderSize = 24;
1564
1565
11.6k
            int i = 0;  // Used after for.
1566
290k
            for (; i < nLeaderSize; i++)
1567
278k
            {
1568
278k
                if (poOpenInfo->pabyHeader[i] < 32 ||
1569
278k
                    poOpenInfo->pabyHeader[i] > 126)
1570
102
                    return nullptr;
1571
278k
            }
1572
1573
11.5k
            if (poOpenInfo->pabyHeader[5] != '1' &&
1574
11.5k
                poOpenInfo->pabyHeader[5] != '2' &&
1575
11.5k
                poOpenInfo->pabyHeader[5] != '3')
1576
25
                return nullptr;
1577
1578
11.5k
            if (poOpenInfo->pabyHeader[6] != 'L')
1579
5
                return nullptr;
1580
11.5k
            if (poOpenInfo->pabyHeader[8] != '1' &&
1581
11.5k
                poOpenInfo->pabyHeader[8] != ' ')
1582
2
                return nullptr;
1583
1584
            // --------------------------------------------------------------------
1585
            //      Find and open the .GEN file.
1586
            // --------------------------------------------------------------------
1587
11.5k
            VSIStatBufL sStatBuf;
1588
1589
11.5k
            CPLString basename = CPLGetBasenameSafe(osFileName);
1590
11.5k
            if (basename.size() != 8)
1591
0
            {
1592
0
                CPLDebug("SRP", "Invalid basename file");
1593
0
                return nullptr;
1594
0
            }
1595
1596
11.5k
            nRecordIndex = static_cast<int>(CPLScanLong(basename + 6, 2));
1597
1598
11.5k
            CPLString path = CPLGetDirnameSafe(osFileName);
1599
11.5k
            CPLString basename01 = ResetTo01(basename);
1600
11.5k
            osFileName = CPLFormFilenameSafe(path, basename01, ".IMG");
1601
1602
11.5k
            osFileName = CPLResetExtensionSafe(osFileName, "GEN");
1603
11.5k
            if (VSIStatL(osFileName, &sStatBuf) != 0)
1604
15
            {
1605
15
                osFileName = CPLResetExtensionSafe(osFileName, "gen");
1606
15
                if (VSIStatL(osFileName, &sStatBuf) != 0)
1607
13
                    return nullptr;
1608
15
            }
1609
1610
11.5k
            osGENFileName = std::move(osFileName);
1611
11.5k
        }
1612
61.6k
    }
1613
1614
61.4k
    if (!osGENFileName.empty() && !osIMGFileName.empty())
1615
11.5k
    {
1616
1617
11.5k
        if (poOpenInfo->eAccess == GA_Update)
1618
0
        {
1619
0
            ReportUpdateNotSupportedByDriver("SRP");
1620
0
            return nullptr;
1621
0
        }
1622
1623
11.5k
        DDFModule module;
1624
11.5k
        DDFRecord *record = nullptr;
1625
11.5k
        if (nRecordIndex >= 0 && module.Open(osGENFileName.c_str(), TRUE))
1626
11.1k
        {
1627
22.0k
            for (int i = 0; i < nRecordIndex; i++)
1628
11.1k
            {
1629
11.1k
                CPLPushErrorHandler(CPLQuietErrorHandler);
1630
11.1k
                record = module.ReadRecord();
1631
11.1k
                CPLPopErrorHandler();
1632
11.1k
                CPLErrorReset();
1633
11.1k
                if (record == nullptr)
1634
324
                    break;
1635
11.1k
            }
1636
11.1k
        }
1637
11.5k
        SRPDataset *poDS =
1638
11.5k
            OpenDataset(osGENFileName.c_str(), osIMGFileName.c_str(), record);
1639
1640
11.5k
        if (poDS)
1641
10.2k
        {
1642
            /* ---------------------------------------------------------- */
1643
            /*      Initialize any PAM information.                       */
1644
            /* ---------------------------------------------------------- */
1645
10.2k
            poDS->SetDescription(poOpenInfo->pszFilename);
1646
10.2k
            poDS->TryLoadXML();
1647
1648
            /* ---------------------------------------------------------- */
1649
            /*      Check for external overviews.                         */
1650
            /* ---------------------------------------------------------- */
1651
10.2k
            if (bFromSubdataset)
1652
0
                poDS->oOvManager.Initialize(poDS, osIMGFileName.c_str());
1653
10.2k
            else
1654
10.2k
                poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
1655
1656
10.2k
            return poDS;
1657
10.2k
        }
1658
11.5k
    }
1659
1660
51.2k
    return nullptr;
1661
61.4k
}
1662
1663
/************************************************************************/
1664
/*                         GDALRegister_SRP()                          */
1665
/************************************************************************/
1666
1667
void GDALRegister_SRP()
1668
1669
26
{
1670
26
    if (GDALGetDriverByName("SRP") != nullptr)
1671
0
        return;
1672
1673
26
    GDALDriver *poDriver = new GDALDriver();
1674
1675
26
    poDriver->SetDescription("SRP");
1676
26
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1677
26
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1678
26
                              "Standard Raster Product (ASRP/USRP)");
1679
26
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/srp.html");
1680
26
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "img");
1681
26
    poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
1682
26
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1683
1684
26
    poDriver->pfnOpen = SRPDataset::Open;
1685
1686
26
    GetGDALDriverManager()->RegisterDriver(poDriver);
1687
26
}