LCOV - code coverage report
Current view: top level - muse/framework/workspace/internal - workspacefile.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 32.1 % 140 45
Test Date: 2026-11-05 04:07:02

            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 : }
        

Generated by: LCOV version 2.0-1