LCOV - code coverage report
Current view: top level - src/engraving/rw - mscloader.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 81.7 % 115 94
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : #include "mscloader.h"
      23              : 
      24              : #include <memory>
      25              : 
      26              : #include "global/io/buffer.h"
      27              : #include "global/types/retval.h"
      28              : 
      29              : #include "../engravingerrors.h"
      30              : 
      31              : #include "../types/types.h"
      32              : 
      33              : #include "../dom/masterscore.h"
      34              : #include "../dom/excerpt.h"
      35              : #include "../dom/imageStore.h"
      36              : 
      37              : #include "engraving/automation/automationdata.h"
      38              : #include "engraving/automation/internal/automationrw.h"
      39              : 
      40              : #include "compat/compatutils.h"
      41              : #include "compat/readstyle.h"
      42              : 
      43              : #include "rwregister.h"
      44              : #include "xmlreader.h"
      45              : #include "inoutdata.h"
      46              : 
      47              : #include "log.h"
      48              : 
      49              : using namespace mu;
      50              : using namespace muse;
      51              : using namespace muse::io;
      52              : using namespace mu::engraving;
      53              : using namespace mu::engraving::rw;
      54              : 
      55         1019 : static RetVal<IReaderPtr> makeReader(int version, bool ignoreVersionError)
      56              : {
      57         1019 :     if (!ignoreVersionError) {
      58         1012 :         if (version > Constants::MSC_VERSION) {
      59            0 :             return RetVal<IReaderPtr>(make_ret(Err::FileTooNew));
      60              :         }
      61              : 
      62         1012 :         if (version < 114) {
      63            0 :             return RetVal<IReaderPtr>(make_ret(Err::FileTooOld));
      64              :         }
      65              : 
      66         1012 :         if (version == 300) {
      67            0 :             return RetVal<IReaderPtr>(make_ret(Err::FileOld300Format));
      68              :         }
      69              :     }
      70              : 
      71         1019 :     return RetVal<IReaderPtr>::make_ok(RWRegister::reader(version));
      72              : }
      73              : 
      74         1008 : Ret MscLoader::loadMscz(MasterScore* masterScore, const MscReader& mscReader, rw::ReadInOutData* inOut,
      75              :                         bool ignoreVersionError)
      76              : {
      77         1016 :     TRACEFUNC;
      78              : 
      79              :     using namespace mu::engraving;
      80              : 
      81         1008 :     IF_ASSERT_FAILED(mscReader.isOpened()) {
      82            0 :         return make_ret(Err::FileOpenError, mscReader.params().filePath);
      83              :     }
      84              : 
      85         1008 :     ScoreLoad sl;
      86              : 
      87         1008 :     if (mscReader.isContainer()) {
      88              :         // Read style
      89              :         {
      90           62 :             ByteArray styleData = mscReader.readStyleFile();
      91           62 :             if (!styleData.empty()) {
      92           61 :                 auto buf = Buffer::opened(IODevice::ReadOnly, &styleData);
      93           61 :                 masterScore->style().read(&buf);
      94           61 :                 if (inOut) {
      95            0 :                     inOut->originalSpatium = masterScore->style().spatium();
      96              :                 }
      97           61 :             }
      98           62 :         }
      99              : 
     100              :         // Read ChordList
     101              :         {
     102           62 :             bool chordListOk = false;
     103           62 :             ByteArray chordListData = mscReader.readChordListFile();
     104           62 :             if (!chordListData.empty()) {
     105            0 :                 auto buf = Buffer::opened(IODevice::ReadOnly, &chordListData);
     106              : 
     107            0 :                 chordListOk = masterScore->chordList()->read(&buf);
     108            0 :             }
     109              : 
     110           62 :             masterScore->chordList()->setCustomChordList(chordListOk);
     111              : 
     112           62 :             if (!chordListOk) {
     113              :                 // See also ReadChordListHook::validate()
     114           62 :                 MStyle& style = masterScore->style();
     115           62 :                 ChordList* chordList = masterScore->chordList();
     116              : 
     117           62 :                 bool custom = style.styleV(Sid::chordStyle).value<ChordStylePreset>() == ChordStylePreset::CUSTOM;
     118           62 :                 chordList->setCustomChordList(custom);
     119              : 
     120              :                 // Ensure that `checkChordList` loads the default chord list
     121           62 :                 chordList->unload();
     122              :             }
     123           62 :         }
     124              : 
     125              :         // Read images
     126              :         {
     127           62 :             if (!MScore::noImages) {
     128           62 :                 std::vector<String> images = mscReader.imageFileNames();
     129           62 :                 for (const String& name : images) {
     130            0 :                     imageStore.add(name.toStdString(), mscReader.readImageFile(name));
     131              :                 }
     132           62 :             }
     133              :         }
     134              :     }
     135              : 
     136         1008 :     ReadInOutData masterReadOutData;
     137         1008 :     if (!inOut) {
     138         1008 :         inOut = &masterReadOutData;
     139              :     }
     140              : 
     141         1008 :     Ret ret = muse::make_ok();
     142              : 
     143              :     // Read score
     144              :     {
     145         1008 :         ByteArray scoreData = mscReader.readScoreFile();
     146         1008 :         String docName = masterScore->fileInfo()->fileName().toString();
     147              : 
     148         1008 :         compat::ReadStyleHook styleHook(masterScore, scoreData, docName);
     149              : 
     150         1008 :         XmlReader xml(scoreData);
     151         1008 :         xml.setDocName(docName);
     152              : 
     153         1008 :         ret = readMasterScore(masterScore, xml, ignoreVersionError, inOut, &styleHook);
     154         1008 :     }
     155              : 
     156              :     // Read excerpts
     157         1008 :     if (ret && masterScore->mscVersion() >= 400 && mscReader.isContainer()) {
     158           62 :         std::vector<String> excerptFileNames = mscReader.excerptFileNames();
     159           73 :         for (const String& excerptFileName : excerptFileNames) {
     160           11 :             Score* partScore = masterScore->createScore();
     161              : 
     162           11 :             compat::ReadStyleHook::setupDefaultStyle(partScore);
     163              : 
     164           11 :             Excerpt* ex = new Excerpt(masterScore);
     165           11 :             ex->setExcerptScore(partScore);
     166           11 :             ex->setFileName(excerptFileName);
     167              : 
     168           11 :             ByteArray excerptStyleData = mscReader.readExcerptStyleFile(excerptFileName);
     169           11 :             auto excerptStyleBuf = Buffer::opened(IODevice::ReadOnly, &excerptStyleData);
     170           11 :             partScore->style().read(&excerptStyleBuf);
     171              : 
     172           11 :             ByteArray excerptData = mscReader.readExcerptFile(excerptFileName);
     173              : 
     174           11 :             XmlReader xml(excerptData);
     175           11 :             xml.setDocName(excerptFileName);
     176              : 
     177           11 :             ReadInOutData partReadInData;
     178           11 :             partReadInData.links = inOut->links;
     179              : 
     180           11 :             RetVal<IReaderPtr> reader = makeReader(masterScore->mscVersion(), ignoreVersionError);
     181           11 :             if (!reader.ret) {
     182            0 :                 ret = reader.ret;
     183            0 :                 break;
     184              :             }
     185              : 
     186           11 :             ret = reader.val->readScoreFile(partScore, xml, &partReadInData);
     187           11 :             if (!ret) {
     188            0 :                 break;
     189              :             }
     190              : 
     191           11 :             Excerpt::linkMeasures(partScore, masterScore);
     192              : 
     193           11 :             if (ex->name().empty()) {
     194              :                 // If no excerpt name tag was found while reading, try the "partName" meta tag
     195            0 :                 const String nameFromMeta = partScore->metaTag(u"partName");
     196              : 
     197            0 :                 if (nameFromMeta.empty()) {
     198              :                     // If that's also empty, fall back to the filename
     199            0 :                     ex->setName(excerptFileName, /*saveAndNotify=*/ false);
     200              :                 } else {
     201            0 :                     ex->setName(nameFromMeta, /*saveAndNotify=*/ false);
     202              :                 }
     203            0 :             }
     204              : 
     205           11 :             masterScore->addExcerpt(ex);
     206           11 :         }
     207           62 :     }
     208              : 
     209              :     // Compatibility conversions
     210              :     // NOTE: must be done after all score and parts have been read
     211         1008 :     compat::CompatUtils::doCompatibilityConversions(masterScore);
     212              : 
     213              :     // Read automation
     214              :     {
     215         1008 :         ByteArray ba = mscReader.readAutomationJsonFile();
     216         1008 :         AutomationDataPtr automationData = std::make_shared<AutomationData>();
     217         1008 :         AutomationRW::read(*automationData, ba);
     218         1008 :         masterScore->setAutomationData(automationData);
     219         1008 :     }
     220              : 
     221         1008 :     return ret;
     222         1008 : }
     223              : 
     224         1008 : Ret MscLoader::readMasterScore(MasterScore* score, XmlReader& e, bool ignoreVersionError, ReadInOutData* out,
     225              :                                compat::ReadStyleHook* styleHook)
     226              : {
     227         1008 :     while (e.readNextStartElement()) {
     228         1008 :         if (e.name() == "museScore") {
     229         1008 :             const String version = e.attribute("version");
     230         1008 :             const StringList sl = version.split(u'.');
     231         1008 :             score->setMscVersion(sl[0].toInt() * 100 + sl[1].toInt());
     232              : 
     233         1008 :             RetVal<IReaderPtr> reader = makeReader(score->mscVersion(), ignoreVersionError);
     234         1008 :             if (!reader.ret) {
     235            0 :                 return reader.ret;
     236              :             }
     237              : 
     238         1008 :             IF_ASSERT_FAILED(reader.val) {
     239            0 :                 return make_ret(Err::FileUnknownError);
     240              :             }
     241              : 
     242              :             //! NOTE We need to achieve that the default style corresponds to the version in which the score is created.
     243              :             //! The values that the user changed will be written on over (only they are stored in the `mscz` file)
     244              :             //! For version 4.0 (400), this does not need to be done,
     245              :             //! because starting from version 4.0 the entire style is stored in a file,
     246              :             //! respectively, the entire style will be loaded, which was when the score was created.
     247         1008 :             if (styleHook && score->mscVersion() < 400) {
     248          473 :                 styleHook->setupDefaultStyle();
     249              :             }
     250              : 
     251         1008 :             if (score->mscVersion() >= 400) {
     252              :                 //! NOTE: make sure we have a chord list
     253              :                 //! Load the default chord list otherwise
     254          535 :                 score->checkChordList();
     255              :             }
     256              : 
     257         1008 :             Ret ret = reader.val->readScoreFile(score, e, out);
     258              : 
     259         1008 :             score->setExcerptsChanged(false);
     260              : 
     261         1008 :             return ret;
     262         1008 :         } else {
     263            0 :             e.unknown();
     264              :         }
     265              :     }
     266              : 
     267            0 :     return make_ret(Err::FileCriticallyCorrupted, e.errorString());
     268              : }
        

Generated by: LCOV version 2.0-1