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