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