Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-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 "workspacefile.h"
23 :
24 : #include "global/io/buffer.h"
25 : #include "global/io/file.h"
26 : #include "global/serialization/xmlstreamreader.h"
27 : #include "global/serialization/xmlstreamwriter.h"
28 : #include "global/serialization/zipreader.h"
29 : #include "global/serialization/zipwriter.h"
30 :
31 : #include "workspaceerrors.h"
32 :
33 : #include "log.h"
34 :
35 : using namespace muse;
36 : using namespace muse::workspace;
37 :
38 4 : WorkspaceFile::WorkspaceFile(const io::path_t& filePath)
39 4 : : m_filePath(filePath)
40 4 : {}
41 :
42 0 : WorkspaceFile::WorkspaceFile(const io::path_t& filePath, const WorkspaceFile* other)
43 0 : : WorkspaceFile(filePath)
44 : {
45 0 : m_meta = other->m_meta;
46 0 : m_data = other->m_data;
47 0 : }
48 :
49 19 : io::path_t WorkspaceFile::filePath() const
50 : {
51 19 : return m_filePath;
52 : }
53 :
54 0 : void WorkspaceFile::redirect(const io::path_t& filePath)
55 : {
56 0 : m_filePath = filePath;
57 0 : }
58 :
59 3 : Ret WorkspaceFile::load()
60 : {
61 3 : m_data.clear();
62 3 : m_meta.clear();
63 :
64 3 : ZipReader zip(m_filePath);
65 :
66 3 : Meta::read(zip, m_meta);
67 3 : if (m_meta.empty() || zip.hasError()) {
68 0 : LOGE() << "failed read meta data";
69 0 : return make_ret(Err::FailedUnPack);
70 : }
71 :
72 3 : std::vector<ZipReader::FileInfo> files = zip.fileInfoList();
73 12 : for (const ZipReader::FileInfo& fi : files) {
74 9 : if (fi.isFile) {
75 6 : ByteArray fdata = zip.fileData(fi.filePath.toStdString());
76 6 : m_data[fi.filePath.toStdString()] = fdata.toQByteArray();
77 6 : }
78 : }
79 :
80 3 : if (zip.hasError()) {
81 0 : LOGE() << "failed read data";
82 0 : return make_ret(Err::FailedUnPack);
83 : }
84 :
85 3 : zip.close();
86 :
87 3 : m_needSave = false;
88 :
89 3 : return make_ret(Ret::Code::Ok);
90 3 : }
91 :
92 0 : Ret WorkspaceFile::save()
93 : {
94 0 : std::vector<std::string> paths;
95 0 : for (const auto& d : m_data) {
96 0 : paths.push_back(d.first);
97 : }
98 :
99 0 : auto outBuf = io::Buffer::opened(io::IODevice::WriteOnly);
100 0 : ZipWriter zip(&outBuf);
101 :
102 0 : Container::write(zip, paths);
103 0 : if (zip.hasError()) {
104 0 : LOGE() << "failed write files to zip";
105 0 : return make_ret(Err::FailedPack);
106 : }
107 :
108 0 : Meta::write(zip, m_meta);
109 0 : if (zip.hasError()) {
110 0 : LOGE() << "failed write files to zip";
111 0 : return make_ret(Err::FailedPack);
112 : }
113 :
114 0 : for (const auto& d : m_data) {
115 0 : if (d.second.isEmpty()) {
116 0 : continue;
117 : }
118 :
119 0 : zip.addFile(d.first, ByteArray::fromQByteArray(d.second));
120 : }
121 :
122 0 : if (zip.hasError()) {
123 0 : LOGE() << "failed write files to zip";
124 0 : return make_ret(Err::FailedPack);
125 : }
126 :
127 0 : zip.close();
128 :
129 0 : const Ret ret = io::File::writeFile(m_filePath, outBuf.data());
130 0 : if (!ret) {
131 0 : return ret;
132 : }
133 :
134 0 : m_needSave = false;
135 :
136 0 : return make_ok();
137 0 : }
138 :
139 0 : void WorkspaceFile::Container::write(ZipWriter& zip, const std::vector<std::string>& paths)
140 : {
141 0 : ByteArray data;
142 0 : auto buf = io::Buffer::opened(io::IODevice::WriteOnly, &data);
143 0 : XmlStreamWriter xml(&buf);
144 0 : xml.startDocument();
145 :
146 0 : xml.startElement("container");
147 0 : xml.startElement("rootfiles");
148 :
149 0 : for (const std::string& f : paths) {
150 0 : xml.element("rootfile", { { "full-path", f } });
151 : }
152 :
153 0 : xml.endElement();
154 0 : xml.endElement();
155 0 : xml.flush();
156 :
157 0 : zip.addFile("META-INF/container.xml", data);
158 0 : }
159 :
160 0 : void WorkspaceFile::Meta::write(ZipWriter& zip, const std::map<std::string, Val>& meta)
161 : {
162 0 : ByteArray data;
163 0 : auto buf = io::Buffer::opened(io::IODevice::WriteOnly, &data);
164 0 : XmlStreamWriter xml(&buf);
165 0 : xml.startDocument();
166 :
167 0 : xml.startElement("metadata");
168 0 : xml.startElement("items");
169 :
170 0 : for (const auto& m : meta) {
171 0 : xml.element("item", { { "name", m.first } }, m.second.toString());
172 : }
173 :
174 0 : xml.endElement();
175 0 : xml.endElement();
176 0 : xml.flush();
177 :
178 0 : zip.addFile("META-INF/metadata.xml", data);
179 0 : }
180 :
181 3 : void WorkspaceFile::Meta::read(ZipReader& zip, std::map<std::string, Val>& meta)
182 : {
183 3 : ByteArray data = zip.fileData("META-INF/metadata.xml");
184 3 : if (data.empty()) {
185 0 : LOGE() << "not found META-INF/metadata.xml";
186 0 : return;
187 : }
188 :
189 3 : XmlStreamReader xml(data);
190 6 : while (xml.readNextStartElement()) {
191 3 : if ("metadata" != xml.name()) {
192 0 : xml.skipCurrentElement();
193 0 : continue;
194 : }
195 :
196 6 : while (xml.readNextStartElement()) {
197 3 : if ("items" != xml.name()) {
198 0 : xml.skipCurrentElement();
199 0 : continue;
200 : }
201 :
202 6 : while (xml.readNextStartElement()) {
203 3 : if ("item" != xml.name()) {
204 0 : xml.skipCurrentElement();
205 0 : continue;
206 : }
207 :
208 3 : std::string name = xml.attribute("name").toStdString();
209 3 : std::string val = xml.readText().toStdString();
210 3 : meta[name] = Val(val);
211 3 : }
212 : }
213 : }
214 3 : }
215 :
216 6 : bool WorkspaceFile::isLoaded() const
217 : {
218 6 : return !m_meta.empty();
219 : }
220 :
221 3 : bool WorkspaceFile::isNeedSave() const
222 : {
223 3 : return m_needSave;
224 : }
225 :
226 0 : Val WorkspaceFile::meta(const std::string& key) const
227 : {
228 0 : auto it = m_meta.find(key);
229 0 : if (it != m_meta.end()) {
230 0 : return it->second;
231 : }
232 0 : return Val();
233 : }
234 :
235 0 : void WorkspaceFile::setMeta(const std::string& key, const Val& val)
236 : {
237 0 : if (this->meta(key) == val) {
238 0 : return;
239 : }
240 :
241 0 : markDirty();
242 :
243 0 : m_meta[key] = val;
244 : }
245 :
246 1 : QByteArray WorkspaceFile::data(const std::string& name) const
247 : {
248 1 : auto it = m_data.find(name);
249 1 : if (it != m_data.end()) {
250 0 : return it->second;
251 : }
252 1 : return QByteArray();
253 : }
254 :
255 0 : void WorkspaceFile::setData(const std::string& name, const QByteArray& data)
256 : {
257 0 : if (this->data(name) == data) {
258 0 : return;
259 : }
260 :
261 0 : markDirty();
262 :
263 0 : m_data[name] = data;
264 : }
265 :
266 0 : void WorkspaceFile::markDirty()
267 : {
268 0 : m_needSave = true;
269 0 : }
|