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

Generated by: LCOV version 1.14