Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: MiraMonRaster driver
4 : * Purpose: Implements MMRBand class: This class manages the metadata of each
5 : * band to be processed. It is useful for maintaining a list of bands
6 : * and for determining the number of subdatasets that need to be
7 : * generated.
8 : * Author: Abel Pau
9 : *
10 : ******************************************************************************
11 : * Copyright (c) 2025, Xavier Pons
12 : *
13 : * SPDX-License-Identifier: MIT
14 : ****************************************************************************/
15 : #include <algorithm>
16 :
17 : #include "miramon_rel.h"
18 : #include "miramon_band.h"
19 :
20 : #include "../miramon_common/mm_gdal_driver_structs.h" // For SECTION_ATTRIBUTE_DATA
21 :
22 : /************************************************************************/
23 : /* MMRBand() */
24 : /************************************************************************/
25 87 : MMRBand::MMRBand(MMRRel &fRel, const CPLString &osBandSectionIn)
26 : : m_pfRel(&fRel), m_nWidth(0), m_nHeight(0),
27 87 : m_osBandSection(osBandSectionIn)
28 :
29 : {
30 : // Getting band and band file name from metadata.
31 87 : CPLString osNomFitxer;
32 87 : osNomFitxer = SECTION_ATTRIBUTE_DATA;
33 87 : osNomFitxer.append(":");
34 87 : osNomFitxer.append(osBandSectionIn);
35 87 : if (!m_pfRel->GetMetadataValue(osNomFitxer, KEY_NomFitxer,
36 116 : m_osRawBandFileName) ||
37 29 : m_osRawBandFileName.empty())
38 : {
39 : // A band name may be empty only if it is the only band present
40 : // in the REL file. Otherwise, inferring the band name from the
41 : // REL filename is considered an error.
42 : // Consequently, for a REL file containing exactly one band, if
43 : // the band name is empty, it shall be inferred from the REL
44 : // filename.
45 : // Example: REL: testI.rel --> IMG: test.img
46 60 : if (m_pfRel->GetNBands() >= 1)
47 0 : m_osBandFileName = "";
48 : else
49 : {
50 : m_osBandFileName =
51 60 : m_pfRel->MMRGetFileNameFromRelName(m_pfRel->GetRELName());
52 : }
53 :
54 60 : if (m_osBandFileName.empty())
55 : {
56 0 : m_nWidth = 0;
57 0 : m_nHeight = 0;
58 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
59 : "The REL file '%s' contains a documented \
60 : band with no explicit or wrong name. Section [%s] or [%s:%s].",
61 0 : m_pfRel->GetRELNameChar(), SECTION_ATTRIBUTE_DATA,
62 : SECTION_ATTRIBUTE_DATA, m_osBandSection.c_str());
63 0 : return;
64 : }
65 60 : m_osBandName = CPLGetBasenameSafe(m_osBandFileName);
66 60 : m_osRawBandFileName = m_osBandName;
67 : }
68 : else
69 : {
70 27 : m_osBandName = CPLGetBasenameSafe(m_osRawBandFileName);
71 27 : CPLString osAux = CPLGetPathSafe(m_pfRel->GetRELNameChar());
72 : m_osBandFileName =
73 27 : CPLFormFilenameSafe(osAux.c_str(), m_osRawBandFileName.c_str(), "");
74 : }
75 :
76 : // There is a band file documented?
77 87 : if (m_osBandName.empty())
78 : {
79 0 : m_nWidth = 0;
80 0 : m_nHeight = 0;
81 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
82 : "The REL file '%s' contains a documented \
83 : band with no explicit name. Section [%s] or [%s:%s].",
84 0 : m_pfRel->GetRELNameChar(), SECTION_ATTRIBUTE_DATA,
85 : SECTION_ATTRIBUTE_DATA, m_osBandSection.c_str());
86 0 : return;
87 : }
88 :
89 : // Getting essential metadata documented at
90 : // https://www.miramon.cat/new_note/eng/notes/MiraMon_raster_file_format.pdf
91 :
92 : // Getting number of columns and rows
93 87 : if (!UpdateColumnsNumberFromREL(m_osBandSection))
94 : {
95 1 : m_nWidth = 0;
96 1 : m_nHeight = 0;
97 1 : return;
98 : }
99 :
100 86 : if (!UpdateRowsNumberFromREL(m_osBandSection))
101 : {
102 1 : m_nWidth = 0;
103 1 : m_nHeight = 0;
104 1 : return;
105 : }
106 :
107 85 : if (m_nWidth <= 0 || m_nHeight <= 0)
108 : {
109 1 : m_nWidth = 0;
110 1 : m_nHeight = 0;
111 1 : CPLError(CE_Failure, CPLE_AppDefined,
112 : "MMRBand::MMRBand : (nWidth <= 0 || nHeight <= 0)");
113 1 : return;
114 : }
115 :
116 : // Getting data type and compression.
117 : // If error, message given inside.
118 84 : if (!UpdateDataTypeFromREL(m_osBandSection))
119 2 : return;
120 :
121 : // Let's see if there is RLE compression
122 82 : m_bIsCompressed =
123 104 : (((m_eMMDataType >= MMDataType::DATATYPE_AND_COMPR_BYTE_RLE) &&
124 142 : (m_eMMDataType <= MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE)) ||
125 60 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BIT);
126 :
127 : // Getting min and max values
128 82 : UpdateMinMaxValuesFromREL(m_osBandSection);
129 :
130 : // Getting min and max values for simbolization
131 82 : UpdateMinMaxVisuValuesFromREL(m_osBandSection);
132 82 : if (!m_bMinVisuSet)
133 : {
134 77 : if (m_bMinSet)
135 : {
136 74 : m_dfVisuMin = m_dfMin;
137 74 : m_bMinVisuSet = true;
138 : }
139 : }
140 82 : if (!m_bMaxVisuSet)
141 : {
142 77 : if (m_bMaxSet)
143 : {
144 74 : m_dfVisuMax = m_dfMax;
145 74 : m_bMaxVisuSet = true;
146 : }
147 : }
148 :
149 : // Getting the friendly description of the band
150 82 : UpdateFriendlyDescriptionFromREL(m_osBandSection);
151 :
152 : // Getting NoData value and definition
153 82 : UpdateNoDataValue(m_osBandSection);
154 :
155 : // Getting reference system and coordinates of the geographic bounding box
156 82 : UpdateReferenceSystemFromREL();
157 :
158 : // Getting the bounding box: coordinates in the terrain
159 82 : UpdateBoundingBoxFromREL(m_osBandSection);
160 :
161 : // MiraMon IMG files are efficient in going to an specified row.
162 : // So le'ts configurate the blocks as line blocks.
163 82 : m_nBlockXSize = m_nWidth;
164 82 : m_nBlockYSize = 1;
165 82 : m_nNRowsPerBlock = 1;
166 :
167 : // Can the binary file that contains all data for this band be opened?
168 82 : m_pfIMG = VSIFOpenL(m_osBandFileName, "rb");
169 82 : if (!m_pfIMG)
170 : {
171 1 : m_nWidth = 0;
172 1 : m_nHeight = 0;
173 1 : CPLError(CE_Failure, CPLE_OpenFailed,
174 : "Failed to open MiraMon band file `%s' with access 'rb'.",
175 : m_osBandFileName.c_str());
176 1 : return;
177 : }
178 :
179 : // We have a valid MMRBand.
180 81 : m_bIsValid = true;
181 : }
182 :
183 : /************************************************************************/
184 : /* ~MMRBand() */
185 : /************************************************************************/
186 87 : MMRBand::~MMRBand()
187 :
188 : {
189 87 : if (m_pfIMG != nullptr)
190 81 : CPL_IGNORE_RET_VAL(VSIFCloseL(m_pfIMG));
191 87 : }
192 :
193 18 : const CPLString MMRBand::GetRELFileName() const
194 : {
195 18 : if (!m_pfRel)
196 0 : return "";
197 18 : return m_pfRel->GetRELName();
198 : }
199 :
200 : /************************************************************************/
201 : /* GetRasterBlock() */
202 : /************************************************************************/
203 115 : CPLErr MMRBand::GetRasterBlock(int /*nXBlock*/, int nYBlock, void *pData,
204 : int nDataSize)
205 :
206 : {
207 115 : if (nYBlock > INT_MAX / (std::max(1, m_nNRowsPerBlock)))
208 : {
209 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Error in GetRasterBlock");
210 0 : return CE_Failure;
211 : }
212 115 : const int iBlock = nYBlock * m_nNRowsPerBlock;
213 :
214 115 : if (m_nBlockXSize > INT_MAX / (std::max(1, m_nDataTypeSizeBytes)))
215 : {
216 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Error in GetRasterBlock");
217 0 : return CE_Failure;
218 : }
219 :
220 230 : if (m_nBlockYSize >
221 115 : INT_MAX / (std::max(1, m_nDataTypeSizeBytes * m_nBlockXSize)))
222 : {
223 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Error in GetRasterBlock");
224 0 : return CE_Failure;
225 : }
226 :
227 115 : const int nGDALBlockSize =
228 115 : m_nDataTypeSizeBytes * m_nBlockXSize * m_nBlockYSize;
229 :
230 : // Calculate block offset in case we have spill file. Use predefined
231 : // block map otherwise.
232 :
233 115 : if (nDataSize != -1 && nGDALBlockSize > nDataSize)
234 : {
235 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid block size: %d",
236 : nGDALBlockSize);
237 0 : return CE_Failure;
238 : }
239 :
240 : // Getting the row offsets to optimize access.
241 115 : if (FillRowOffsets() == false || m_aFileOffsets.empty())
242 : {
243 0 : CPLError(CE_Failure, CPLE_AppDefined,
244 : "Some error in offsets calculation");
245 0 : return CE_Failure;
246 : }
247 :
248 : // Read the block in the documented or deduced offset
249 115 : if (VSIFSeekL(m_pfIMG, m_aFileOffsets[iBlock], SEEK_SET))
250 : {
251 0 : CPLError(CE_Failure, CPLE_AppDefined,
252 : "Read from invalid offset for grid block.");
253 0 : return CE_Failure;
254 : }
255 :
256 : size_t nCompressedRawSize;
257 115 : if (iBlock == m_nHeight - 1)
258 35 : nCompressedRawSize = SIZE_MAX; // We don't know it
259 : else
260 160 : nCompressedRawSize = static_cast<size_t>(m_aFileOffsets[iBlock + 1] -
261 80 : m_aFileOffsets[iBlock]);
262 :
263 115 : return GetBlockData(pData, nCompressedRawSize);
264 : }
265 :
266 63 : void MMRBand::UpdateGeoTransform()
267 : {
268 63 : m_gt[0] = GetBoundingBoxMinX();
269 63 : m_gt[1] = (GetBoundingBoxMaxX() - m_gt[0]) / GetWidth();
270 63 : m_gt[2] = 0.0; // No rotation in MiraMon rasters
271 63 : m_gt[3] = GetBoundingBoxMaxY();
272 63 : m_gt[4] = 0.0;
273 63 : m_gt[5] = (GetBoundingBoxMinY() - m_gt[3]) / GetHeight();
274 63 : }
275 :
276 : /************************************************************************/
277 : /* Other functions */
278 : /************************************************************************/
279 :
280 : // [ATTRIBUTE_DATA:xxxx] or [OVERVIEW:ASPECTES_TECNICS]
281 173 : bool MMRBand::Get_ATTRIBUTE_DATA_or_OVERVIEW_ASPECTES_TECNICS_int(
282 : const CPLString &osSection, const char *pszKey, int *nValue,
283 : const char *pszErrorMessage)
284 : {
285 173 : if (osSection.empty() || !pszKey || !nValue)
286 0 : return false;
287 :
288 346 : CPLString osValue;
289 173 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, pszKey,
290 173 : osValue) ||
291 0 : osValue.empty())
292 : {
293 173 : if (m_pfRel->GetMetadataValue(SECTION_OVERVIEW,
294 : SECTION_ASPECTES_TECNICS, pszKey,
295 344 : osValue) == false ||
296 171 : osValue.empty())
297 : {
298 2 : if (pszErrorMessage)
299 2 : CPLError(CE_Failure, CPLE_AppDefined, "%s", pszErrorMessage);
300 2 : return false;
301 : }
302 : }
303 :
304 171 : if (1 != sscanf(osValue, "%d", nValue))
305 : {
306 0 : if (pszErrorMessage)
307 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", pszErrorMessage);
308 0 : return false;
309 : }
310 171 : return true;
311 : }
312 :
313 83 : bool MMRBand::GetDataTypeAndBytesPerPixel(const char *pszCompType,
314 : MMDataType *nCompressionType,
315 : MMBytesPerPixel *nBytesPerPixel)
316 : {
317 83 : if (!nCompressionType || !nBytesPerPixel || !pszCompType)
318 0 : return false;
319 :
320 83 : if (EQUAL(pszCompType, "bit"))
321 : {
322 2 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_BIT;
323 2 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_BYTE_I_RLE;
324 2 : return true;
325 : }
326 81 : if (EQUAL(pszCompType, "byte"))
327 : {
328 43 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_BYTE;
329 43 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_BYTE_I_RLE;
330 43 : return true;
331 : }
332 38 : if (EQUAL(pszCompType, "byte-RLE"))
333 : {
334 10 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_BYTE_RLE;
335 10 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_BYTE_I_RLE;
336 10 : return true;
337 : }
338 28 : if (EQUAL(pszCompType, "integer"))
339 : {
340 2 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_INTEGER;
341 2 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
342 2 : return true;
343 : }
344 26 : if (EQUAL(pszCompType, "integer-RLE"))
345 : {
346 2 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE;
347 2 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
348 2 : return true;
349 : }
350 24 : if (EQUAL(pszCompType, "uinteger"))
351 : {
352 4 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_UINTEGER;
353 4 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
354 4 : return true;
355 : }
356 20 : if (EQUAL(pszCompType, "uinteger-RLE"))
357 : {
358 2 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE;
359 2 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
360 2 : return true;
361 : }
362 18 : if (EQUAL(pszCompType, "long"))
363 : {
364 3 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_LONG;
365 3 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
366 3 : return true;
367 : }
368 15 : if (EQUAL(pszCompType, "long-RLE"))
369 : {
370 2 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_LONG_RLE;
371 2 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
372 2 : return true;
373 : }
374 13 : if (EQUAL(pszCompType, "real"))
375 : {
376 4 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_REAL;
377 4 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
378 4 : return true;
379 : }
380 9 : if (EQUAL(pszCompType, "real-RLE"))
381 : {
382 2 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_REAL_RLE;
383 2 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
384 2 : return true;
385 : }
386 7 : if (EQUAL(pszCompType, "double"))
387 : {
388 2 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_DOUBLE;
389 2 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_DOUBLE_I_RLE;
390 2 : return true;
391 : }
392 5 : if (EQUAL(pszCompType, "double-RLE"))
393 : {
394 4 : *nCompressionType = MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE;
395 4 : *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_DOUBLE_I_RLE;
396 4 : return true;
397 : }
398 :
399 1 : return false;
400 : }
401 :
402 : // Getting data type from metadata
403 84 : bool MMRBand::UpdateDataTypeFromREL(const CPLString osSection)
404 : {
405 84 : m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_UNDEFINED;
406 84 : m_eMMBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_UNDEFINED;
407 :
408 168 : CPLString osValue;
409 84 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
410 168 : "TipusCompressio", osValue) ||
411 84 : osValue.empty())
412 : {
413 1 : m_nWidth = 0;
414 1 : m_nHeight = 0;
415 1 : CPLError(CE_Failure, CPLE_AppDefined,
416 : "MiraMonRaster: no nDataType documented");
417 1 : return false;
418 : }
419 :
420 83 : if (!GetDataTypeAndBytesPerPixel(osValue.c_str(), &m_eMMDataType,
421 : &m_eMMBytesPerPixel))
422 : {
423 1 : m_nWidth = 0;
424 1 : m_nHeight = 0;
425 1 : CPLError(CE_Failure, CPLE_AppDefined,
426 : "MiraMonRaster: data type unhandled");
427 1 : return false;
428 : }
429 :
430 82 : m_nDataTypeSizeBytes = std::max(1, static_cast<int>(m_eMMBytesPerPixel));
431 82 : return true;
432 : }
433 :
434 : // Getting number of columns from metadata
435 87 : bool MMRBand::UpdateColumnsNumberFromREL(const CPLString &osSection)
436 : {
437 87 : return Get_ATTRIBUTE_DATA_or_OVERVIEW_ASPECTES_TECNICS_int(
438 : osSection, "columns", &m_nWidth,
439 87 : "MMRBand::MMRBand : No number of columns documented");
440 : }
441 :
442 86 : bool MMRBand::UpdateRowsNumberFromREL(const CPLString &osSection)
443 : {
444 86 : return Get_ATTRIBUTE_DATA_or_OVERVIEW_ASPECTES_TECNICS_int(
445 : osSection, "rows", &m_nHeight,
446 86 : "MMRBand::MMRBand : No number of rows documented");
447 : }
448 :
449 : // Getting nodata value from metadata
450 82 : void MMRBand::UpdateNoDataValue(const CPLString &osSection)
451 : {
452 164 : CPLString osValue;
453 82 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, "NODATA",
454 121 : osValue) ||
455 39 : osValue.empty())
456 : {
457 51 : m_dfNoData = 0; // No a valid value.
458 51 : m_bNoDataSet = false;
459 : }
460 : else
461 : {
462 31 : m_dfNoData = CPLAtof(osValue);
463 31 : m_bNoDataSet = true;
464 : }
465 82 : }
466 :
467 82 : void MMRBand::UpdateMinMaxValuesFromREL(const CPLString &osSection)
468 : {
469 82 : m_bMinSet = false;
470 :
471 164 : CPLString osValue;
472 :
473 164 : CPLString osAuxSection = SECTION_ATTRIBUTE_DATA;
474 82 : osAuxSection.append(":");
475 82 : osAuxSection.append(osSection);
476 164 : if (m_pfRel->GetMetadataValue(osAuxSection, "min", osValue) &&
477 82 : !osValue.empty())
478 : {
479 79 : if (1 == CPLsscanf(osValue, "%lf", &m_dfMin))
480 79 : m_bMinSet = true;
481 : }
482 :
483 82 : m_bMaxSet = false;
484 164 : if (m_pfRel->GetMetadataValue(osAuxSection, "max", osValue) &&
485 82 : !osValue.empty())
486 : {
487 79 : if (1 == CPLsscanf(osValue, "%lf", &m_dfMax))
488 79 : m_bMaxSet = true;
489 : }
490 :
491 : // Special case: dfMin > dfMax
492 82 : if (m_bMinSet && m_bMaxSet && m_dfMin > m_dfMax)
493 : {
494 0 : m_bMinSet = false;
495 0 : m_bMaxSet = false;
496 : }
497 82 : }
498 :
499 82 : void MMRBand::UpdateMinMaxVisuValuesFromREL(const CPLString &osSection)
500 : {
501 82 : m_bMinVisuSet = false;
502 82 : m_dfVisuMin = 1;
503 :
504 164 : CPLString osValue;
505 82 : if (m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
506 87 : "Color_ValorColor_0", osValue) &&
507 5 : !osValue.empty())
508 : {
509 5 : if (1 == CPLsscanf(osValue, "%lf", &m_dfVisuMin))
510 5 : m_bMinVisuSet = true;
511 : }
512 :
513 82 : m_bMaxVisuSet = false;
514 82 : m_dfVisuMax = 1;
515 :
516 82 : if (m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
517 87 : "Color_ValorColor_n_1", osValue) &&
518 5 : !osValue.empty())
519 : {
520 5 : if (1 == CPLsscanf(osValue, "%lf", &m_dfVisuMax))
521 5 : m_bMaxVisuSet = true;
522 : }
523 82 : }
524 :
525 82 : void MMRBand::UpdateFriendlyDescriptionFromREL(const CPLString &osSection)
526 : {
527 : // This "if" is due to CID 1620830 in Coverity Scan
528 82 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
529 82 : "descriptor", m_osFriendlyDescription))
530 10 : m_osFriendlyDescription = "";
531 82 : }
532 :
533 82 : void MMRBand::UpdateReferenceSystemFromREL()
534 : {
535 : // This "if" is due to CID 1620842 in Coverity Scan
536 82 : if (!m_pfRel->GetMetadataValue("SPATIAL_REFERENCE_SYSTEM:HORIZONTAL",
537 82 : "HorizontalSystemIdentifier", m_osRefSystem))
538 0 : m_osRefSystem = "";
539 82 : }
540 :
541 82 : void MMRBand::UpdateBoundingBoxFromREL(const CPLString &osSection)
542 : {
543 : // Bounding box of the band
544 : // [ATTRIBUTE_DATA:xxxx:EXTENT] or [EXTENT]
545 164 : CPLString osValue;
546 82 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
547 154 : SECTION_EXTENT, "MinX", osValue) ||
548 72 : osValue.empty())
549 : {
550 10 : m_dfBBMinX = 0;
551 : }
552 : else
553 : {
554 72 : if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMinX))
555 0 : m_dfBBMinX = 0;
556 : }
557 :
558 82 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
559 154 : SECTION_EXTENT, "MaxX", osValue) ||
560 72 : osValue.empty())
561 : {
562 10 : m_dfBBMaxX = m_nWidth;
563 : }
564 : else
565 : {
566 72 : if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMaxX))
567 : {
568 : // If the value is something that cannot be scanned,
569 : // we silently continue as it was undefined.
570 0 : m_dfBBMaxX = m_nWidth;
571 : }
572 : }
573 :
574 82 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
575 154 : SECTION_EXTENT, "MinY", osValue) ||
576 72 : osValue.empty())
577 : {
578 10 : m_dfBBMinY = 0;
579 : }
580 : else
581 : {
582 72 : if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMinY))
583 0 : m_dfBBMinY = 0;
584 : }
585 :
586 82 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
587 154 : SECTION_EXTENT, "MaxY", osValue) ||
588 72 : osValue.empty())
589 : {
590 10 : m_dfBBMaxY = m_nHeight;
591 : }
592 : else
593 : {
594 72 : if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMaxY))
595 : {
596 : // If the value is something that cannot be scanned,
597 : // we silently continue as it was undefined.
598 0 : m_dfBBMaxY = m_nHeight;
599 : }
600 : }
601 82 : }
602 :
603 : /************************************************************************/
604 : /* Functions that read bytes from IMG file band */
605 : /************************************************************************/
606 : template <typename TYPE>
607 48 : CPLErr MMRBand::UncompressRow(void *rowBuffer, size_t nCompressedRawSize)
608 : {
609 48 : int nAccumulated = 0L, nIAccumulated = 0L;
610 : unsigned char cCounter;
611 48 : size_t nCompressedIndex = 0;
612 :
613 : TYPE RLEValue;
614 : TYPE *pDst;
615 48 : size_t sizeof_TYPE = sizeof(TYPE);
616 :
617 96 : std::vector<unsigned char> aCompressedRow;
618 :
619 48 : if (nCompressedRawSize != SIZE_MAX)
620 : {
621 28 : if (nCompressedRawSize > 1000 * 1000 &&
622 0 : GetFileSize() < nCompressedRawSize)
623 : {
624 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too small file");
625 0 : return CE_Failure;
626 : }
627 : try
628 : {
629 28 : aCompressedRow.resize(nCompressedRawSize);
630 : }
631 0 : catch (const std::exception &)
632 : {
633 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
634 : "Out of memory allocating working buffer");
635 0 : return CE_Failure;
636 : }
637 28 : if (VSIFReadL(aCompressedRow.data(), nCompressedRawSize, 1, m_pfIMG) !=
638 : 1)
639 0 : return CE_Failure;
640 : }
641 :
642 144 : while (nAccumulated < m_nWidth)
643 : {
644 96 : if (nCompressedRawSize == SIZE_MAX)
645 : {
646 40 : if (VSIFReadL(&cCounter, 1, 1, m_pfIMG) != 1)
647 0 : return CE_Failure;
648 : }
649 : else
650 : {
651 56 : if (nCompressedIndex >= aCompressedRow.size())
652 : {
653 0 : CPLError(CE_Failure, CPLE_AppDefined,
654 : "Invalid nCompressedIndex");
655 0 : return CE_Failure;
656 : }
657 56 : cCounter = aCompressedRow[nCompressedIndex];
658 56 : nCompressedIndex++;
659 : }
660 :
661 96 : if (cCounter == 0) /* Not compressed part */
662 : {
663 : /* The following counter read does not indicate
664 : "how many repeated values follow" but rather
665 : "how many are decompressed in standard raster format" */
666 0 : if (nCompressedRawSize == SIZE_MAX)
667 : {
668 0 : if (VSIFReadL(&cCounter, 1, 1, m_pfIMG) != 1)
669 0 : return CE_Failure;
670 : }
671 : else
672 : {
673 0 : if (nCompressedIndex >= aCompressedRow.size())
674 : {
675 0 : CPLError(CE_Failure, CPLE_AppDefined,
676 : "Invalid nCompressedIndex");
677 0 : return CE_Failure;
678 : }
679 0 : cCounter = aCompressedRow[nCompressedIndex];
680 0 : nCompressedIndex++;
681 : }
682 :
683 0 : nAccumulated += cCounter;
684 :
685 0 : if (nAccumulated > m_nWidth) /* This should not happen if the file
686 : is RLE and does not share counters across rows */
687 0 : return CE_Failure;
688 :
689 0 : for (; nIAccumulated < nAccumulated; nIAccumulated++)
690 : {
691 0 : if (nCompressedRawSize == SIZE_MAX)
692 : {
693 0 : VSIFReadL(&RLEValue, sizeof_TYPE, 1, m_pfIMG);
694 0 : memcpy((static_cast<TYPE *>(rowBuffer)) + nIAccumulated,
695 : &RLEValue, sizeof_TYPE);
696 : }
697 : else
698 : {
699 0 : if (nCompressedIndex + sizeof_TYPE > aCompressedRow.size())
700 : {
701 0 : CPLError(CE_Failure, CPLE_AppDefined,
702 : "Invalid nCompressedIndex");
703 0 : return CE_Failure;
704 : }
705 0 : memcpy((static_cast<TYPE *>(rowBuffer)) + nIAccumulated,
706 0 : &aCompressedRow[nCompressedIndex], sizeof_TYPE);
707 0 : nCompressedIndex += sizeof_TYPE;
708 : }
709 : }
710 : }
711 : else
712 : {
713 96 : nAccumulated += cCounter;
714 96 : if (nAccumulated > m_nWidth) /* This should not happen if the file
715 : is RLE and does not share counters across rows */
716 0 : return CE_Failure;
717 :
718 96 : if (nCompressedRawSize == SIZE_MAX)
719 : {
720 40 : if (VSIFReadL(&RLEValue, sizeof_TYPE, 1, m_pfIMG) != 1)
721 0 : return CE_Failure;
722 : }
723 : else
724 : {
725 56 : if (nCompressedIndex + sizeof(TYPE) > aCompressedRow.size())
726 : {
727 0 : CPLError(CE_Failure, CPLE_AppDefined,
728 : "Invalid nCompressedIndex");
729 0 : return CE_Failure;
730 : }
731 56 : memcpy(&RLEValue, &aCompressedRow[nCompressedIndex],
732 : sizeof(TYPE));
733 56 : nCompressedIndex += sizeof(TYPE);
734 : }
735 :
736 96 : const int nCount = nAccumulated - nIAccumulated;
737 96 : pDst = static_cast<TYPE *>(rowBuffer) + nIAccumulated;
738 :
739 96 : std::fill(pDst, pDst + nCount, RLEValue);
740 :
741 96 : nIAccumulated = nAccumulated;
742 : }
743 : }
744 :
745 48 : return CE_None;
746 : }
747 :
748 121 : CPLErr MMRBand::GetBlockData(void *rowBuffer, size_t nCompressedRawSize)
749 : {
750 121 : if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BIT)
751 : {
752 16 : const int nGDALBlockSize = DIV_ROUND_UP(m_nBlockXSize, 8);
753 :
754 16 : if (VSIFReadL(rowBuffer, nGDALBlockSize, 1, m_pfIMG) != 1)
755 : {
756 0 : CPLError(CE_Failure, CPLE_AppDefined, "Error while reading band");
757 0 : return CE_Failure;
758 : }
759 16 : return CE_None;
760 : }
761 :
762 105 : if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BYTE ||
763 78 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_INTEGER ||
764 72 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_UINTEGER ||
765 66 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_LONG ||
766 60 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_REAL ||
767 54 : m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_DOUBLE)
768 : {
769 57 : if (VSIFReadL(rowBuffer, m_nDataTypeSizeBytes, m_nWidth, m_pfIMG) !=
770 57 : static_cast<size_t>(m_nWidth))
771 : {
772 0 : CPLError(CE_Failure, CPLE_AppDefined, "Error while reading band");
773 0 : return CE_Failure;
774 : }
775 57 : return CE_None;
776 : }
777 :
778 : CPLErr eErr;
779 48 : switch (m_eMMDataType)
780 : {
781 18 : case MMDataType::DATATYPE_AND_COMPR_BYTE_RLE:
782 18 : eErr = UncompressRow<GByte>(rowBuffer, nCompressedRawSize);
783 18 : break;
784 6 : case MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE:
785 6 : eErr = UncompressRow<GInt16>(rowBuffer, nCompressedRawSize);
786 6 : break;
787 6 : case MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE:
788 6 : eErr = UncompressRow<GUInt16>(rowBuffer, nCompressedRawSize);
789 6 : break;
790 6 : case MMDataType::DATATYPE_AND_COMPR_LONG_RLE:
791 6 : eErr = UncompressRow<GInt32>(rowBuffer, nCompressedRawSize);
792 6 : break;
793 6 : case MMDataType::DATATYPE_AND_COMPR_REAL_RLE:
794 6 : eErr = UncompressRow<float>(rowBuffer, nCompressedRawSize);
795 6 : break;
796 6 : case MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE:
797 6 : eErr = UncompressRow<double>(rowBuffer, nCompressedRawSize);
798 6 : break;
799 :
800 0 : default:
801 0 : CPLError(CE_Failure, CPLE_AppDefined, "Error in datatype");
802 0 : eErr = CE_Failure;
803 : }
804 :
805 48 : return eErr;
806 : } // End of GetBlockData()
807 :
808 14 : int MMRBand::PositionAtStartOfRowOffsetsInFile()
809 : {
810 : vsi_l_offset nFileSize, nHeaderOffset;
811 : char szChain[16];
812 : short int nVersion, nSubVersion;
813 : int nOffsetSize, nOffsetsSectionType;
814 :
815 14 : if (VSIFSeekL(m_pfIMG, 0, SEEK_END))
816 0 : return 0;
817 :
818 14 : nFileSize = VSIFTellL(m_pfIMG);
819 :
820 14 : if (nFileSize < 32) // Minimum required size
821 2 : return 0;
822 :
823 12 : if (m_nHeight)
824 : {
825 12 : if (nFileSize < static_cast<vsi_l_offset>(32) + m_nHeight + 32)
826 0 : return 0;
827 : }
828 :
829 12 : vsi_l_offset nHeadOffset = nFileSize - 32;
830 :
831 12 : if (VSIFSeekL(m_pfIMG, nHeadOffset, SEEK_SET)) // Reading final header.
832 0 : return 0;
833 12 : if (VSIFReadL(szChain, 16, 1, m_pfIMG) != 1)
834 0 : return 0;
835 204 : for (int nIndex = 0; nIndex < 16; nIndex++)
836 : {
837 192 : if (szChain[nIndex] != '\0')
838 0 : return 0; // Supposed 0's are not 0.
839 : }
840 :
841 12 : if (VSIFReadL(szChain, 8, 1, m_pfIMG) != 1)
842 0 : return 0;
843 :
844 12 : if (strncmp(szChain, "IMG ", 4) || szChain[5] != '.')
845 0 : return 0;
846 :
847 : // Some version checks
848 12 : szChain[7] = 0;
849 12 : if (sscanf(szChain + 6, "%hd", &nSubVersion) != 1 || nSubVersion < 0)
850 0 : return 0;
851 :
852 12 : szChain[5] = 0;
853 12 : if (sscanf(szChain + 4, "%hd", &nVersion) != 1 || nVersion != 1)
854 0 : return 0;
855 :
856 : // Next header to be read
857 12 : if (VSIFReadL(&nHeaderOffset, sizeof(vsi_l_offset), 1, m_pfIMG) != 1)
858 0 : return 0;
859 :
860 24 : std::set<vsi_l_offset> alreadyVisitedOffsets;
861 : bool bRepeat;
862 12 : do
863 : {
864 12 : bRepeat = FALSE;
865 :
866 12 : if (VSIFSeekL(m_pfIMG, nHeaderOffset, SEEK_SET))
867 0 : return 0;
868 :
869 12 : if (VSIFReadL(szChain, 8, 1, m_pfIMG) != 1)
870 0 : return 0;
871 :
872 12 : if (strncmp(szChain, "IMG ", 4) || szChain[5] != '.')
873 0 : return 0;
874 :
875 12 : if (VSIFReadL(&nOffsetsSectionType, 4, 1, m_pfIMG) != 1)
876 0 : return 0;
877 :
878 12 : if (nOffsetsSectionType != 2) // 2 = row offsets section
879 : {
880 : // This is not the section I am looking for
881 0 : if (VSIFSeekL(m_pfIMG, 8 + 4, SEEK_CUR))
882 0 : return 0;
883 :
884 0 : if (VSIFReadL(&nHeaderOffset, sizeof(vsi_l_offset), 1, m_pfIMG) !=
885 : 1)
886 0 : return 0;
887 :
888 0 : if (nHeaderOffset == 0)
889 0 : return 0;
890 :
891 0 : if (cpl::contains(alreadyVisitedOffsets, nHeaderOffset))
892 : {
893 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
894 : "Error reading offsets. They will be ignored.");
895 0 : return 0;
896 : }
897 :
898 0 : alreadyVisitedOffsets.insert(nHeaderOffset);
899 :
900 0 : bRepeat = TRUE;
901 : }
902 :
903 : } while (bRepeat);
904 :
905 12 : szChain[7] = 0;
906 12 : if (sscanf(szChain + 6, "%hd", &nSubVersion) != 1 || nSubVersion < 0)
907 0 : return 0;
908 12 : szChain[5] = 0;
909 12 : if (sscanf(szChain + 4, "%hd", &nVersion) != 1 || nVersion != 1)
910 0 : return 0;
911 :
912 : /*
913 : Now I'm in the correct section
914 : -------------------------------
915 : Info about this section:
916 : RasterRLE: minimum size: nHeight*2
917 : Offsets: minimum size: 32+nHeight*4
918 : Final: size: 32
919 : */
920 :
921 12 : if (m_nHeight)
922 : {
923 12 : if (nHeaderOffset < static_cast<vsi_l_offset>(m_nHeight) *
924 12 : 2 || // Minimum size of an RLE
925 12 : nFileSize - nHeaderOffset <
926 12 : static_cast<vsi_l_offset>(32) + m_nHeight +
927 : 32) // Minimum size of the section in version 1.0
928 0 : return 0;
929 : }
930 :
931 24 : if (VSIFReadL(&nOffsetSize, 4, 1, m_pfIMG) != 1 ||
932 12 : (nOffsetSize != 8 && nOffsetSize != 4 && nOffsetSize != 2 &&
933 12 : nOffsetSize != 1))
934 0 : return 0;
935 :
936 12 : if (m_nHeight)
937 : {
938 12 : if (nFileSize - nHeaderOffset <
939 12 : 32 + static_cast<vsi_l_offset>(nOffsetSize) * m_nHeight +
940 : 32) // No space for this section in this file
941 0 : return 0;
942 :
943 : // I leave the file prepared to read offsets
944 12 : if (VSIFSeekL(m_pfIMG, 16, SEEK_CUR))
945 0 : return 0;
946 : }
947 : else
948 : {
949 0 : if (VSIFSeekL(m_pfIMG, 4, SEEK_CUR))
950 0 : return 0;
951 :
952 0 : if (VSIFSeekL(m_pfIMG, 4, SEEK_CUR))
953 0 : return 0;
954 :
955 : // I leave the file prepared to read offsets
956 0 : if (VSIFSeekL(m_pfIMG, 8, SEEK_CUR))
957 0 : return 0;
958 : }
959 :
960 : // There are offsets!
961 12 : return nOffsetSize;
962 : } // Fi de PositionAtStartOfRowOffsetsInFile()
963 :
964 : /************************************************************************/
965 : /* GetFileSize() */
966 : /************************************************************************/
967 :
968 0 : vsi_l_offset MMRBand::GetFileSize()
969 : {
970 0 : if (m_nFileSize == 0)
971 : {
972 0 : const auto nCurPos = VSIFTellL(m_pfIMG);
973 0 : VSIFSeekL(m_pfIMG, 0, SEEK_END);
974 0 : m_nFileSize = VSIFTellL(m_pfIMG);
975 0 : VSIFSeekL(m_pfIMG, nCurPos, SEEK_SET);
976 : }
977 0 : return m_nFileSize;
978 : }
979 :
980 : /************************************************************************/
981 : /* FillRowOffsets() */
982 : /************************************************************************/
983 :
984 115 : bool MMRBand::FillRowOffsets()
985 : {
986 : vsi_l_offset nStartOffset;
987 : int nIRow;
988 : vsi_l_offset nBytesPerPixelPerNCol;
989 : int nSizeToRead; // nSizeToRead is not an offset, but the size of the offsets being read
990 : // directly from the IMG file (can be 1, 2, 4, or 8).
991 : vsi_l_offset nFileByte;
992 : size_t nMaxBytesPerCompressedRow;
993 115 : const int nGDALBlockSize = DIV_ROUND_UP(m_nBlockXSize, 8);
994 :
995 : // If it's filled, there is no need to fill it again
996 115 : if (!m_aFileOffsets.empty())
997 80 : return true;
998 :
999 : // Sanity check to avoid attempting huge memory allocation
1000 35 : if (m_nHeight > 1000 * 1000)
1001 : {
1002 0 : if (GetFileSize() < static_cast<vsi_l_offset>(m_nHeight))
1003 : {
1004 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too small file");
1005 0 : return false;
1006 : }
1007 : }
1008 :
1009 : try
1010 : {
1011 35 : m_aFileOffsets.resize(static_cast<size_t>(m_nHeight) + 1);
1012 : }
1013 0 : catch (const std::bad_alloc &e)
1014 : {
1015 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1016 0 : return false;
1017 : }
1018 :
1019 35 : switch (m_eMMDataType)
1020 : {
1021 2 : case MMDataType::DATATYPE_AND_COMPR_BIT:
1022 :
1023 : // "<=" it's ok. There is space and it's to make easier the programming
1024 20 : for (nIRow = 0; nIRow <= m_nHeight; nIRow++)
1025 18 : m_aFileOffsets[nIRow] =
1026 18 : static_cast<vsi_l_offset>(nIRow) * nGDALBlockSize;
1027 2 : break;
1028 :
1029 19 : case MMDataType::DATATYPE_AND_COMPR_BYTE:
1030 : case MMDataType::DATATYPE_AND_COMPR_INTEGER:
1031 : case MMDataType::DATATYPE_AND_COMPR_UINTEGER:
1032 : case MMDataType::DATATYPE_AND_COMPR_LONG:
1033 : case MMDataType::DATATYPE_AND_COMPR_REAL:
1034 : case MMDataType::DATATYPE_AND_COMPR_DOUBLE:
1035 19 : nBytesPerPixelPerNCol =
1036 19 : m_nDataTypeSizeBytes * static_cast<vsi_l_offset>(m_nWidth);
1037 : // "<=" it's ok. There is space and it's to make easier the programming
1038 95 : for (nIRow = 0; nIRow <= m_nHeight; nIRow++)
1039 76 : m_aFileOffsets[nIRow] =
1040 76 : static_cast<vsi_l_offset>(nIRow) * nBytesPerPixelPerNCol;
1041 19 : break;
1042 :
1043 14 : case MMDataType::DATATYPE_AND_COMPR_BYTE_RLE:
1044 : case MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE:
1045 : case MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE:
1046 : case MMDataType::DATATYPE_AND_COMPR_LONG_RLE:
1047 : case MMDataType::DATATYPE_AND_COMPR_REAL_RLE:
1048 : case MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE:
1049 :
1050 14 : nStartOffset = VSIFTellL(m_pfIMG);
1051 :
1052 : // Let's determine if are there offsets in the file
1053 14 : if (0 < (nSizeToRead = PositionAtStartOfRowOffsetsInFile()))
1054 : {
1055 : // I have offsets!!
1056 12 : nFileByte = 0L; // all bits to 0
1057 48 : for (nIRow = 0; nIRow < m_nHeight; nIRow++)
1058 : {
1059 36 : if (VSIFReadL(&nFileByte, nSizeToRead, 1, m_pfIMG) != 1)
1060 0 : return false;
1061 :
1062 36 : m_aFileOffsets[nIRow] = nFileByte;
1063 :
1064 : // Let's check that the difference between two offsets is in a int range
1065 36 : if (nIRow > 0)
1066 : {
1067 48 : if (m_aFileOffsets[nIRow] <=
1068 24 : m_aFileOffsets[static_cast<size_t>(nIRow) - 1])
1069 0 : return false;
1070 :
1071 24 : if (m_aFileOffsets[nIRow] -
1072 24 : m_aFileOffsets[static_cast<size_t>(nIRow) -
1073 24 : 1] >=
1074 : static_cast<vsi_l_offset>(SIZE_MAX))
1075 0 : return false;
1076 : }
1077 : }
1078 12 : m_aFileOffsets[nIRow] = 0; // Not reliable
1079 12 : VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
1080 12 : break;
1081 : }
1082 :
1083 : // Not indexed RLE. We create a dynamic indexation
1084 4 : if (m_nWidth >
1085 2 : INT_MAX /
1086 2 : (std::max(1, static_cast<int>(m_eMMBytesPerPixel)) + 1))
1087 : {
1088 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too large row: %d",
1089 : m_nWidth);
1090 0 : VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
1091 0 : return false;
1092 : }
1093 :
1094 2 : nMaxBytesPerCompressedRow =
1095 2 : static_cast<int>(m_eMMBytesPerPixel)
1096 2 : ? (m_nWidth * (static_cast<int>(m_eMMBytesPerPixel) + 1))
1097 0 : : (m_nWidth * (1 + 1));
1098 : unsigned char *pBuffer;
1099 :
1100 2 : if (nullptr == (pBuffer = static_cast<unsigned char *>(
1101 2 : VSI_MALLOC_VERBOSE(nMaxBytesPerCompressedRow))))
1102 : {
1103 0 : VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
1104 0 : return false;
1105 : }
1106 :
1107 2 : VSIFSeekL(m_pfIMG, 0, SEEK_SET);
1108 2 : m_aFileOffsets[0] = 0;
1109 8 : for (nIRow = 0; nIRow < m_nHeight; nIRow++)
1110 : {
1111 6 : GetBlockData(pBuffer, SIZE_MAX);
1112 6 : m_aFileOffsets[static_cast<size_t>(nIRow) + 1] =
1113 6 : VSIFTellL(m_pfIMG);
1114 : }
1115 2 : VSIFree(pBuffer);
1116 2 : VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
1117 2 : break;
1118 :
1119 0 : default:
1120 0 : return false;
1121 : } // End of switch (eMMDataType)
1122 35 : return true;
1123 :
1124 : } // End of FillRowOffsets()
|