LCOV - code coverage report
Current view: top level - src/engraving/rw/read302 - read302.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 65.9 % 217 143
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 "read302.h"
      23              : 
      24              : #include "global/defer.h"
      25              : 
      26              : #include "iengravingfont.h"
      27              : 
      28              : #include "rw/compat/compatutils.h"
      29              : #include "style/style.h"
      30              : 
      31              : #include "dom/excerpt.h"
      32              : #include "dom/factory.h"
      33              : #include "dom/masterscore.h"
      34              : #include "dom/part.h"
      35              : #include "dom/score.h"
      36              : #include "dom/scoreorder.h"
      37              : #include "dom/spanner.h"
      38              : #include "dom/staff.h"
      39              : #include "dom/text.h"
      40              : 
      41              : #include "../read400/staffrw.h"
      42              : #include "../read400/tread.h"
      43              : #include "../compat/readstyle.h"
      44              : 
      45              : #include "log.h"
      46              : 
      47              : using namespace mu;
      48              : using namespace mu::engraving;
      49              : using namespace mu::engraving::rw;
      50              : using namespace mu::engraving::read400;
      51              : using namespace mu::engraving::read302;
      52              : using namespace mu::engraving::compat;
      53              : 
      54          430 : bool Read302::readScoreTag(Score* score, XmlReader& e, ReadContext& ctx)
      55              : {
      56         8939 :     while (e.readNextStartElement()) {
      57         8509 :         ctx.setTrack(muse::nidx);
      58         8509 :         const AsciiStringView tag(e.name());
      59         8509 :         if (tag == "Staff") {
      60          751 :             read400::StaffRead::readStaff(score, e, ctx);
      61         7758 :         } else if (tag == "Omr") {
      62            0 :             e.skipCurrentElement();
      63         7758 :         } else if (tag == "Audio") {
      64            0 :             e.skipCurrentElement();
      65         7758 :         } else if (tag == "showOmr") {
      66            0 :             e.skipCurrentElement();
      67         7758 :         } else if (tag == "playMode") {
      68            0 :             e.skipCurrentElement();
      69         7758 :         } else if (tag == "LayerTag") {
      70           53 :             e.skipCurrentElement();
      71         7705 :         } else if (tag == "Layer") {
      72            0 :             e.skipCurrentElement();
      73         7705 :         } else if (tag == "currentLayer") {
      74           53 :             e.skipCurrentElement();
      75         7652 :         } else if (tag == "Synthesizer") {
      76            8 :             score->m_synthesizerState.read(e);
      77         7644 :         } else if (tag == "page-offset") {
      78            0 :             score->m_pageNumberOffset = e.readInt();
      79         7644 :         } else if (tag == "Division") {
      80          430 :             score->m_fileDivision = e.readInt();
      81         7214 :         } else if (tag == "showInvisible") {
      82          430 :             score->m_showInvisible = e.readInt();
      83         6784 :         } else if (tag == "showUnprintable") {
      84          430 :             score->m_showUnprintable = e.readInt();
      85         6354 :         } else if (tag == "showFrames") {
      86          430 :             score->m_showFrames = e.readInt();
      87         5924 :         } else if (tag == "showMargins") {
      88          430 :             score->m_showPageborders = e.readInt();
      89         5494 :         } else if (tag == "markIrregularMeasures") {
      90            0 :             score->m_markIrregularMeasures = e.readInt();
      91         5494 :         } else if (tag == "Style") {
      92          430 :             double sp = score->style().value(Sid::spatium).toReal();
      93              : 
      94          430 :             ReadStyleHook::readStyleTag(score, e);
      95              : 
      96          430 :             if (ctx.overrideSpatium()) {
      97            0 :                 ctx.setOriginalSpatium(score->style().spatium());
      98            0 :                 score->style().set(Sid::spatium, sp);
      99              :             }
     100          430 :             score->m_engravingFont = score->engravingFonts()->fontByName(score->style().styleSt(Sid::musicalSymbolFont).toStdString());
     101         5064 :         } else if (tag == "copyright" || tag == "rights") {
     102            0 :             score->setMetaTag(u"copyright", Text::readXmlText(e, score));
     103         5064 :         } else if (tag == "movement-number") {
     104            0 :             score->setMetaTag(u"movementNumber", e.readText());
     105         5064 :         } else if (tag == "movement-title") {
     106            0 :             score->setMetaTag(u"movementTitle", e.readText());
     107         5064 :         } else if (tag == "work-number") {
     108            0 :             score->setMetaTag(u"workNumber", e.readText());
     109         5064 :         } else if (tag == "work-title") {
     110            0 :             score->setMetaTag(u"workTitle", e.readText());
     111         5064 :         } else if (tag == "source") {
     112            0 :             score->setMetaTag(u"source", e.readText());
     113         5064 :         } else if (tag == "metaTag") {
     114         4364 :             String name = e.attribute("name");
     115         4364 :             score->setMetaTag(name, e.readText());
     116         5064 :         } else if (tag == "Order") {
     117            3 :             ScoreOrder order;
     118            3 :             order.read(e);
     119            3 :             if (order.isValid()) {
     120            3 :                 score->setScoreOrder(order);
     121              :             }
     122          700 :         } else if (tag == "Part") {
     123          665 :             Part* part = new Part(score);
     124          665 :             read400::TRead::read(part, e, ctx);
     125          665 :             score->appendPart(part);
     126           32 :         } else if ((tag == "HairPin")
     127           32 :                    || (tag == "Ottava")
     128           32 :                    || (tag == "TextLine")
     129           32 :                    || (tag == "Volta")
     130           32 :                    || (tag == "Trill")
     131           32 :                    || (tag == "Slur")
     132           64 :                    || (tag == "Pedal")) {
     133            0 :             Spanner* s = toSpanner(Factory::createItemByName(tag, score->dummy()));
     134            0 :             read400::TRead::readItem(s, e, ctx);
     135            0 :             score->addSpanner(s);
     136           32 :         } else if (e.name() == "Tracklist") {
     137            0 :             int strack = e.intAttribute("sTrack",   -1);
     138            0 :             int dtrack = e.intAttribute("dstTrack", -1);
     139            0 :             if (strack != -1 && dtrack != -1) {
     140            0 :                 ctx.tracks().insert({ strack, dtrack });
     141              :             }
     142            0 :             e.skipCurrentElement();
     143           32 :         } else if (tag == "Score") {            // recursion
     144           16 :             ctx.tracks().clear();                 // ???
     145           16 :             MasterScore* m = score->masterScore();
     146           16 :             Score* s = m->createScore();
     147              : 
     148           16 :             ReadStyleHook::setupDefaultStyle(s);
     149              : 
     150           16 :             Excerpt* ex = new Excerpt(m);
     151           16 :             ex->setExcerptScore(s);
     152           16 :             ctx.setLastMeasure(nullptr);
     153              : 
     154           16 :             Score* curScore = ctx.score();
     155           16 :             ctx.setScore(s);
     156              : 
     157           16 :             s->setIsOpen(true);
     158           16 :             readScoreTag(s, e, ctx);
     159              : 
     160           16 :             ctx.setScore(curScore);
     161              : 
     162           16 :             Excerpt::linkMeasures(s, m);
     163           16 :             ex->setTracksMapping(ctx.tracks());
     164           16 :             m->addExcerpt(ex);
     165           16 :         } else if (tag == "name") {
     166           16 :             String n = e.readText();
     167           16 :             if (!score->isMaster()) {     //ignore the name if it's not a child score
     168           16 :                 score->excerpt()->setName(n, /*saveAndNotify=*/ false);
     169              :             }
     170           16 :         } else if (tag == "layoutMode") {
     171            0 :             if (ctx.forcePageMode()) {
     172            0 :                 e.skipCurrentElement();
     173            0 :                 continue;
     174              :             }
     175            0 :             String s = e.readText();
     176            0 :             if (s == "line") {
     177            0 :                 score->setLayoutMode(LayoutMode::LINE);
     178            0 :             } else if (s == "system") {
     179            0 :                 score->setLayoutMode(LayoutMode::SYSTEM);
     180              :             } else {
     181            0 :                 LOGD("layoutMode: %s", muPrintable(s));
     182              :             }
     183            0 :         } else {
     184            0 :             e.unknown();
     185              :         }
     186              :     }
     187          430 :     ctx.reconnectBrokenConnectors();
     188          430 :     if (e.error() != muse::XmlStreamReader::NoError) {
     189            0 :         if (e.error() == muse::XmlStreamReader::CustomError) {
     190            0 :             LOGE() << e.errorString();
     191              :         } else {
     192            0 :             LOGE() << String(u"XML read error at byte offset %1: %2").arg(e.byteOffset())
     193            0 :                 .arg(String::fromAscii(e.name().ascii()));
     194              :         }
     195            0 :         return false;
     196              :     }
     197              : 
     198          430 :     score->m_fileDivision = Constants::DIVISION;
     199              : 
     200          430 :     if (score->mscVersion() == 302) {
     201              :         // MuseScore 3.6.x scores had some wrong instrument IDs
     202           14 :         for (Part* part : score->parts()) {
     203           14 :             for (const auto& pair : part->instruments()) {
     204            7 :                 fixInstrumentId(pair.second);
     205              :             }
     206              :         }
     207              :     } else {
     208              :         // Older scores had no IDs at all
     209         1081 :         for (Part* part : score->parts()) {
     210         1341 :             for (const auto& pair : part->instruments()) {
     211          683 :                 pair.second->updateInstrumentId();
     212              :             }
     213              :         }
     214              :     }
     215              : 
     216          430 :     score->updateTicksAndTimeSigMap();
     217          430 :     if (score->isMaster()) {
     218              :         // While reading the score, some elements might use `score->repeatList()` (which is incorrect
     219              :         // anyway, because the repeatList will be incomplete because the score is incomplete, but some
     220              :         // elements still do it).
     221              :         // `score->repeatList()` calls `_repeatList->update()`; the repeat list then thinks that it is
     222              :         // up-to-date from that point. But we weren't finished reading the score, so the score will still
     223              :         // change. We need to tell the repeat list about that, so that it will be updated next time
     224              :         // someone uses it.
     225          414 :         static_cast<MasterScore*>(score)->invalidateRepeatList();
     226              :     }
     227          430 :     score->connectTies();
     228          430 :     score->undoRemoveStaleTieJumpPoints(false);
     229              : 
     230         1095 :     for (Part* p : score->parts()) {
     231          665 :         p->updateHarmonyChannels(false);
     232              :     }
     233              : 
     234          430 :     score->masterScore()->rebuildMidiMapping();
     235          430 :     score->masterScore()->updateChannel();
     236              : 
     237         1181 :     for (Staff* staff : score->staves()) {
     238          751 :         staff->updateOttava();
     239              :     }
     240              : 
     241          430 :     if (score->isMaster()) {
     242          414 :         CompatUtils::assignInitialPartToExcerpts(score->masterScore()->excerpts());
     243              :     }
     244              : 
     245          430 :     return true;
     246              : }
     247              : 
     248          414 : muse::Ret Read302::readScoreFile(Score* score, XmlReader& e, ReadInOutData* out)
     249              : {
     250          414 :     ReadContext ctx(score);
     251          414 :     if (out) {
     252          414 :         if (out->overriddenSpatium.has_value()) {
     253            0 :             ctx.setSpatium(out->overriddenSpatium.value());
     254            0 :             ctx.setOverrideSpatium(true);
     255              :         }
     256              : 
     257          414 :         ctx.setPropertiesToSkip(out->propertiesToSkip);
     258          414 :         ctx.setForcePageMode(out->forcePageMode);
     259              :     }
     260              : 
     261          414 :     DEFER {
     262          414 :         if (out) {
     263          414 :             out->settingsCompat = std::move(ctx.settingCompat());
     264              :         }
     265          414 :     };
     266              : 
     267         1002 :     while (e.readNextStartElement()) {
     268          588 :         const AsciiStringView tag(e.name());
     269          588 :         if (tag == "programVersion") {
     270           87 :             score->setMscoreVersion(e.readText());
     271          501 :         } else if (tag == "programRevision") {
     272           87 :             score->setMscoreRevision(e.readInt(nullptr, 16));
     273          414 :         } else if (tag == "Score") {
     274          414 :             if (!readScoreTag(score, e, ctx)) {
     275            0 :                 if (e.error() == muse::XmlStreamReader::CustomError) {
     276            0 :                     return make_ret(Err::FileCriticallyCorrupted, e.errorString());
     277              :                 }
     278            0 :                 return make_ret(Err::FileBadFormat, e.errorString());
     279              :             }
     280              : 
     281          414 :             if (ctx.overrideSpatium() && out) {
     282            0 :                 out->originalSpatium = ctx.originalSpatium();
     283              :             }
     284            0 :         } else if (tag == "Revision") {
     285            0 :             e.skipCurrentElement();
     286              :         }
     287              :     }
     288              : 
     289          414 :     return muse::make_ok();
     290          414 : }
     291              : 
     292            7 : void Read302::fixInstrumentId(Instrument* instrument)
     293              : {
     294            7 :     String id = instrument->id();
     295            7 :     String trackName = instrument->trackName().toLower();
     296              : 
     297              :     // incorrect instrument IDs in 3.6.x
     298            7 :     if (id == u"Winds") {
     299            1 :         id = u"winds";
     300            6 :     } else if (id == u"harmonica-d12high-g") {
     301            0 :         id = u"harmonica-d10high-g";
     302            6 :     } else if (id == u"harmonica-d12f") {
     303            0 :         id = u"harmonica-d10f";
     304            6 :     } else if (id == u"harmonica-d12d") {
     305            0 :         id = u"harmonica-d10d";
     306            6 :     } else if (id == u"harmonica-d12c") {
     307            0 :         id = u"harmonica-d10c";
     308            6 :     } else if (id == u"harmonica-d12a") {
     309            0 :         id = u"harmonica-d10a";
     310            6 :     } else if (id == u"harmonica-d12-g") {
     311            0 :         id = u"harmonica-d10g";
     312            6 :     } else if (id == u"drumset" && trackName == u"percussion") {
     313            0 :         id = u"percussion";
     314            6 :     } else if (id == u"cymbal" && trackName == u"cymbals") {
     315            0 :         id = u"marching-cymbals";
     316            6 :     } else if (id == u"bass-drum" && trackName == u"bass drums") {
     317            0 :         id = u"marching-bass-drums";
     318            6 :     } else if (id.startsWith(u"mdl-")) {
     319              :         // See https://github.com/musescore/mdl/blob/master/resources/instruments/mdl_1_3_0.xml
     320            0 :         if (id == u"mdl-snareline" || id == u"mdl-snareline-a" || id == u"mdl-snaresolo" || id == u"mdl-snaresolo-a") {
     321            0 :             id = u"marching-snare";
     322            0 :         } else if (id == u"mdl-tenorline" || id == u"mdl-tenorsolo" || id == u"mdl-flubs") {
     323            0 :             id = u"marching-tenor-drums";
     324            0 :         } else if (id == u"mdl-bassline-10" || id == u"mdl-bassline-5") {
     325            0 :             id = u"marching-bass-drums";
     326            0 :         } else if (id == u"mdl-cymballine") {
     327            0 :             id = u"marching-cymbals";
     328            0 :         } else if (id == u"mdl-showtenorline" || id == u"mdl-rail" || id == u"mdl-drumset" || id == u"mdl-sampler") {
     329            0 :             id = u"drumset";
     330              :         }
     331              :     }
     332              : 
     333            7 :     instrument->setId(id);
     334            7 : }
     335              : 
     336            0 : bool Read302::pasteStaff(XmlReader&, Segment*, staff_idx_t, Fraction)
     337              : {
     338            0 :     UNREACHABLE;
     339              :     return false;
     340              : }
     341              : 
     342            0 : void Read302::pasteSymbols(XmlReader&, ChordRest*)
     343              : {
     344            0 :     UNREACHABLE;
     345              : }
     346              : 
     347            0 : void Read302::readTremoloCompat(compat::TremoloCompat*, XmlReader&)
     348              : {
     349            0 :     UNREACHABLE;
     350              : }
     351              : 
     352            0 : void Read302::doReadItem(EngravingItem*, XmlReader&)
     353              : {
     354            0 :     UNREACHABLE;
     355              : }
        

Generated by: LCOV version 2.0-1