LCOV - code coverage report
Current view: top level - frmts/miramon - miramon_rel.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 396 462 85.7 %
Date: 2025-09-10 17:48:50 Functions: 25 25 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  MiraMonRaster driver
       4             :  * Purpose:  Implements MMRRel: provides access to the REL file, which
       5             :  *           holds all the necessary metadata to correctly interpret and
       6             :  *           access the associated raw data.
       7             :  * Author:   Abel Pau
       8             :  *
       9             :  ******************************************************************************
      10             :  * Copyright (c) 2025, Xavier Pons
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "cpl_port.h"
      16             : #include "gdal_priv.h"
      17             : #include "cpl_string.h"
      18             : 
      19             : #include "miramon_rel.h"
      20             : #include "miramon_band.h"
      21             : 
      22             : #include "../miramon_common/mm_gdal_functions.h"  // For MMCheck_REL_FILE()
      23             : 
      24             : CPLString MMRRel::m_szImprobableRELChain = "@#&%$|``|$%&#@";
      25             : 
      26             : /************************************************************************/
      27             : /*                              MMRRel()                               */
      28             : /************************************************************************/
      29         246 : MMRRel::MMRRel(const CPLString &osRELFilenameIn, bool bIMGMustExist)
      30         246 :     : m_osRelFileName(osRELFilenameIn)
      31             : {
      32         492 :     CPLString osRelCandidate = osRELFilenameIn;
      33             : 
      34             :     // Getting the name of the REL
      35         492 :     const CPLString osMMRPrefix = "MiraMonRaster:";
      36         246 :     if (STARTS_WITH(osRelCandidate, osMMRPrefix))
      37             :     {
      38             :         // SUBDATASET case: gets the names of the bands in the subdataset
      39           6 :         size_t nPos = osRelCandidate.ifind(osMMRPrefix);
      40           6 :         if (nPos != 0)
      41           0 :             return;
      42             : 
      43           6 :         CPLString osSDSReL = osRelCandidate.substr(osMMRPrefix.size());
      44             : 
      45             :         // Getting the internal names of the bands
      46           6 :         const CPLStringList aosTokens(CSLTokenizeString2(osSDSReL, ",", 0));
      47           6 :         const int nTokens = CSLCount(aosTokens);
      48             : 
      49           6 :         if (nTokens < 1)
      50           0 :             return;
      51             : 
      52           6 :         osRelCandidate = aosTokens[0];
      53           6 :         osRelCandidate.replaceAll("\"", "");
      54             : 
      55             :         // Getting the list of bands in the subdataset
      56          12 :         for (int nIBand = 0; nIBand < nTokens - 1; nIBand++)
      57             :         {
      58             :             // Raw band name
      59          12 :             CPLString osBandName = aosTokens[nIBand + 1];
      60           6 :             osBandName.replaceAll("\"", "");
      61           6 :             m_papoSDSBands.emplace_back(osBandName);
      62             :         }
      63           6 :         m_bIsAMiraMonFile = true;
      64             :     }
      65             :     else
      66             :     {
      67             :         // Getting the metadata file name. If it's already a REL file,
      68             :         // then same name is returned.
      69         240 :         osRelCandidate = GetAssociatedMetadataFileName(m_osRelFileName.c_str());
      70         240 :         if (osRelCandidate.empty())
      71             :         {
      72         168 :             if (m_bIsAMiraMonFile)
      73             :             {
      74           0 :                 CPLError(CE_Failure, CPLE_OpenFailed,
      75             :                          "Metadata file for %s should exist.",
      76             :                          m_osRelFileName.c_str());
      77             :             }
      78         168 :             if (!bIMGMustExist)
      79             :             {
      80             :                 // Simulates that we have a MiraMon file
      81             :                 // and we can ask things to this Rel file.
      82           4 :                 UpdateRELNameChar(m_osRelFileName);
      83           4 :                 m_bIsAMiraMonFile = true;
      84           4 :                 if (!OpenRELFile())
      85           0 :                     return;
      86             :             }
      87         168 :             return;
      88             :         }
      89             :         else
      90             :         {
      91             :             // It's a REL and it's not empty, so it's a MiraMon file
      92          72 :             VSILFILE *pF = VSIFOpenL(osRelCandidate, "r");
      93          72 :             if (!pF)
      94             :             {
      95           0 :                 CPLError(CE_Failure, CPLE_OpenFailed,
      96             :                          "Metadata file %s could not be opened.",
      97             :                          m_osRelFileName.c_str());
      98           0 :                 return;
      99             :             }
     100          72 :             VSIFSeekL(pF, 0, SEEK_END);
     101          72 :             if (VSIFTellL(pF))
     102          72 :                 m_bIsAMiraMonFile = true;
     103             :             else
     104             :             {
     105           0 :                 CPLError(
     106             :                     CE_Failure, CPLE_OpenFailed,
     107             :                     "Metadata file for %s should have some information in.",
     108             :                     m_osRelFileName.c_str());
     109             : 
     110           0 :                 VSIFCloseL(pF);
     111           0 :                 return;
     112             :             }
     113          72 :             VSIFCloseL(pF);
     114             :         }
     115             :     }
     116             : 
     117             :     // If rel name was not a REL name, we update that
     118             :     // from the one found in the process of discovering it.
     119          78 :     UpdateRELNameChar(osRelCandidate);
     120             : 
     121             :     // We let it be opened
     122          78 :     if (!OpenRELFile())
     123           0 :         return;
     124             : 
     125             :     // Collect band information
     126          78 :     if (ParseBandInfo() != CE_None)
     127           8 :         return;
     128             : 
     129             :     // We have a valid object MMRREL.
     130          70 :     m_bIsValid = true;
     131             : 
     132          70 :     return;
     133             : }
     134             : 
     135             : /************************************************************************/
     136             : /*                              ~MMRRel()                              */
     137             : /************************************************************************/
     138             : 
     139         246 : MMRRel::~MMRRel()
     140             : {
     141         246 :     CloseRELFile();
     142         246 : }
     143             : 
     144             : /************************************************************************/
     145             : /*                     Getting section-key-value                        */
     146             : /************************************************************************/
     147             : // Used when the MMRREL is not yet constructed.
     148             : CPLString
     149         247 : MMRRel::GetValueFromSectionKeyPriorToREL(const CPLString &osPriorRelName,
     150             :                                          const CPLString &osSection,
     151             :                                          const CPLString &osKey)
     152             : {
     153         247 :     if (osPriorRelName.empty())
     154           0 :         return "";
     155             : 
     156         247 :     VSILFILE *pPriorRELFile = VSIFOpenL(osPriorRelName, "rb");
     157         247 :     if (!pPriorRELFile)
     158           0 :         return "";
     159             : 
     160         494 :     CPLString osValue = GetValueFromSectionKey(pPriorRELFile, osSection, osKey);
     161         247 :     VSIFCloseL(pPriorRELFile);
     162         247 :     return osValue;
     163             : }
     164             : 
     165             : // Used when the MMRREL is already constructed.
     166        3567 : CPLString MMRRel::GetValueFromSectionKeyFromREL(const CPLString &osSection,
     167             :                                                 const CPLString &osKey)
     168             : {
     169        3567 :     if (!GetRELFile())
     170             :     {
     171           0 :         CPLError(CE_Failure, CPLE_AppDefined, "REL file is not opened: \"%s\"",
     172             :                  m_osRelFileName.c_str());
     173           0 :         return "";
     174             :     }
     175             : 
     176        3567 :     return GetValueFromSectionKey(GetRELFile(), osSection, osKey);
     177             : }
     178             : 
     179             : // This function is the C++ equivalent of MMReturnValueFromSectionINIFile().
     180             : // It improves upon the original by using CPLString instead of raw char pointers,
     181             : // and by operating on an already opened file pointer rather than reopening the file
     182             : // on each invocation.
     183             : // MMReturnValueFromSectionINIFile() is retained in miramon_common because it is
     184             : // widely used by existing, already OGR tested code (and in the common code itself).
     185             : // At least in C++ code the modern version is used
     186        3814 : CPLString MMRRel::GetValueFromSectionKey(VSILFILE *pf,
     187             :                                          const CPLString &osSection,
     188             :                                          const CPLString &osKey)
     189             : {
     190        3814 :     if (!pf)
     191           0 :         return "";
     192             : 
     193        7628 :     CPLString osCurrentSection;
     194        7628 :     CPLString osCurrentKey, osCurrentValue;
     195        3814 :     bool bIAmInMySection = false;
     196             : 
     197             :     const char *pszLine;
     198             : 
     199        3814 :     VSIFSeekL(pf, 0, SEEK_SET);
     200      427753 :     while ((pszLine = CPLReadLine2L(pf, 10000, nullptr)) != nullptr)
     201             :     {
     202      426866 :         CPLString rawLine = pszLine;
     203             : 
     204      426866 :         rawLine.Recode(CPL_ENC_ISO8859_1, CPL_ENC_UTF8);
     205      426866 :         rawLine.Trim();
     206             : 
     207      426866 :         if (rawLine.empty() || rawLine[0] == ';' || rawLine[0] == '#')
     208       71124 :             continue;
     209             : 
     210      355742 :         if (rawLine[0] == '[' && rawLine[rawLine.size() - 1] == ']')
     211             :         {
     212       74872 :             if (bIAmInMySection)
     213             :             {
     214             :                 // This is the next section to mine, so nothing to find here.
     215         906 :                 return m_szImprobableRELChain;
     216             :             }
     217             : 
     218       73966 :             osCurrentSection = rawLine.substr(1, rawLine.size() - 2);
     219       73966 :             osCurrentSection.Trim();
     220             : 
     221       73966 :             if (!EQUAL(osCurrentSection, osSection))
     222       70877 :                 bIAmInMySection = false;
     223             :             else
     224        3089 :                 bIAmInMySection = true;
     225             : 
     226       73966 :             continue;
     227             :         }
     228             : 
     229      280870 :         if (!bIAmInMySection)
     230      269246 :             continue;
     231             : 
     232       11624 :         size_t equalPos = rawLine.find('=');
     233       11624 :         if (equalPos != CPLString::npos)
     234             :         {
     235       11624 :             osCurrentKey = rawLine.substr(0, equalPos);
     236       11624 :             osCurrentValue = rawLine.substr(equalPos + 1);
     237       11624 :             osCurrentKey.Trim();
     238       11624 :             osCurrentValue.Trim();
     239             : 
     240       11624 :             if (EQUAL(osCurrentKey, osKey))
     241        2021 :                 return osCurrentValue;
     242             :         }
     243             :     }
     244             : 
     245         887 :     return m_szImprobableRELChain;  // Key not found
     246             : }
     247             : 
     248             : /************************************************************************/
     249             : /*                     Other functions                                  */
     250             : /************************************************************************/
     251             : 
     252             : // Converts FileNameI.rel to FileName.img
     253         130 : CPLString MMRRel::MMRGetFileNameFromRelName(const CPLString &osRELFile)
     254             : {
     255         130 :     if (osRELFile.empty())
     256           0 :         return "";
     257             : 
     258         260 :     CPLString osFile = CPLString(CPLResetExtensionSafe(osRELFile, "").c_str());
     259             : 
     260         130 :     if (osFile.length() < 2)
     261           0 :         return "";
     262             : 
     263         130 :     osFile.resize(osFile.size() - 2);  // I.
     264         130 :     osFile += pszExtRaster;
     265             : 
     266         130 :     return osFile;
     267             : }
     268             : 
     269             : // Converts FileName.img to FileNameI.rel
     270         181 : CPLString MMRRel::MMRGetSimpleMetadataName(const CPLString &osLayerName)
     271             : {
     272         181 :     if (osLayerName.empty())
     273           0 :         return "";
     274             : 
     275             :     // Extract extension
     276             :     CPLString osRELFile =
     277         362 :         CPLString(CPLResetExtensionSafe(osLayerName, "").c_str());
     278             : 
     279         181 :     if (!osRELFile.length())
     280           0 :         return "";
     281             : 
     282             :     // Extract "."
     283         181 :     osRELFile.resize(osRELFile.size() - 1);
     284             :     // Add "I.rel"
     285         181 :     osRELFile += pszExtRasterREL;
     286             : 
     287         181 :     return osRELFile;
     288             : }
     289             : 
     290             : // Gets the value from a section-key accessing directly to the RELFile.
     291             : // It happens when MMRel is used to access a REL that is not an IMG sidecar
     292             : // or at the Identify() process, when we don't have already the MMRRel constructed.
     293         187 : bool MMRRel::GetAndExcludeMetadataValueDirectly(const CPLString &osRELFile,
     294             :                                                 const CPLString &osSection,
     295             :                                                 const CPLString &osKey,
     296             :                                                 CPLString &osValue)
     297             : {
     298         187 :     addExcludedSectionKey(osSection, osKey);
     299         187 :     return GetMetadataValueDirectly(osRELFile, osSection, osKey, osValue);
     300             : }
     301             : 
     302         247 : bool MMRRel::GetMetadataValueDirectly(const CPLString &osRELFile,
     303             :                                       const CPLString &osSection,
     304             :                                       const CPLString &osKey,
     305             :                                       CPLString &osValue)
     306             : {
     307         247 :     osValue = GetValueFromSectionKeyPriorToREL(osRELFile, osSection, osKey);
     308             : 
     309         247 :     if (osValue != m_szImprobableRELChain)
     310         130 :         return true;  // Found
     311             : 
     312         117 :     osValue = "";
     313         117 :     return false;  // Key not found
     314             : }
     315             : 
     316          81 : bool MMRRel::SameFile(const CPLString &osFile1, const CPLString &osFile2)
     317             : {
     318          81 :     if (EQUAL(osFile1, osFile2))
     319          17 :         return true;
     320             : 
     321             :     // Just to be more sure:
     322         128 :     CPLString osLayerName1 = osFile1;
     323          64 :     osLayerName1.replaceAll("\\", "/");
     324         128 :     CPLString osLayerName2 = osFile2;
     325          64 :     osLayerName2.replaceAll("\\", "/");
     326             : 
     327          64 :     if (EQUAL(osLayerName1, osLayerName2))
     328           0 :         return true;
     329             : 
     330          64 :     return false;
     331             : }
     332             : 
     333             : // Gets the state (enum class MMRNomFitxerState) of NomFitxer in the
     334             : // specified section
     335             : // [pszSection]
     336             : // NomFitxer=Value
     337          81 : MMRNomFitxerState MMRRel::MMRStateOfNomFitxerInSection(
     338             :     const CPLString &osLayerName, const CPLString &osSection,
     339             :     const CPLString &osRELFile, bool bNomFitxerMustExist)
     340             : {
     341         162 :     CPLString osDocumentedLayerName;
     342             : 
     343          81 :     if (!GetAndExcludeMetadataValueDirectly(osRELFile, osSection, KEY_NomFitxer,
     344          90 :                                             osDocumentedLayerName) ||
     345           9 :         osDocumentedLayerName.empty())
     346             :     {
     347         144 :         CPLString osIIMGFromREL = MMRGetFileNameFromRelName(osRELFile);
     348          72 :         if (SameFile(osIIMGFromREL, osLayerName))
     349          13 :             return MMRNomFitxerState::NOMFITXER_VALUE_EXPECTED;
     350             : 
     351          59 :         if (bNomFitxerMustExist)
     352          22 :             return MMRNomFitxerState::NOMFITXER_VALUE_UNEXPECTED;
     353             :         else
     354          37 :             return MMRNomFitxerState::NOMFITXER_NOT_FOUND;
     355             :     }
     356             : 
     357          18 :     CPLString osFileAux = CPLFormFilenameSafe(CPLGetPathSafe(osRELFile).c_str(),
     358          18 :                                               osDocumentedLayerName, "");
     359             : 
     360           9 :     osDocumentedLayerName.Trim();
     361           9 :     if (*osDocumentedLayerName == '*' || *osDocumentedLayerName == '?')
     362           0 :         return MMRNomFitxerState::NOMFITXER_VALUE_UNEXPECTED;
     363             : 
     364           9 :     if (SameFile(osFileAux, osLayerName))
     365           4 :         return MMRNomFitxerState::NOMFITXER_VALUE_EXPECTED;
     366             : 
     367           5 :     return MMRNomFitxerState::NOMFITXER_VALUE_UNEXPECTED;
     368             : }
     369             : 
     370             : // Tries to find a reference to the IMG file 'pszLayerName'
     371             : // we are opening in the REL file 'pszRELFile'
     372          51 : CPLString MMRRel::MMRGetAReferenceToIMGFile(const CPLString &osLayerName,
     373             :                                             const CPLString &osRELFile)
     374             : {
     375          51 :     if (osRELFile.empty())
     376             :     {
     377           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Expected File name.");
     378           0 :         return "";
     379             :     }
     380             : 
     381             :     // [ATTRIBUTE_DATA]
     382             :     // NomFitxer=
     383             :     // It should be empty but if it's not, at least,
     384             :     // the value has to be osLayerName
     385          51 :     MMRNomFitxerState iState = MMRStateOfNomFitxerInSection(
     386             :         osLayerName, SECTION_ATTRIBUTE_DATA, osRELFile, false);
     387             : 
     388          51 :     if (iState == MMRNomFitxerState::NOMFITXER_VALUE_EXPECTED ||
     389             :         iState == MMRNomFitxerState::NOMFITXER_VALUE_EMPTY)
     390             :     {
     391          14 :         return osRELFile;
     392             :     }
     393          37 :     else if (iState == MMRNomFitxerState::NOMFITXER_VALUE_UNEXPECTED)
     394             :     {
     395           0 :         if (m_bIsAMiraMonFile)
     396             :         {
     397           0 :             CPLError(
     398             :                 CE_Failure, CPLE_OpenFailed,
     399             :                 "Unexpected value for SECTION_ATTRIBUTE_DATA [NomFitxer] in "
     400             :                 "%s file.",
     401             :                 osRELFile.c_str());
     402             :         }
     403           0 :         return "";
     404             :     }
     405             : 
     406             :     // Discarting not supported via SDE (some files
     407             :     // could have this option)
     408          74 :     CPLString osVia;
     409          37 :     if (GetAndExcludeMetadataValueDirectly(osRELFile, SECTION_ATTRIBUTE_DATA,
     410             :                                            KEY_via, osVia))
     411             :     {
     412           0 :         if (!osVia.empty() && !EQUAL(osVia, "SDE"))
     413             :         {
     414           0 :             if (m_bIsAMiraMonFile)
     415             :             {
     416           0 :                 CPLError(CE_Failure, CPLE_OpenFailed,
     417             :                          "Unexpected Via in %s file", osRELFile.c_str());
     418             :             }
     419           0 :             return "";
     420             :         }
     421             :     }
     422             : 
     423          74 :     CPLString osFieldNames;
     424             : 
     425          37 :     if (!GetAndExcludeMetadataValueDirectly(osRELFile, SECTION_ATTRIBUTE_DATA,
     426             :                                             Key_IndexesNomsCamps,
     427          68 :                                             osFieldNames) ||
     428          31 :         osFieldNames.empty())
     429             :     {
     430           8 :         if (m_bIsAMiraMonFile)
     431             :         {
     432           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     433             :                      "IndexesNomsCamps not found in %s file",
     434             :                      osRELFile.c_str());
     435             :         }
     436           8 :         return "";
     437             :     }
     438             : 
     439             :     // Getting the internal names of the bands
     440          58 :     const CPLStringList aosTokens(CSLTokenizeString2(osFieldNames, ",", 0));
     441          29 :     const int nTokenBands = CSLCount(aosTokens);
     442             : 
     443          58 :     CPLString osBandSectionKey;
     444          58 :     CPLString osAttributeDataName;
     445          58 :     for (int nIBand = 0; nIBand < nTokenBands; nIBand++)
     446             :     {
     447          32 :         osBandSectionKey = KEY_NomCamp;
     448          32 :         osBandSectionKey.append("_");
     449          32 :         osBandSectionKey.append(aosTokens[nIBand]);
     450             : 
     451          32 :         CPLString osBandSectionValue;
     452             : 
     453          32 :         if (!GetAndExcludeMetadataValueDirectly(
     454             :                 osRELFile, SECTION_ATTRIBUTE_DATA, osBandSectionKey,
     455          62 :                 osBandSectionValue) ||
     456          30 :             osBandSectionValue.empty())
     457           2 :             continue;  // A band without name (·$· unexpected)
     458             : 
     459             :         // Example: [ATTRIBUTE_DATA:G1]
     460          30 :         osAttributeDataName = SECTION_ATTRIBUTE_DATA;
     461          30 :         osAttributeDataName.append(":");
     462          30 :         osAttributeDataName.append(osBandSectionValue.Trim());
     463             : 
     464             :         // Let's see if this band contains the expected name
     465             :         // or none (in monoband case)
     466          30 :         iState = MMRStateOfNomFitxerInSection(osLayerName, osAttributeDataName,
     467             :                                               osRELFile, true);
     468          30 :         if (iState == MMRNomFitxerState::NOMFITXER_VALUE_EXPECTED)
     469           3 :             return osRELFile;
     470             : 
     471          27 :         else if (iState == MMRNomFitxerState::NOMFITXER_VALUE_UNEXPECTED)
     472          27 :             continue;
     473             : 
     474             :         // If there is only one band is accepted NOMFITXER_NOT_FOUND/EMPTY iState result
     475           0 :         if (nTokenBands == 1)
     476           0 :             return osRELFile;
     477             :     }
     478             : 
     479          26 :     if (m_bIsAMiraMonFile)
     480             :     {
     481           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     482             :                  "REL search failed for all bands in %s file",
     483             :                  osRELFile.c_str());
     484             :     }
     485          26 :     return "";
     486             : }
     487             : 
     488             : // Finds the metadata filename associated to osFileName (usually an IMG file)
     489         240 : CPLString MMRRel::GetAssociatedMetadataFileName(const CPLString &osFileName)
     490             : {
     491         240 :     if (osFileName.empty())
     492             :     {
     493           0 :         if (m_bIsAMiraMonFile)
     494           0 :             CPLError(CE_Failure, CPLE_OpenFailed, "Expected File name.");
     495           0 :         return "";
     496             :     }
     497             : 
     498             :     // If the string finishes in "I.rel" we consider it can be
     499             :     // the associated file to all bands that are documented in this file.
     500         240 :     if (cpl::ends_with(osFileName, pszExtRasterREL))
     501             :     {
     502          55 :         m_bIsAMiraMonFile = true;
     503          55 :         return osFileName;
     504             :     }
     505             : 
     506             :     // If the file is not a REL file, let's try to find the associated REL
     507             :     // It must be a IMG file.
     508         370 :     CPLString osExtension = CPLString(CPLGetExtensionSafe(osFileName).c_str());
     509         185 :     if (!EQUAL(osExtension, pszExtRaster + 1))
     510           4 :         return "";
     511             : 
     512             :     // Converting FileName.img to FileNameI.rel
     513         362 :     CPLString osRELFile = MMRGetSimpleMetadataName(osFileName);
     514         181 :     if (osRELFile.empty())
     515             :     {
     516           0 :         if (m_bIsAMiraMonFile)
     517             :         {
     518           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     519             :                      "Failing in conversion from .img to I.rel for %s file",
     520             :                      osFileName.c_str());
     521             :         }
     522           0 :         return "";
     523             :     }
     524             : 
     525             :     // Checking if the file exists
     526             :     VSIStatBufL sStat;
     527         181 :     if (VSIStatExL(osRELFile.c_str(), &sStat, VSI_STAT_EXISTS_FLAG) == 0)
     528          14 :         return MMRGetAReferenceToIMGFile(osFileName, osRELFile);
     529             : 
     530             :     // If the file I.rel doesn't exist then it has to be found
     531             :     // in the same folder than the .img file.
     532         334 :     const CPLString osPath = CPLGetPathSafe(osFileName);
     533         334 :     const CPLStringList folder(VSIReadDir(osPath.c_str()));
     534         167 :     const int size = folder.size();
     535             : 
     536        1279 :     for (int i = 0; i < size; i++)
     537             :     {
     538        1115 :         if (folder[i][0] == '.' || !strstr(folder[i], "I.rel"))
     539             :         {
     540        1078 :             continue;
     541             :         }
     542             : 
     543             :         const CPLString osFilePath =
     544          37 :             CPLFormFilenameSafe(osPath, folder[i], nullptr);
     545             : 
     546          37 :         osRELFile = MMRGetAReferenceToIMGFile(osFileName, osFilePath);
     547          37 :         if (!osRELFile.empty())
     548           3 :             return osRELFile;
     549             :     }
     550             : 
     551         164 :     if (m_bIsAMiraMonFile)
     552             :     {
     553           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "REL search failed for %s file",
     554             :                  osFileName.c_str());
     555             :     }
     556             : 
     557         164 :     return "";
     558             : }
     559             : 
     560             : /************************************************************************/
     561             : /*                         CheckBandInRel()                             */
     562             : /************************************************************************/
     563          12 : CPLErr MMRRel::CheckBandInRel(const CPLString &osRELFileName,
     564             :                               const CPLString &osIMGFile)
     565             : 
     566             : {
     567          24 :     CPLString osFieldNames;
     568          12 :     if (!GetMetadataValueDirectly(osRELFileName, SECTION_ATTRIBUTE_DATA,
     569          24 :                                   Key_IndexesNomsCamps, osFieldNames) ||
     570          12 :         osFieldNames.empty())
     571           0 :         return CE_Failure;
     572             : 
     573             :     // Separator ,
     574          24 :     const CPLStringList aosTokens(CSLTokenizeString2(osFieldNames, ",", 0));
     575          12 :     const int nTokenCount = CSLCount(aosTokens);
     576             : 
     577          12 :     if (!nTokenCount)
     578           0 :         return CE_Failure;
     579             : 
     580          24 :     CPLString osBandSectionKey;
     581          24 :     CPLString osBandSectionValue;
     582          24 :     for (int i = 0; i < nTokenCount; i++)
     583             :     {
     584          24 :         osBandSectionKey = KEY_NomCamp;
     585          24 :         osBandSectionKey.append("_");
     586          24 :         osBandSectionKey.append(aosTokens[i]);
     587             : 
     588          24 :         if (!GetMetadataValueDirectly(osRELFileName, SECTION_ATTRIBUTE_DATA,
     589          48 :                                       osBandSectionKey, osBandSectionValue) ||
     590          24 :             osBandSectionValue.empty())
     591           0 :             return CE_Failure;
     592             : 
     593          24 :         CPLString osAttributeDataName;
     594          24 :         osAttributeDataName = SECTION_ATTRIBUTE_DATA;
     595          24 :         osAttributeDataName.append(":");
     596          24 :         osAttributeDataName.append(osBandSectionValue.Trim());
     597             : 
     598          24 :         CPLString osRawBandFileName;
     599             : 
     600          24 :         if (!GetMetadataValueDirectly(osRELFileName, osAttributeDataName,
     601          48 :                                       KEY_NomFitxer, osRawBandFileName) ||
     602          24 :             osRawBandFileName.empty())
     603             :         {
     604           0 :             CPLString osBandFileName = MMRGetFileNameFromRelName(osRELFileName);
     605           0 :             if (osBandFileName.empty())
     606           0 :                 return CE_Failure;
     607             :         }
     608             :         else
     609             :         {
     610          24 :             if (!EQUAL(osRawBandFileName, osIMGFile))
     611          12 :                 continue;
     612          12 :             break;  // Found
     613             :         }
     614             :     }
     615             : 
     616          12 :     return CE_None;
     617             : }
     618             : 
     619       55496 : int MMRRel::IdentifySubdataSetFile(const CPLString &osFileName)
     620             : {
     621      110998 :     const CPLString osMMRPrefix = "MiraMonRaster:";
     622       55501 :     if (!STARTS_WITH(osFileName, osMMRPrefix))
     623       55490 :         return FALSE;
     624             : 
     625             :     // SUBDATASETS
     626          12 :     size_t nPos = osFileName.ifind(osMMRPrefix);
     627          12 :     if (nPos != 0)
     628           0 :         return GDAL_IDENTIFY_FALSE;
     629             : 
     630          24 :     CPLString osRELAndBandName = osFileName.substr(osMMRPrefix.size());
     631             : 
     632          24 :     const CPLStringList aosTokens(CSLTokenizeString2(osRELAndBandName, ",", 0));
     633          12 :     const int nTokens = CSLCount(aosTokens);
     634             :     // Getting the REL associated to the bands
     635             :     // We need the REL and at least one band (index + name).
     636          12 :     if (nTokens < 2)
     637           0 :         return GDAL_IDENTIFY_FALSE;
     638             : 
     639             :     // Let's remove "\"" if existent.
     640          24 :     CPLString osRELName = aosTokens[0];
     641          12 :     osRELName.replaceAll("\"", "");
     642             : 
     643             :     // It must be a I.rel file.
     644          12 :     if (!cpl::ends_with(osRELName, pszExtRasterREL))
     645           0 :         return GDAL_IDENTIFY_FALSE;
     646             : 
     647          12 :     if (MMCheck_REL_FILE(osRELName))
     648           0 :         return GDAL_IDENTIFY_FALSE;
     649             : 
     650             :     // Let's see if the specified bands are in the REL file
     651             :     // Getting the index + internal names of the bands
     652          24 :     for (int nIBand = 1; nIBand < nTokens; nIBand++)
     653             :     {
     654             :         // Let's check that this band (papszTokens[nIBand]) is in the REL file.
     655          12 :         CPLString osBandName = aosTokens[nIBand];
     656             : 
     657             :         // Let's remove "\"" if existent.
     658          12 :         osBandName.replaceAll("\"", "");
     659             : 
     660             :         // If it's not an IMG file return FALSE
     661             :         CPLString osExtension =
     662          12 :             CPLString(CPLGetExtensionSafe(osBandName).c_str());
     663          12 :         if (!EQUAL(osExtension, pszExtRaster + 1))
     664           0 :             return GDAL_IDENTIFY_FALSE;
     665             : 
     666          12 :         if (CE_None != CheckBandInRel(osRELName, osBandName))
     667           0 :             return GDAL_IDENTIFY_FALSE;
     668             :     }
     669          12 :     return GDAL_IDENTIFY_TRUE;
     670             : }
     671             : 
     672       55489 : int MMRRel::IdentifyFile(const GDALOpenInfo *poOpenInfo)
     673             : {
     674             :     // IMG files are shared for many drivers.
     675             :     // Identify will mark it as unknown.
     676             :     // Open function will try to open that, but as it has computation
     677             :     // cost is better avoid doing it here.
     678       55489 :     if (poOpenInfo->IsExtensionEqualToCI("IMG"))
     679         475 :         return GDAL_IDENTIFY_UNKNOWN;
     680             : 
     681       55009 :     if (!poOpenInfo->IsExtensionEqualToCI("REL"))
     682       54897 :         return GDAL_IDENTIFY_FALSE;
     683             : 
     684             :     // In fact, the file has to end with I.rel (pszExtRasterREL)
     685         115 :     if (!cpl::ends_with(std::string_view(poOpenInfo->pszFilename),
     686             :                         pszExtRasterREL))
     687           2 :         return GDAL_IDENTIFY_FALSE;
     688             : 
     689             :     // Some versions of REL files are not allowed.
     690         112 :     if (MMCheck_REL_FILE(poOpenInfo->pszFilename))
     691           2 :         return GDAL_IDENTIFY_FALSE;
     692             : 
     693         110 :     return GDAL_IDENTIFY_TRUE;
     694             : }
     695             : 
     696             : /************************************************************************/
     697             : /*                     GetMetadataValue()                               */
     698             : /************************************************************************/
     699         328 : bool MMRRel::GetMetadataValue(const CPLString &osMainSection,
     700             :                               const CPLString &osSubSection,
     701             :                               const CPLString &osSubSubSection,
     702             :                               const CPLString &osKey, CPLString &osValue)
     703             : {
     704         328 :     CPLAssert(
     705             :         isAMiraMonFile());  // Trying to access metadata from the wrong way
     706             : 
     707             :     // Searches in [pszMainSection:pszSubSection]
     708         656 :     CPLString osAttributeDataName;
     709         328 :     osAttributeDataName = osMainSection;
     710         328 :     osAttributeDataName.append(":");
     711         328 :     osAttributeDataName.append(osSubSection);
     712         328 :     osAttributeDataName.append(":");
     713         328 :     osAttributeDataName.append(osSubSubSection);
     714             : 
     715         328 :     addExcludedSectionKey(osAttributeDataName, osKey);
     716         328 :     osValue = GetValueFromSectionKeyFromREL(osAttributeDataName, osKey);
     717         328 :     if (osValue != m_szImprobableRELChain)
     718           0 :         return true;  // Found
     719             : 
     720             :     // If the value is not found then searches in [pszMainSection]
     721         328 :     addExcludedSectionKey(osSubSubSection, osKey);
     722         328 :     osValue = GetValueFromSectionKeyFromREL(osSubSubSection, osKey);
     723         328 :     if (osValue == m_szImprobableRELChain)
     724             :     {
     725          40 :         osValue = "";
     726          40 :         return false;  // Key not found
     727             :     }
     728         288 :     return true;  // Found
     729             : }
     730             : 
     731        1109 : bool MMRRel::GetMetadataValue(const CPLString &osMainSection,
     732             :                               const CPLString &osSubSection,
     733             :                               const CPLString &osKey, CPLString &osValue)
     734             : {
     735        1109 :     CPLAssert(
     736             :         isAMiraMonFile());  // Trying to access metadata from the wrong way
     737             : 
     738             :     // Searches in [pszMainSection:pszSubSection]
     739        2218 :     CPLString osAttributeDataName;
     740        1109 :     osAttributeDataName = osMainSection;
     741        1109 :     osAttributeDataName.append(":");
     742        1109 :     osAttributeDataName.append(osSubSection);
     743             : 
     744        1109 :     addExcludedSectionKey(osAttributeDataName, osKey);
     745        1109 :     osValue = GetValueFromSectionKeyFromREL(osAttributeDataName, osKey);
     746        1109 :     if (osValue != m_szImprobableRELChain)
     747         324 :         return true;  // Found
     748             : 
     749             :     // If the value is not found then searches in [pszMainSection]
     750         785 :     addExcludedSectionKey(osMainSection, osKey);
     751         785 :     osValue = GetValueFromSectionKeyFromREL(osMainSection, osKey);
     752         785 :     if (osValue == m_szImprobableRELChain)
     753             :     {
     754         509 :         osValue = "";
     755         509 :         return false;  // Key not found
     756             :     }
     757         276 :     return true;  // Found
     758             : }
     759             : 
     760        1017 : bool MMRRel::GetMetadataValue(const CPLString &osSection,
     761             :                               const CPLString &osKey, CPLString &osValue)
     762             : {
     763        1017 :     CPLAssert(
     764             :         isAMiraMonFile());  // Trying to access metadata from the wrong way
     765             : 
     766        1017 :     addExcludedSectionKey(osSection, osKey);
     767        1017 :     osValue = GetValueFromSectionKeyFromREL(osSection, osKey);
     768        1017 :     if (osValue == m_szImprobableRELChain)
     769             :     {
     770          14 :         osValue = "";
     771          14 :         return false;  // Key not found
     772             :     }
     773        1003 :     return true;  // Found
     774             : }
     775             : 
     776          82 : void MMRRel::UpdateRELNameChar(const CPLString &osRelFileNameIn)
     777             : {
     778          82 :     m_osRelFileName = osRelFileNameIn;
     779          82 : }
     780             : 
     781             : /************************************************************************/
     782             : /*                          ParseBandInfo()                             */
     783             : /************************************************************************/
     784          78 : CPLErr MMRRel::ParseBandInfo()
     785             : {
     786          78 :     m_nBands = 0;
     787             : 
     788         156 :     CPLString osFieldNames;
     789          78 :     if (!GetMetadataValue(SECTION_ATTRIBUTE_DATA, Key_IndexesNomsCamps,
     790         155 :                           osFieldNames) ||
     791          77 :         osFieldNames.empty())
     792             :     {
     793           2 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     794             :                  "%s-%s section-key should exist in %s.",
     795             :                  SECTION_ATTRIBUTE_DATA, Key_IndexesNomsCamps,
     796             :                  m_osRelFileName.c_str());
     797           2 :         return CE_Failure;
     798             :     }
     799             : 
     800             :     // Separator ,
     801         152 :     const CPLStringList aosTokens(CSLTokenizeString2(osFieldNames, ",", 0));
     802          76 :     const int nMaxBands = CSLCount(aosTokens);
     803             : 
     804          76 :     if (!nMaxBands)
     805             :     {
     806           0 :         CPLError(CE_Failure, CPLE_AssertionFailed, "No bands in file %s.",
     807             :                  m_osRelFileName.c_str());
     808           0 :         return CE_Failure;
     809             :     }
     810             : 
     811         152 :     CPLString osBandSectionKey;
     812         152 :     CPLString osBandSectionValue;
     813             : 
     814             :     int nNBand;
     815          76 :     if (m_papoSDSBands.size())
     816           6 :         nNBand = static_cast<int>(m_papoSDSBands.size());
     817             :     else
     818          70 :         nNBand = nMaxBands;
     819             : 
     820          76 :     m_oBands.reserve(nNBand);
     821             : 
     822         170 :     for (int i = 0; i < nMaxBands; i++)
     823             :     {
     824         100 :         osBandSectionKey = KEY_NomCamp;
     825         100 :         osBandSectionKey.append("_");
     826         100 :         osBandSectionKey.append(aosTokens[i]);
     827             : 
     828         100 :         if (!GetMetadataValue(SECTION_ATTRIBUTE_DATA, osBandSectionKey,
     829         199 :                               osBandSectionValue) ||
     830          99 :             osBandSectionValue.empty())
     831           1 :             continue;
     832             : 
     833          99 :         if (m_papoSDSBands.size())
     834             :         {
     835          18 :             CPLString osRawBandFileName;
     836          18 :             if (!GetMetadataValue(SECTION_ATTRIBUTE_DATA, osBandSectionValue,
     837          36 :                                   KEY_NomFitxer, osRawBandFileName) ||
     838          18 :                 osRawBandFileName.empty())
     839           0 :                 return CE_Failure;
     840             : 
     841             :             // I'm in a Subataset
     842             :             size_t nISDSBand;
     843          30 :             for (nISDSBand = 0; nISDSBand < m_papoSDSBands.size(); nISDSBand++)
     844             :             {
     845          18 :                 if (m_papoSDSBands[nISDSBand] == osRawBandFileName)
     846           6 :                     break;
     847             :             }
     848          18 :             if (nISDSBand == m_papoSDSBands.size())
     849          12 :                 continue;
     850             :         }
     851             : 
     852          87 :         if (m_nBands >= nNBand)
     853           0 :             break;
     854             : 
     855             :         m_oBands.emplace_back(
     856          87 :             std::make_unique<MMRBand>(*this, osBandSectionValue.Trim()));
     857             : 
     858          87 :         if (!m_oBands[m_nBands]->IsValid())
     859             :         {
     860             :             // This band is not been completed
     861           6 :             return CE_Failure;
     862             :         }
     863             : 
     864          81 :         m_nBands++;
     865             :     }
     866             : 
     867          70 :     return CE_None;
     868             : }
     869             : 
     870         128 : int MMRRel::GetColumnsNumberFromREL()
     871             : {
     872             :     // Number of columns of the subdataset (if exist)
     873             :     // Section [OVERVIEW:ASPECTES_TECNICS] in rel file
     874         256 :     CPLString osValue;
     875             : 
     876         256 :     if (!GetMetadataValue(SECTION_OVVW_ASPECTES_TECNICS, "columns", osValue) ||
     877         128 :         osValue.empty())
     878           0 :         return 0;  // Default value
     879             : 
     880             :     int nValue;
     881         128 :     if (1 != sscanf(osValue, "%d", &nValue))
     882           0 :         return 0;  // Default value
     883             : 
     884         128 :     return nValue;
     885             : }
     886             : 
     887         128 : int MMRRel::GetRowsNumberFromREL()
     888             : {
     889             :     // Number of columns of the subdataset (if exist)
     890             :     // Section [OVERVIEW:ASPECTES_TECNICS] in rel file
     891             :     // Key raws
     892         256 :     CPLString osValue;
     893             : 
     894         256 :     if (!GetMetadataValue(SECTION_OVVW_ASPECTES_TECNICS, "rows", osValue) ||
     895         128 :         osValue.empty())
     896           0 :         return 0;  // Default value
     897             : 
     898             :     int nValue;
     899         128 :     if (1 != sscanf(osValue, "%d", &nValue))
     900           0 :         return 0;  // Default value
     901             : 
     902         128 :     return nValue;
     903             : }
     904             : 
     905             : /************************************************************************/
     906             : /*                     Preserving metadata                              */
     907             : /************************************************************************/
     908          63 : void MMRRel::RELToGDALMetadata(GDALDataset *poDS)
     909             : {
     910          63 :     if (!m_pRELFile)
     911             :     {
     912           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     913             :                  "REL file cannot be opened: \"%s\"", m_osRelFileName.c_str());
     914           0 :         return;
     915             :     }
     916             : 
     917         126 :     CPLString osCurrentSection;
     918         126 :     CPLString osPendingKey, osPendingValue;
     919             : 
     920        6994 :     auto isExcluded = [&](const CPLString &section, const CPLString &key)
     921             :     {
     922       20146 :         return GetExcludedMetadata().count({section, key}) ||
     923       20146 :                GetExcludedMetadata().count({section, ""});
     924          63 :     };
     925             : 
     926             :     const char *pszLine;
     927             : 
     928          63 :     VSIFSeekL(m_pRELFile, 0, SEEK_SET);
     929       10488 :     while ((pszLine = CPLReadLine2L(m_pRELFile, 10000, nullptr)) != nullptr)
     930             :     {
     931       10425 :         CPLString rawLine = pszLine;
     932             : 
     933       10425 :         rawLine.Recode(CPL_ENC_ISO8859_1, CPL_ENC_UTF8);
     934       10425 :         rawLine.Trim();
     935             : 
     936       10425 :         if (rawLine.empty() || rawLine[0] == ';' || rawLine[0] == '#')
     937        1685 :             continue;
     938             : 
     939        8740 :         if (rawLine[0] == '[' && rawLine[rawLine.size() - 1] == ']')
     940             :         {
     941             :             // Saves last key
     942        1746 :             if (!osPendingKey.empty())
     943             :             {
     944        1683 :                 if (!isExcluded(osCurrentSection, osPendingKey))
     945             :                 {
     946             :                     CPLString fullKey =
     947        4692 :                         osCurrentSection + m_SecKeySeparator + osPendingKey;
     948             : 
     949        1564 :                     poDS->SetMetadataItem(fullKey.c_str(),
     950        1564 :                                           osPendingValue.Trim().c_str(),
     951        1564 :                                           m_kMetadataDomain);
     952             :                 }
     953        1683 :                 osPendingKey.clear();
     954        1683 :                 osPendingValue.clear();
     955             :             }
     956             : 
     957        1746 :             osCurrentSection = rawLine.substr(1, rawLine.size() - 2);
     958        1746 :             osCurrentSection.Trim();
     959        1746 :             continue;
     960             :         }
     961             : 
     962        6994 :         size_t equalPos = rawLine.find('=');
     963        6994 :         if (equalPos != CPLString::npos)
     964             :         {
     965             :             // Desa clau anterior
     966        6994 :             if (!osPendingKey.empty())
     967             :             {
     968        5248 :                 if (!isExcluded(osCurrentSection, osPendingKey))
     969             :                 {
     970             :                     CPLString fullKey =
     971       13614 :                         osCurrentSection + m_SecKeySeparator + osPendingKey;
     972             : 
     973        4538 :                     poDS->SetMetadataItem(fullKey.c_str(),
     974        4538 :                                           osPendingValue.Trim().c_str(),
     975        4538 :                                           m_kMetadataDomain);
     976             :                 }
     977             :             }
     978             : 
     979        6994 :             osPendingKey = rawLine.substr(0, equalPos);
     980        6994 :             osPendingValue = rawLine.substr(equalPos + 1);
     981        6994 :             osPendingKey.Trim();
     982        6994 :             osPendingValue.Trim();
     983             :         }
     984           0 :         else if (!osPendingKey.empty())
     985             :         {
     986           0 :             osPendingValue += "\n" + rawLine;
     987             :         }
     988             :     }
     989             : 
     990             :     // Saves last key
     991          63 :     if (!osPendingKey.empty())
     992             :     {
     993         189 :         CPLString fullKey = osCurrentSection + m_SecKeySeparator + osPendingKey;
     994          63 :         if (!isExcluded(osCurrentSection, osPendingKey))
     995          56 :             poDS->SetMetadataItem(fullKey.c_str(),
     996          56 :                                   osPendingValue.Trim().c_str(),
     997          56 :                                   m_kMetadataDomain);
     998             :     }
     999             : }

Generated by: LCOV version 1.14