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) 2026 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 "notationmnxreader.h"
23 : #include "import/mnximporter.h"
24 :
25 : #include <stdexcept>
26 :
27 : #include "engraving/dom/masterscore.h"
28 : #include "engraving/engravingerrors.h"
29 : #include "io/file.h"
30 : #include "log.h"
31 :
32 : #ifdef MNXDOM_SYSTEM
33 : #include <mnxdom/mnxdom.h>
34 : #else
35 : #include "mnxdom.h"
36 : #endif
37 :
38 : using namespace mu::iex::mnxio;
39 : using namespace mu::engraving;
40 : using namespace muse;
41 :
42 0 : const IMnxConfiguration* NotationMnxReader::mnxConfiguration() const
43 : {
44 0 : const auto configuration = m_mnxConfiguration();
45 0 : IF_ASSERT_FAILED(configuration) {
46 0 : throw std::runtime_error("MNX configuration is not available");
47 : }
48 0 : return configuration.get();
49 0 : }
50 :
51 85 : Ret NotationMnxReader::importJson(MasterScore* score, ByteArray&& jsonData, const io::path_t& path) const
52 : {
53 : try {
54 85 : auto doc = mnx::Document::create(jsonData.constData(), jsonData.size());
55 85 : jsonData.clear();
56 85 : if (!mnx::validation::schemaValidate(doc)) {
57 0 : LOGE() << path << " does not validate to embedded MNX schema.";
58 0 : const bool exactSchemaValidation = mnxConfiguration()->mnxRequireExactSchemaValidation();
59 0 : if (!exactSchemaValidation && mnx::validation::hasValidDocumentRoot(doc)) {
60 0 : LOGW() << path << " has a valid document root; importing with exact schema validation disabled.";
61 : } else {
62 0 : return make_ret(Ret::Code::NotSupported, TranslatableString("importexport/mnx", "File is not a valid MNX document.").str);
63 : }
64 : }
65 85 : LOGI() << "MNX import started: schema version=" << doc.mnx().version() << " path=" << path;
66 85 : if (doc.global().measures().empty()) {
67 0 : LOGE() << path << " contains no measures.";
68 0 : return make_ret(Ret::Code::NotSupported, TranslatableString("importexport/mnx", "File contains no measures.").str);
69 : }
70 85 : MnxImporter importer(score, std::move(doc));
71 85 : importer.importMnx();
72 85 : } catch (const std::exception& ex) {
73 0 : LOGE() << String::fromStdString(ex.what());
74 0 : return make_ret(Ret::Code::InternalError);
75 0 : }
76 :
77 85 : return make_ok();
78 : }
79 :
80 35 : Ret NotationMnxReader::importMnx(MasterScore* score, ByteArray&& mnxData, const io::path_t& path) const
81 : {
82 : /// @todo Eventually this will require unzipping the mnx archive and fishing out the json.
83 : /// Until the mnx archive format is specced out, we simply treat MNX as raw JSON.
84 35 : return importJson(score, std::move(mnxData), path);
85 : }
86 :
87 85 : Ret NotationMnxReader::read(MasterScore* score, const io::path_t& path, const Options&)
88 : {
89 85 : io::File mnxFile(path);
90 85 : if (!mnxFile.exists()) {
91 0 : return make_ret(Err::FileNotFound, path);
92 : }
93 :
94 85 : if (!mnxFile.open(io::IODevice::ReadOnly)) {
95 0 : LOGE() << "could not open MNX file: " << path.toString();
96 0 : return make_ret(Err::FileOpenError, path);
97 : }
98 :
99 85 : ByteArray data = mnxFile.readAll();
100 85 : mnxFile.close();
101 :
102 85 : std::string suffix = muse::io::suffix(path);
103 85 : if (suffix == "json") {
104 50 : return importJson(score, std::move(data), path);
105 35 : } else if (suffix == "mnx") {
106 35 : return importMnx(score, std::move(data), path);
107 : }
108 :
109 0 : return make_ret(Err::FileUnknownType, suffix);
110 85 : }
|