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