[engraving] removed Qt dependencies
This commit is contained in:
parent
fd059563a0
commit
c83d6630a2
23 changed files with 1141 additions and 137 deletions
|
@ -98,6 +98,7 @@ QString SettingListModel::typeToString(Val::Type t) const
|
|||
case Val::Type::Undefined: return "Undefined";
|
||||
case Val::Type::Bool: return "Bool";
|
||||
case Val::Type::Int: return "Int";
|
||||
case Val::Type::Int64: return "Int";
|
||||
case Val::Type::Double: return "Double";
|
||||
case Val::Type::String: return "String";
|
||||
case Val::Type::Color: return "Color";
|
||||
|
|
|
@ -122,10 +122,12 @@ QString AdvancedPreferencesModel::typeToString(Val::Type type) const
|
|||
case Val::Type::Undefined: return "Undefined";
|
||||
case Val::Type::Bool: return "Bool";
|
||||
case Val::Type::Int: return "Int";
|
||||
case Val::Type::Int64: return "Int";
|
||||
case Val::Type::Double: return "Double";
|
||||
case Val::Type::String: return "String";
|
||||
case Val::Type::Color: return "Color";
|
||||
default: return "Undefined";
|
||||
case Val::Type::List: return "List";
|
||||
case Val::Type::Map: return "Map";
|
||||
}
|
||||
return "Undefined";
|
||||
}
|
||||
|
|
|
@ -291,8 +291,8 @@ StringList MscReader::ZipFileReader::fileList() const
|
|||
|
||||
StringList files;
|
||||
std::vector<ZipReader::FileInfo> fileInfoList = m_zip->fileInfoList();
|
||||
if (m_zip->status() != ZipReader::NoError) {
|
||||
LOGD() << "failed read meta, status: " << m_zip->status();
|
||||
if (m_zip->hasError()) {
|
||||
LOGD() << "failed read meta";
|
||||
}
|
||||
|
||||
for (const ZipReader::FileInfo& fi : fileInfoList) {
|
||||
|
@ -311,8 +311,8 @@ ByteArray MscReader::ZipFileReader::fileData(const String& fileName) const
|
|||
}
|
||||
|
||||
ByteArray data = m_zip->fileData(fileName.toStdString());
|
||||
if (m_zip->status() != ZipReader::NoError) {
|
||||
LOGD() << "failed read data, status: " << m_zip->status();
|
||||
if (m_zip->hasError()) {
|
||||
LOGD() << "failed read data";
|
||||
return ByteArray();
|
||||
}
|
||||
return data;
|
||||
|
|
|
@ -296,8 +296,8 @@ bool MscWriter::ZipFileWriter::addFileData(const String& fileName, const ByteArr
|
|||
}
|
||||
|
||||
m_zip->addFile(fileName.toStdString(), data);
|
||||
if (m_zip->status() != ZipWriter::NoError) {
|
||||
LOGE() << "failed write files to zip, status: " << m_zip->status();
|
||||
if (m_zip->hasError()) {
|
||||
LOGE() << "failed write files to zip";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -63,10 +63,6 @@ set(MODULE_SRC
|
|||
${CMAKE_CURRENT_LIST_DIR}/progress.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/smuflranges.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/smuflranges.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/xmlreader.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/xmlreader.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/xmlwriter.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/xmlwriter.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/utils.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/utils.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/defer.h
|
||||
|
@ -109,9 +105,9 @@ set(MODULE_SRC
|
|||
${CMAKE_CURRENT_LIST_DIR}/serialization/zipreader.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/serialization/zipwriter.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/serialization/zipwriter.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/serialization/internal/qzip.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/serialization/internal/qzipreader_p.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/serialization/internal/qzipwriter_p.h
|
||||
|
||||
${CMAKE_CURRENT_LIST_DIR}/serialization/internal/zipcontainer.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/serialization/internal/zipcontainer.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/serialization/textstream.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/serialization/textstream.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/serialization/json.cpp
|
||||
|
@ -124,6 +120,10 @@ if (NOT GLOBAL_NO_INTERNAL)
|
|||
${CMAKE_CURRENT_LIST_DIR}/globalmodule.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/settings.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/settings.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/xmlreader.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/xmlreader.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/xmlwriter.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/xmlwriter.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal/application.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal/application.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/internal/globalconfiguration.cpp
|
||||
|
@ -137,6 +137,10 @@ if (NOT GLOBAL_NO_INTERNAL)
|
|||
|
||||
${CMAKE_CURRENT_LIST_DIR}/io/internal/filesystem.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/io/internal/filesystem.h
|
||||
|
||||
${CMAKE_CURRENT_LIST_DIR}/serialization/internal/qzip.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/serialization/internal/qzipreader_p.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/serialization/internal/qzipwriter_p.h
|
||||
)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -31,6 +31,11 @@ Dir::Dir(const path_t& path)
|
|||
{
|
||||
}
|
||||
|
||||
path_t Dir::path() const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
path_t Dir::absolutePath() const
|
||||
{
|
||||
return fileSystem()->absoluteFilePath(m_path);
|
||||
|
@ -55,3 +60,30 @@ RetVal<io::paths_t> Dir::scanFiles(const io::path_t& rootDir, const std::vector<
|
|||
{
|
||||
return fileSystem()->scanFiles(rootDir, filters, mode);
|
||||
}
|
||||
|
||||
path_t Dir::fromNativeSeparators(const path_t& pathName)
|
||||
{
|
||||
#if defined(Q_OS_WIN)
|
||||
String path = pathName.toString();
|
||||
size_t i = path.indexOf(u'\\');
|
||||
if (i != mu::nidx) {
|
||||
String n(path);
|
||||
if (n.startsWith(u"\\\\?\\")) {
|
||||
n.remove(0, 4);
|
||||
i = n.indexOf(u'\\');
|
||||
if (i == mu::nidx) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < n.size(); ++i) {
|
||||
if (n.at(i) == u'\\') {
|
||||
n[i] = u'/';
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
#endif
|
||||
return pathName;
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ public:
|
|||
Dir() = default;
|
||||
Dir(const path_t& path);
|
||||
|
||||
path_t path() const;
|
||||
path_t absolutePath() const;
|
||||
|
||||
bool exists() const;
|
||||
|
@ -50,6 +51,8 @@ public:
|
|||
static RetVal<io::paths_t> scanFiles(const io::path_t& rootDir, const std::vector<std::string>& filters,
|
||||
ScanMode mode = ScanMode::FilesInCurrentDirAndSubdirs);
|
||||
|
||||
static path_t fromNativeSeparators(const path_t& pathName);
|
||||
|
||||
private:
|
||||
path_t m_path;
|
||||
};
|
||||
|
|
|
@ -127,6 +127,10 @@ String FileInfo::doSuffix(const String& filePath)
|
|||
}
|
||||
|
||||
size_t lastSep = filePath.lastIndexOf(u'/');
|
||||
if (lastSep == mu::nidx) {
|
||||
lastSep = 0;
|
||||
}
|
||||
|
||||
if (lastDot < lastSep) {
|
||||
return String();
|
||||
}
|
||||
|
@ -173,3 +177,13 @@ bool FileInfo::exists(const path_t& filePath)
|
|||
{
|
||||
return fileSystem()->exists(filePath);
|
||||
}
|
||||
|
||||
Dir FileInfo::dir() const
|
||||
{
|
||||
size_t lastSep = m_filePath.lastIndexOf(u'/');
|
||||
if (lastSep == mu::nidx) {
|
||||
return Dir(".");
|
||||
}
|
||||
String dirPath = m_filePath.mid(0, lastSep);
|
||||
return Dir(io::path_t(dirPath));
|
||||
}
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
#ifndef MU_IO_FILEINFO_H
|
||||
#define MU_IO_FILEINFO_H
|
||||
|
||||
#include "types/string.h"
|
||||
#include "dir.h"
|
||||
|
||||
#include "modularity/ioc.h"
|
||||
#include "ifilesystem.h"
|
||||
|
||||
|
@ -53,6 +56,8 @@ public:
|
|||
DateTime birthTime() const;
|
||||
DateTime lastModified() const;
|
||||
|
||||
Dir dir() const;
|
||||
|
||||
private:
|
||||
static String doSuffix(const String& filePath);
|
||||
|
||||
|
|
|
@ -21,9 +21,6 @@
|
|||
*/
|
||||
#include "path.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "stringutils.h"
|
||||
#include "fileinfo.h"
|
||||
|
||||
|
@ -114,99 +111,98 @@ std::wstring path_t::toStdWString() const
|
|||
|
||||
std::string mu::io::suffix(const mu::io::path_t& path)
|
||||
{
|
||||
QFileInfo fi(path.toQString());
|
||||
return fi.suffix().toLower().toStdString();
|
||||
return FileInfo::suffix(path).toLower().toStdString();
|
||||
}
|
||||
|
||||
mu::io::path_t mu::io::filename(const mu::io::path_t& path, bool includingExtension)
|
||||
{
|
||||
QFileInfo fi(path.toQString());
|
||||
FileInfo fi(path);
|
||||
return includingExtension ? fi.fileName() : fi.completeBaseName();
|
||||
}
|
||||
|
||||
mu::io::path_t mu::io::basename(const mu::io::path_t& path)
|
||||
{
|
||||
QFileInfo fi(path.toQString());
|
||||
FileInfo fi(path);
|
||||
return fi.baseName();
|
||||
}
|
||||
|
||||
mu::io::path_t mu::io::completeBasename(const mu::io::path_t& path)
|
||||
{
|
||||
QFileInfo fi(path.toQString());
|
||||
FileInfo fi(path);
|
||||
return fi.completeBaseName();
|
||||
}
|
||||
|
||||
mu::io::path_t mu::io::absolutePath(const path_t& path)
|
||||
{
|
||||
return QFileInfo(path.toQString()).absolutePath();
|
||||
}
|
||||
|
||||
mu::io::path_t mu::io::dirname(const mu::io::path_t& path)
|
||||
{
|
||||
return QFileInfo(path.toQString()).dir().dirName();
|
||||
return FileInfo(path).absolutePath();
|
||||
}
|
||||
|
||||
mu::io::path_t mu::io::dirpath(const mu::io::path_t& path)
|
||||
{
|
||||
return QFileInfo(path.toQString()).dir().path();
|
||||
return FileInfo(path).dir().path();
|
||||
}
|
||||
|
||||
mu::io::path_t mu::io::absoluteDirpath(const mu::io::path_t& path)
|
||||
{
|
||||
return QFileInfo(path.toQString()).dir().absolutePath();
|
||||
return FileInfo(path).dir().absolutePath();
|
||||
}
|
||||
|
||||
bool mu::io::isAbsolute(const path_t& path)
|
||||
{
|
||||
return QFileInfo(path.toQString()).isAbsolute();
|
||||
return FileInfo(path).isAbsolute();
|
||||
}
|
||||
|
||||
bool mu::io::isAllowedFileName(const path_t& fn_)
|
||||
{
|
||||
QString fn = basename(fn_).toQString();
|
||||
std::string fn = basename(fn_).toStdString();
|
||||
if (fn.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Windows filenames are not case sensitive.
|
||||
fn = fn.toUpper();
|
||||
fn = String::fromStdString(fn).toUpper().toStdString();
|
||||
|
||||
static const QString illegal="<>:\"|?*";
|
||||
static const std::string illegal="<>:\"|?*";
|
||||
|
||||
for (const QChar& c : fn) {
|
||||
for (const char& c : fn) {
|
||||
// Check for control characters
|
||||
if (c.toLatin1() > 0 && c.toLatin1() < 32) {
|
||||
if (c > 0 && c < 32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for illegal characters
|
||||
if (illegal.contains(c)) {
|
||||
return false;
|
||||
for (const char& ilc : illegal) {
|
||||
if (c == ilc) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for device names in filenames
|
||||
static const QStringList devices = {
|
||||
static const std::vector<std::string> devices = {
|
||||
"CON", "PRN", "AUX", "NUL",
|
||||
"COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
|
||||
"LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"
|
||||
};
|
||||
|
||||
foreach (const QString& s, devices) {
|
||||
for (const std::string& s : devices) {
|
||||
if (fn == s) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for trailing periods or spaces
|
||||
if (fn.right(1) == "." || fn.right(1) == " ") {
|
||||
if (fn.back() == '.' || fn.back() == ' ') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for pathnames that are too long
|
||||
if (fn.length() > 96) {
|
||||
if (fn.size() > 96) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Since we are checking for a filename, it mustn't be a directory
|
||||
if (fn.right(1) == "\\") {
|
||||
if (fn.back() == '\\') {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -219,20 +215,30 @@ mu::io::path_t mu::io::escapeFileName(const mu::io::path_t& fn_)
|
|||
// special characters in filenames are a constant source
|
||||
// of trouble, this replaces some of them common in german:
|
||||
//
|
||||
QString fn = fn_.toQString();
|
||||
String fn = fn_.toString();
|
||||
fn = fn.simplified();
|
||||
fn = fn.replace(QChar(' '), "_");
|
||||
fn = fn.replace(QChar('\n'), "_");
|
||||
fn = fn.replace(QChar(0xe4), "ae"); // ä
|
||||
fn = fn.replace(QChar(0xf6), "oe"); // ö
|
||||
fn = fn.replace(QChar(0xfc), "ue"); // ü
|
||||
fn = fn.replace(QChar(0xdf), "ss"); // ß
|
||||
fn = fn.replace(QChar(0xc4), "Ae"); // Ä
|
||||
fn = fn.replace(QChar(0xd6), "Oe"); // Ö
|
||||
fn = fn.replace(QChar(0xdc), "Ue"); // Ü
|
||||
fn = fn.replace(QChar(0x266d), "b"); // musical flat sign, happen in instrument names, so can happen in part (file) names
|
||||
fn = fn.replace(QChar(0x266f), "#"); // musical sharp sign, can happen in titles, so can happen in score (file) names
|
||||
fn = fn.replace(QRegularExpression("[" + QRegularExpression::escape("\\/:*?\"<>|") + "]"), "_"); //FAT/NTFS special chars
|
||||
fn = fn.replace(' ', '_');
|
||||
fn = fn.replace('\n', '_');
|
||||
fn = fn.replace(Char(0xe4), u"ae"); // ä
|
||||
fn = fn.replace(Char(0xf6), u"oe"); // ö
|
||||
fn = fn.replace(Char(0xfc), u"ue"); // ü
|
||||
fn = fn.replace(Char(0xdf), u"ss"); // ß
|
||||
fn = fn.replace(Char(0xc4), u"Ae"); // Ä
|
||||
fn = fn.replace(Char(0xd6), u"Oe"); // Ö
|
||||
fn = fn.replace(Char(0xdc), u"Ue"); // Ü
|
||||
fn = fn.replace(Char(0x266d), u"b"); // musical flat sign, happen in instrument names, so can happen in part (file) names
|
||||
fn = fn.replace(Char(0x266f), u"#"); // musical sharp sign, can happen in titles, so can happen in score (file) names
|
||||
//FAT/NTFS special chars
|
||||
fn = fn.replace('<', '_')
|
||||
.replace('>', '_')
|
||||
.replace(':', '_')
|
||||
.replace('"', '_')
|
||||
.replace('/', '_')
|
||||
.replace('\\', '_')
|
||||
.replace('|', '_')
|
||||
.replace('?', '_')
|
||||
.replace('*', '_');
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ path_t filename(const path_t& path, bool includingExtension = true);
|
|||
path_t basename(const path_t& path);
|
||||
path_t completeBasename(const path_t& path);
|
||||
path_t absolutePath(const path_t& path);
|
||||
path_t dirname(const path_t& path);
|
||||
//path_t dirname(const path_t& path);
|
||||
path_t dirpath(const path_t& path);
|
||||
path_t absoluteDirpath(const path_t& path);
|
||||
|
||||
|
|
828
src/framework/global/serialization/internal/zipcontainer.cpp
Normal file
828
src/framework/global/serialization/internal/zipcontainer.cpp
Normal file
|
@ -0,0 +1,828 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
* MuseScore-CLA-applies
|
||||
*
|
||||
* MuseScore
|
||||
* Music Composition & Notation
|
||||
*
|
||||
* Copyright (C) 2021 MuseScore BVBA and others
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "zipcontainer.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <cstring>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "io/dir.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
// Zip standard version for archives handled by this API
|
||||
// (actually, the only basic support of this version is implemented but it is enough for now)
|
||||
#define ZIP_VERSION 20
|
||||
|
||||
#if 0
|
||||
#define ZDEBUG LOGD
|
||||
#else
|
||||
#define ZDEBUG if (0) LOGD
|
||||
#endif
|
||||
|
||||
using namespace mu::io;
|
||||
|
||||
namespace mu {
|
||||
static inline uint readUInt(const uint8_t* data)
|
||||
{
|
||||
return (data[0]) + (data[1] << 8) + (data[2] << 16) + (data[3] << 24);
|
||||
}
|
||||
|
||||
static inline ushort readUShort(const uint8_t* data)
|
||||
{
|
||||
return (data[0]) + (data[1] << 8);
|
||||
}
|
||||
|
||||
static inline void writeUInt(uint8_t* data, uint i)
|
||||
{
|
||||
data[0] = i & 0xff;
|
||||
data[1] = (i >> 8) & 0xff;
|
||||
data[2] = (i >> 16) & 0xff;
|
||||
data[3] = (i >> 24) & 0xff;
|
||||
}
|
||||
|
||||
static inline void writeUShort(uint8_t* data, ushort i)
|
||||
{
|
||||
data[0] = i & 0xff;
|
||||
data[1] = (i >> 8) & 0xff;
|
||||
}
|
||||
|
||||
static inline void copyUInt(uint8_t* dest, const uint8_t* src)
|
||||
{
|
||||
dest[0] = src[0];
|
||||
dest[1] = src[1];
|
||||
dest[2] = src[2];
|
||||
dest[3] = src[3];
|
||||
}
|
||||
|
||||
static inline void copyUShort(uint8_t* dest, const uint8_t* src)
|
||||
{
|
||||
dest[0] = src[0];
|
||||
dest[1] = src[1];
|
||||
}
|
||||
|
||||
static void writeMSDosDate(uint8_t* dest, const std::tm& dt)
|
||||
{
|
||||
if (dt.tm_year > 0) {
|
||||
uint16_t time
|
||||
=(dt.tm_hour << 11) // 5 bit hour
|
||||
| (dt.tm_min << 5) // 6 bit minute
|
||||
| (dt.tm_sec >> 1); // 5 bit double seconds
|
||||
|
||||
dest[0] = time & 0xff;
|
||||
dest[1] = time >> 8;
|
||||
|
||||
uint16_t date
|
||||
=((dt.tm_year + 1900 - 1980) << 9) // 7 bit year 1980-based
|
||||
| ((dt.tm_mon + 1) << 5) // 4 bit month
|
||||
| (dt.tm_mday); // 5 bit day
|
||||
|
||||
dest[2] = char(date);
|
||||
dest[3] = char(date >> 8);
|
||||
} else {
|
||||
dest[0] = 0;
|
||||
dest[1] = 0;
|
||||
dest[2] = 0;
|
||||
dest[3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int inflate(Bytef* dest, ulong* destLen, const Bytef* source, ulong sourceLen)
|
||||
{
|
||||
z_stream stream;
|
||||
int err;
|
||||
|
||||
stream.next_in = const_cast<Bytef*>(source);
|
||||
stream.avail_in = (uInt)sourceLen;
|
||||
if ((uLong)stream.avail_in != sourceLen) {
|
||||
return Z_BUF_ERROR;
|
||||
}
|
||||
|
||||
stream.next_out = dest;
|
||||
stream.avail_out = (uInt) * destLen;
|
||||
if ((uLong)stream.avail_out != *destLen) {
|
||||
return Z_BUF_ERROR;
|
||||
}
|
||||
|
||||
stream.zalloc = (alloc_func)0;
|
||||
stream.zfree = (free_func)0;
|
||||
|
||||
err = inflateInit2(&stream, -MAX_WBITS);
|
||||
if (err != Z_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = inflate(&stream, Z_FINISH);
|
||||
if (err != Z_STREAM_END) {
|
||||
inflateEnd(&stream);
|
||||
if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) {
|
||||
return Z_DATA_ERROR;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
*destLen = stream.total_out;
|
||||
|
||||
err = inflateEnd(&stream);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int deflate(Bytef* dest, ulong* destLen, const Bytef* source, ulong sourceLen)
|
||||
{
|
||||
z_stream stream;
|
||||
int err;
|
||||
|
||||
stream.next_in = const_cast<Bytef*>(source);
|
||||
stream.avail_in = (uInt)sourceLen;
|
||||
stream.next_out = dest;
|
||||
stream.avail_out = (uInt) * destLen;
|
||||
if ((uLong)stream.avail_out != *destLen) {
|
||||
return Z_BUF_ERROR;
|
||||
}
|
||||
|
||||
stream.zalloc = (alloc_func)0;
|
||||
stream.zfree = (free_func)0;
|
||||
stream.opaque = (voidpf)0;
|
||||
|
||||
err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
|
||||
if (err != Z_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = deflate(&stream, Z_FINISH);
|
||||
if (err != Z_STREAM_END) {
|
||||
deflateEnd(&stream);
|
||||
return err == Z_OK ? Z_BUF_ERROR : err;
|
||||
}
|
||||
*destLen = stream.total_out;
|
||||
|
||||
err = deflateEnd(&stream);
|
||||
return err;
|
||||
}
|
||||
|
||||
namespace WindowsFileAttributes {
|
||||
enum {
|
||||
Dir = 0x10, // FILE_ATTRIBUTE_DIRECTORY
|
||||
File = 0x80, // FILE_ATTRIBUTE_NORMAL
|
||||
TypeMask = 0x90,
|
||||
|
||||
ReadOnly = 0x01, // FILE_ATTRIBUTE_READONLY
|
||||
PermMask = 0x01
|
||||
};
|
||||
}
|
||||
|
||||
namespace UnixFileAttributes {
|
||||
enum {
|
||||
Dir = 0040000, // __S_IFDIR
|
||||
File = 0100000, // __S_IFREG
|
||||
SymLink = 0120000, // __S_IFLNK
|
||||
TypeMask = 0170000, // __S_IFMT
|
||||
|
||||
ReadUser = 0400, // __S_IRUSR
|
||||
WriteUser = 0200, // __S_IWUSR
|
||||
ExeUser = 0100, // __S_IXUSR
|
||||
ReadGroup = 0040, // __S_IRGRP
|
||||
WriteGroup = 0020, // __S_IWGRP
|
||||
ExeGroup = 0010, // __S_IXGRP
|
||||
ReadOther = 0004, // __S_IROTH
|
||||
WriteOther = 0002, // __S_IWOTH
|
||||
ExeOther = 0001, // __S_IXOTH
|
||||
PermMask = 0777
|
||||
};
|
||||
}
|
||||
|
||||
static std::tm readMSDosDate(const uint8_t* src)
|
||||
{
|
||||
std::tm tm;
|
||||
uint dosDate = readUInt(src);
|
||||
uint64_t uDate;
|
||||
uDate = (uint64_t)(dosDate >> 16);
|
||||
tm.tm_mday = (uDate & 0x1f);
|
||||
tm.tm_mon = ((uDate & 0x1E0) >> 5) - 1;
|
||||
tm.tm_year = (((uDate & 0x0FE00) >> 9) + 1980 - 1900);
|
||||
tm.tm_hour = ((dosDate & 0xF800) >> 11);
|
||||
tm.tm_min = ((dosDate & 0x7E0) >> 5);
|
||||
tm.tm_sec = ((dosDate & 0x1f) << 1);
|
||||
|
||||
return tm;
|
||||
}
|
||||
|
||||
// for details, see http://www.pkware.com/documents/casestudies/APPNOTE.TXT
|
||||
|
||||
enum HostOS {
|
||||
HostFAT = 0,
|
||||
HostAMIGA = 1,
|
||||
HostVMS = 2, // VAX/VMS
|
||||
HostUnix = 3,
|
||||
HostVM_CMS = 4,
|
||||
HostAtari = 5, // what if it's a minix filesystem? [cjh]
|
||||
HostHPFS = 6, // filesystem used by OS/2 (and NT 3.x)
|
||||
HostMac = 7,
|
||||
HostZ_System = 8,
|
||||
HostCPM = 9,
|
||||
HostTOPS20 = 10, // pkzip 2.50 NTFS
|
||||
HostNTFS = 11, // filesystem used by Windows NT
|
||||
HostQDOS = 12, // SMS/QDOS
|
||||
HostAcorn = 13, // Archimedes Acorn RISC OS
|
||||
HostVFAT = 14, // filesystem used by Windows 95, NT
|
||||
HostMVS = 15,
|
||||
HostBeOS = 16, // hybrid POSIX/database filesystem
|
||||
HostTandem = 17,
|
||||
HostOS400 = 18,
|
||||
HostOSX = 19
|
||||
};
|
||||
|
||||
enum GeneralPurposeFlag {
|
||||
Encrypted = 0x01,
|
||||
AlgTune1 = 0x02,
|
||||
AlgTune2 = 0x04,
|
||||
HasDataDescriptor = 0x08,
|
||||
PatchedData = 0x20,
|
||||
StrongEncrypted = 0x40,
|
||||
Utf8Names = 0x0800,
|
||||
CentralDirectoryEncrypted = 0x2000
|
||||
};
|
||||
|
||||
enum CompressionMethod {
|
||||
CompressionMethodStored = 0,
|
||||
CompressionMethodShrunk = 1,
|
||||
CompressionMethodReduced1 = 2,
|
||||
CompressionMethodReduced2 = 3,
|
||||
CompressionMethodReduced3 = 4,
|
||||
CompressionMethodReduced4 = 5,
|
||||
CompressionMethodImploded = 6,
|
||||
CompressionMethodReservedTokenizing = 7, // reserved for tokenizing
|
||||
CompressionMethodDeflated = 8,
|
||||
CompressionMethodDeflated64 = 9,
|
||||
CompressionMethodPKImploding = 10,
|
||||
|
||||
CompressionMethodBZip2 = 12,
|
||||
|
||||
CompressionMethodLZMA = 14,
|
||||
|
||||
CompressionMethodTerse = 18,
|
||||
CompressionMethodLz77 = 19,
|
||||
|
||||
CompressionMethodJpeg = 96,
|
||||
CompressionMethodWavPack = 97,
|
||||
CompressionMethodPPMd = 98,
|
||||
CompressionMethodWzAES = 99
|
||||
};
|
||||
|
||||
struct LocalFileHeader
|
||||
{
|
||||
uint8_t signature[4]; // 0x04034b50
|
||||
uint8_t version_needed[2];
|
||||
uint8_t general_purpose_bits[2];
|
||||
uint8_t compression_method[2];
|
||||
uint8_t last_mod_file[4];
|
||||
uint8_t crc_32[4];
|
||||
uint8_t compressed_size[4];
|
||||
uint8_t uncompressed_size[4];
|
||||
uint8_t file_name_length[2];
|
||||
uint8_t extra_field_length[2];
|
||||
};
|
||||
|
||||
struct DataDescriptor
|
||||
{
|
||||
uint8_t crc_32[4];
|
||||
uint8_t compressed_size[4];
|
||||
uint8_t uncompressed_size[4];
|
||||
};
|
||||
|
||||
struct CentralFileHeader
|
||||
{
|
||||
uint8_t signature[4]; // 0x02014b50
|
||||
uint8_t version_made[2];
|
||||
uint8_t version_needed[2];
|
||||
uint8_t general_purpose_bits[2];
|
||||
uint8_t compression_method[2];
|
||||
uint8_t last_mod_file[4];
|
||||
uint8_t crc_32[4];
|
||||
uint8_t compressed_size[4];
|
||||
uint8_t uncompressed_size[4];
|
||||
uint8_t file_name_length[2];
|
||||
uint8_t extra_field_length[2];
|
||||
uint8_t file_comment_length[2];
|
||||
uint8_t disk_start[2];
|
||||
uint8_t internal_file_attributes[2];
|
||||
uint8_t external_file_attributes[4];
|
||||
uint8_t offset_local_header[4];
|
||||
LocalFileHeader toLocalHeader() const;
|
||||
};
|
||||
|
||||
struct EndOfDirectory
|
||||
{
|
||||
uint8_t signature[4]; // 0x06054b50
|
||||
uint8_t this_disk[2];
|
||||
uint8_t start_of_directory_disk[2];
|
||||
uint8_t num_dir_entries_this_disk[2];
|
||||
uint8_t num_dir_entries[2];
|
||||
uint8_t directory_size[4];
|
||||
uint8_t dir_start_offset[4];
|
||||
uint8_t comment_length[2];
|
||||
};
|
||||
|
||||
struct FileHeader
|
||||
{
|
||||
CentralFileHeader h;
|
||||
ByteArray file_name;
|
||||
ByteArray extra_field;
|
||||
ByteArray file_comment;
|
||||
};
|
||||
|
||||
LocalFileHeader CentralFileHeader::toLocalHeader() const
|
||||
{
|
||||
LocalFileHeader h;
|
||||
writeUInt(h.signature, 0x04034b50);
|
||||
copyUShort(h.version_needed, version_needed);
|
||||
copyUShort(h.general_purpose_bits, general_purpose_bits);
|
||||
copyUShort(h.compression_method, compression_method);
|
||||
copyUInt(h.last_mod_file, last_mod_file);
|
||||
copyUInt(h.crc_32, crc_32);
|
||||
copyUInt(h.compressed_size, compressed_size);
|
||||
copyUInt(h.uncompressed_size, uncompressed_size);
|
||||
copyUShort(h.file_name_length, file_name_length);
|
||||
copyUShort(h.extra_field_length, extra_field_length);
|
||||
return h;
|
||||
}
|
||||
|
||||
struct ZipContainer::Impl {
|
||||
IODevice* device = nullptr;
|
||||
|
||||
bool dirtyFileTree = true;
|
||||
std::vector<FileHeader> fileHeaders;
|
||||
ByteArray comment;
|
||||
uint start_of_directory = 0;
|
||||
ZipContainer::Status status = ZipContainer::NoError;
|
||||
|
||||
ZipContainer::CompressionPolicy compressionPolicy;
|
||||
|
||||
enum EntryType {
|
||||
Directory, File, Symlink
|
||||
};
|
||||
|
||||
void addEntry(EntryType type, const std::string& fileName, const ByteArray& contents);
|
||||
|
||||
Impl(IODevice* d)
|
||||
: device(d) {}
|
||||
|
||||
void scanFiles();
|
||||
ZipContainer::FileInfo fillFileInfo(int index) const;
|
||||
};
|
||||
|
||||
void ZipContainer::Impl::scanFiles()
|
||||
{
|
||||
if (!dirtyFileTree) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(device->isOpen() || device->open(IODevice::ReadOnly))) {
|
||||
status = ZipContainer::FileOpenError;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((device->openMode() & IODevice::ReadOnly) == 0) { // only read the index from readable files.
|
||||
status = ZipContainer::FileReadError;
|
||||
return;
|
||||
}
|
||||
|
||||
dirtyFileTree = false;
|
||||
uint8_t tmp[4];
|
||||
device->read(tmp, 4);
|
||||
if (readUInt(tmp) != 0x04034b50) {
|
||||
LOGW("Zip: not a zip file!");
|
||||
return;
|
||||
}
|
||||
|
||||
// find EndOfDirectory header
|
||||
int i = 0;
|
||||
int start_of_directory_local = -1;
|
||||
int num_dir_entries = 0;
|
||||
EndOfDirectory eod;
|
||||
while (start_of_directory_local == -1) {
|
||||
const int pos = device->size() - int(sizeof(EndOfDirectory)) - i;
|
||||
if (pos < 0 || i > 65535) {
|
||||
LOGW("Zip: EndOfDirectory not found");
|
||||
return;
|
||||
}
|
||||
|
||||
device->seek(pos);
|
||||
device->read((uint8_t*)&eod, sizeof(EndOfDirectory));
|
||||
if (readUInt(eod.signature) == 0x06054b50) {
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
// have the eod
|
||||
start_of_directory_local = readUInt(eod.dir_start_offset);
|
||||
num_dir_entries = readUShort(eod.num_dir_entries);
|
||||
ZDEBUG("start_of_directory at %d, num_dir_entries=%d", start_of_directory_local, num_dir_entries);
|
||||
int comment_length = readUShort(eod.comment_length);
|
||||
if (comment_length != i) {
|
||||
LOGW("Zip: failed to parse zip file.");
|
||||
}
|
||||
comment = device->read(std::min(comment_length, i));
|
||||
|
||||
device->seek(start_of_directory_local);
|
||||
for (i = 0; i < num_dir_entries; ++i) {
|
||||
FileHeader header;
|
||||
int read = device->read((uint8_t*)&header.h, sizeof(CentralFileHeader));
|
||||
if (read < (int)sizeof(CentralFileHeader)) {
|
||||
LOGW("Zip: Failed to read complete header, index may be incomplete");
|
||||
break;
|
||||
}
|
||||
if (readUInt(header.h.signature) != 0x02014b50) {
|
||||
LOGW("Zip: invalid header signature, index may be incomplete");
|
||||
break;
|
||||
}
|
||||
|
||||
size_t l = readUShort(header.h.file_name_length);
|
||||
header.file_name = device->read(l);
|
||||
if (header.file_name.size() != l) {
|
||||
LOGW("Zip: Failed to read filename from zip index, index may be incomplete");
|
||||
break;
|
||||
}
|
||||
l = readUShort(header.h.extra_field_length);
|
||||
header.extra_field = device->read(l);
|
||||
if (header.extra_field.size() != l) {
|
||||
LOGW("Zip: Failed to read extra field in zip file, skipping file, index may be incomplete");
|
||||
break;
|
||||
}
|
||||
l = readUShort(header.h.file_comment_length);
|
||||
header.file_comment = device->read(l);
|
||||
if (header.file_comment.size() != l) {
|
||||
LOGW("Zip: Failed to read read file comment, index may be incomplete");
|
||||
break;
|
||||
}
|
||||
|
||||
ZDEBUG("found file '%s'", header.file_name.data());
|
||||
fileHeaders.push_back(header);
|
||||
}
|
||||
}
|
||||
|
||||
ZipContainer::FileInfo ZipContainer::Impl::fillFileInfo(int index) const
|
||||
{
|
||||
ZipContainer::FileInfo fileInfo;
|
||||
FileHeader header = fileHeaders.at(index);
|
||||
uint32_t mode = readUInt(header.h.external_file_attributes);
|
||||
const HostOS hostOS = HostOS(readUShort(header.h.version_made) >> 8);
|
||||
switch (hostOS) {
|
||||
case HostUnix:
|
||||
mode = (mode >> 16) & 0xffff;
|
||||
switch (mode & UnixFileAttributes::TypeMask) {
|
||||
case UnixFileAttributes::SymLink:
|
||||
fileInfo.isSymLink = true;
|
||||
break;
|
||||
case UnixFileAttributes::Dir:
|
||||
fileInfo.isDir = true;
|
||||
break;
|
||||
case UnixFileAttributes::File:
|
||||
default: // ### just for the case; should we warn?
|
||||
fileInfo.isFile = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case HostFAT:
|
||||
case HostNTFS:
|
||||
case HostHPFS:
|
||||
case HostVFAT:
|
||||
switch (mode & WindowsFileAttributes::TypeMask) {
|
||||
case WindowsFileAttributes::Dir:
|
||||
fileInfo.isDir = true;
|
||||
break;
|
||||
case WindowsFileAttributes::File:
|
||||
default:
|
||||
fileInfo.isFile = true;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
LOGW("Zip: Zip entry format at %d is not supported.", index);
|
||||
return fileInfo; // we don't support anything else
|
||||
}
|
||||
|
||||
// ushort general_purpose_bits = readUShort(header.h.general_purpose_bits);
|
||||
// if bit 11 is set, the filename and comment fields must be encoded using UTF-8
|
||||
// const bool inUtf8 = (general_purpose_bits & Utf8Names) != 0;
|
||||
fileInfo.filePath = header.file_name.constChar();
|
||||
fileInfo.crc = readUInt(header.h.crc_32);
|
||||
fileInfo.size = readUInt(header.h.uncompressed_size);
|
||||
fileInfo.lastModified = readMSDosDate(header.h.last_mod_file);
|
||||
|
||||
// fix the file path, if broken (convert separators, eat leading and trailing ones)
|
||||
fileInfo.filePath = Dir::fromNativeSeparators(fileInfo.filePath).toStdString();
|
||||
while (!fileInfo.filePath.empty() && (fileInfo.filePath.front() == '.' || fileInfo.filePath.front() == '/')) {
|
||||
fileInfo.filePath = fileInfo.filePath.substr(1);
|
||||
}
|
||||
while (!fileInfo.filePath.empty() && fileInfo.filePath.back() == '/') {
|
||||
fileInfo.filePath = fileInfo.filePath.substr(fileInfo.filePath.size() - 1);
|
||||
}
|
||||
return fileInfo;
|
||||
}
|
||||
|
||||
void ZipContainer::Impl::addEntry(EntryType type, const std::string& fileName, const ByteArray& contents)
|
||||
{
|
||||
if (!(device->isOpen() || device->open(IODevice::WriteOnly))) {
|
||||
status = ZipContainer::FileOpenError;
|
||||
return;
|
||||
}
|
||||
device->seek(start_of_directory);
|
||||
|
||||
// don't compress small files
|
||||
ZipContainer::CompressionPolicy compression = compressionPolicy;
|
||||
if (compressionPolicy == ZipContainer::AutoCompress) {
|
||||
if (contents.size() < 64) {
|
||||
compression = ZipContainer::NeverCompress;
|
||||
} else {
|
||||
compression = ZipContainer::AlwaysCompress;
|
||||
}
|
||||
}
|
||||
|
||||
FileHeader header;
|
||||
std::memset(&header.h, 0, sizeof(CentralFileHeader));
|
||||
writeUInt(header.h.signature, 0x02014b50);
|
||||
|
||||
writeUShort(header.h.version_needed, ZIP_VERSION);
|
||||
writeUInt(header.h.uncompressed_size, contents.size());
|
||||
|
||||
std::time_t t = std::time(0); // get time now
|
||||
std::tm* now = std::localtime(&t);
|
||||
writeMSDosDate(header.h.last_mod_file, *now);
|
||||
ByteArray data = contents;
|
||||
if (compression == ZipContainer::AlwaysCompress) {
|
||||
writeUShort(header.h.compression_method, CompressionMethodDeflated);
|
||||
|
||||
ulong len = contents.size();
|
||||
// shamelessly copied form zlib
|
||||
len += (len >> 12) + (len >> 14) + 11;
|
||||
int res;
|
||||
do {
|
||||
data.resize(len);
|
||||
res = deflate((uint8_t*)data.data(), &len, (const uint8_t*)contents.constData(), contents.size());
|
||||
|
||||
switch (res) {
|
||||
case Z_OK:
|
||||
data.resize(len);
|
||||
break;
|
||||
case Z_MEM_ERROR:
|
||||
LOGW("Zip: Z_MEM_ERROR: Not enough memory to compress file, skipping");
|
||||
data.resize(0);
|
||||
break;
|
||||
case Z_BUF_ERROR:
|
||||
len *= 2;
|
||||
break;
|
||||
}
|
||||
} while (res == Z_BUF_ERROR);
|
||||
}
|
||||
// TODO add a check if data.size() > contents.size(). Then try to store the original and revert the compression method to be uncompressed
|
||||
writeUInt(header.h.compressed_size, data.size());
|
||||
uint crc_32 = ::crc32(0, 0, 0);
|
||||
crc_32 = ::crc32(crc_32, (const uint8_t*)contents.constData(), contents.size());
|
||||
writeUInt(header.h.crc_32, crc_32);
|
||||
|
||||
// if bit 11 is set, the filename and comment fields must be encoded using UTF-8
|
||||
ushort general_purpose_bits = Utf8Names; // always use utf-8
|
||||
writeUShort(header.h.general_purpose_bits, general_purpose_bits);
|
||||
|
||||
//const bool inUtf8 = (general_purpose_bits & Utf8Names) != 0;
|
||||
header.file_name = ByteArray(fileName.c_str(), fileName.size());
|
||||
if (header.file_name.size() > 0xffff) {
|
||||
LOGW("Zip: Filename is too long, chopping it to 65535 bytes");
|
||||
header.file_name = header.file_name.left(0xffff); // ### don't break the utf-8 sequence, if any
|
||||
}
|
||||
if (header.file_comment.size() + header.file_name.size() > 0xffff) {
|
||||
LOGW("Zip: File comment is too long, chopping it to 65535 bytes");
|
||||
header.file_comment.truncate(0xffff - header.file_name.size()); // ### don't break the utf-8 sequence, if any
|
||||
}
|
||||
writeUShort(header.h.file_name_length, header.file_name.size());
|
||||
//h.extra_field_length[2];
|
||||
|
||||
writeUShort(header.h.version_made, HostUnix << 8);
|
||||
//uint8_t internal_file_attributes[2];
|
||||
//uint8_t external_file_attributes[4];
|
||||
uint32_t mode = 0;
|
||||
switch (type) {
|
||||
case Symlink:
|
||||
mode |= UnixFileAttributes::SymLink;
|
||||
break;
|
||||
case Directory:
|
||||
mode |= UnixFileAttributes::Dir;
|
||||
break;
|
||||
case File:
|
||||
mode |= UnixFileAttributes::File;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
break;
|
||||
}
|
||||
writeUInt(header.h.external_file_attributes, mode << 16);
|
||||
writeUInt(header.h.offset_local_header, start_of_directory);
|
||||
|
||||
fileHeaders.push_back(header);
|
||||
|
||||
LocalFileHeader h = header.h.toLocalHeader();
|
||||
device->write((const uint8_t*)&h, sizeof(LocalFileHeader));
|
||||
device->write(header.file_name);
|
||||
device->write(data);
|
||||
start_of_directory = device->pos();
|
||||
dirtyFileTree = true;
|
||||
}
|
||||
|
||||
ZipContainer::ZipContainer(IODevice* device)
|
||||
: p(new Impl(device))
|
||||
{
|
||||
assert(device);
|
||||
}
|
||||
|
||||
ZipContainer::~ZipContainer()
|
||||
{
|
||||
close();
|
||||
delete p;
|
||||
}
|
||||
|
||||
std::vector<ZipContainer::FileInfo> ZipContainer::fileInfoList() const
|
||||
{
|
||||
p->scanFiles();
|
||||
std::vector<FileInfo> files;
|
||||
const int numFileHeaders = p->fileHeaders.size();
|
||||
files.reserve(numFileHeaders);
|
||||
for (int i = 0; i < numFileHeaders; ++i) {
|
||||
files.push_back(p->fillFileInfo(i));
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
int ZipContainer::count() const
|
||||
{
|
||||
p->scanFiles();
|
||||
return p->fileHeaders.size();
|
||||
}
|
||||
|
||||
ByteArray ZipContainer::fileData(const std::string& fileName) const
|
||||
{
|
||||
p->scanFiles();
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < p->fileHeaders.size(); ++i) {
|
||||
if (p->fileHeaders.at(i).file_name == ByteArray::fromRawData(fileName.c_str(), fileName.size())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == p->fileHeaders.size()) {
|
||||
return ByteArray();
|
||||
}
|
||||
|
||||
FileHeader header = p->fileHeaders.at(i);
|
||||
|
||||
ushort version_needed = readUShort(header.h.version_needed);
|
||||
if (version_needed > ZIP_VERSION) {
|
||||
LOGW("Zip: .ZIP specification version %d implementationis needed to extract the data.", version_needed);
|
||||
return ByteArray();
|
||||
}
|
||||
|
||||
ushort general_purpose_bits = readUShort(header.h.general_purpose_bits);
|
||||
int compressed_size = readUInt(header.h.compressed_size);
|
||||
int uncompressed_size = readUInt(header.h.uncompressed_size);
|
||||
int start = readUInt(header.h.offset_local_header);
|
||||
|
||||
p->device->seek(start);
|
||||
LocalFileHeader lh;
|
||||
p->device->read((uint8_t*)&lh, sizeof(LocalFileHeader));
|
||||
uint skip = readUShort(lh.file_name_length) + readUShort(lh.extra_field_length);
|
||||
p->device->seek(p->device->pos() + skip);
|
||||
|
||||
int compression_method = readUShort(lh.compression_method);
|
||||
|
||||
if ((general_purpose_bits & Encrypted) != 0) {
|
||||
LOGW("Zip: Unsupported encryption method is needed to extract the data.");
|
||||
return ByteArray();
|
||||
}
|
||||
|
||||
ByteArray compressed = p->device->read(compressed_size);
|
||||
if (compression_method == CompressionMethodStored) {
|
||||
// no compression
|
||||
compressed.truncate(uncompressed_size);
|
||||
return compressed;
|
||||
} else if (compression_method == CompressionMethodDeflated) {
|
||||
// Deflate
|
||||
//qDebug("compressed=%d", compressed.size());
|
||||
compressed.truncate(compressed_size);
|
||||
ByteArray baunzip;
|
||||
ulong len = std::max(uncompressed_size, 1);
|
||||
int res;
|
||||
do {
|
||||
baunzip.resize(len);
|
||||
res = inflate((uint8_t*)baunzip.data(), &len,
|
||||
(const uint8_t*)compressed.constData(), compressed_size);
|
||||
|
||||
switch (res) {
|
||||
case Z_OK:
|
||||
if ((size_t)len != baunzip.size()) {
|
||||
baunzip.resize(len);
|
||||
}
|
||||
break;
|
||||
case Z_MEM_ERROR:
|
||||
LOGW("Zip: Z_MEM_ERROR: Not enough memory");
|
||||
break;
|
||||
case Z_BUF_ERROR:
|
||||
len *= 2;
|
||||
break;
|
||||
case Z_DATA_ERROR:
|
||||
LOGW("Zip: Z_DATA_ERROR: Input data is corrupted");
|
||||
break;
|
||||
}
|
||||
} while (res == Z_BUF_ERROR);
|
||||
return baunzip;
|
||||
}
|
||||
|
||||
LOGW("Zip: Unsupported compression method %d is needed to extract the data.", compression_method);
|
||||
return ByteArray();
|
||||
}
|
||||
|
||||
ZipContainer::Status ZipContainer::status() const
|
||||
{
|
||||
return p->status;
|
||||
}
|
||||
|
||||
void ZipContainer::setCompressionPolicy(CompressionPolicy policy)
|
||||
{
|
||||
p->compressionPolicy = policy;
|
||||
}
|
||||
|
||||
ZipContainer::CompressionPolicy ZipContainer::compressionPolicy() const
|
||||
{
|
||||
return p->compressionPolicy;
|
||||
}
|
||||
|
||||
void ZipContainer::addFile(const std::string& fileName, const ByteArray& data)
|
||||
{
|
||||
p->addEntry(Impl::File, Dir::fromNativeSeparators(fileName).toStdString(), data);
|
||||
}
|
||||
|
||||
void ZipContainer::addDirectory(const std::string& dirName)
|
||||
{
|
||||
std::string name(Dir::fromNativeSeparators(dirName).toStdString());
|
||||
// separator is mandatory
|
||||
if (name.back() != '/') {
|
||||
name.push_back('/');
|
||||
}
|
||||
p->addEntry(Impl::Directory, name, ByteArray());
|
||||
}
|
||||
|
||||
void ZipContainer::close()
|
||||
{
|
||||
if (!(p->device->openMode() & IODevice::WriteOnly)) {
|
||||
p->device->close();
|
||||
return;
|
||||
}
|
||||
|
||||
//qDebug("Zip::close writing directory, %d entries", p->fileHeaders.size());
|
||||
p->device->seek(p->start_of_directory);
|
||||
// write new directory
|
||||
for (size_t i = 0; i < p->fileHeaders.size(); ++i) {
|
||||
const FileHeader& header = p->fileHeaders.at(i);
|
||||
p->device->write((const uint8_t*)&header.h, sizeof(CentralFileHeader));
|
||||
p->device->write(header.file_name);
|
||||
p->device->write(header.extra_field);
|
||||
p->device->write(header.file_comment);
|
||||
}
|
||||
int dir_size = p->device->pos() - p->start_of_directory;
|
||||
// write end of directory
|
||||
EndOfDirectory eod;
|
||||
memset(&eod, 0, sizeof(EndOfDirectory));
|
||||
writeUInt(eod.signature, 0x06054b50);
|
||||
//uint8_t this_disk[2];
|
||||
//uint8_t start_of_directory_disk[2];
|
||||
writeUShort(eod.num_dir_entries_this_disk, p->fileHeaders.size());
|
||||
writeUShort(eod.num_dir_entries, p->fileHeaders.size());
|
||||
writeUInt(eod.directory_size, dir_size);
|
||||
writeUInt(eod.dir_start_offset, p->start_of_directory);
|
||||
writeUShort(eod.comment_length, p->comment.size());
|
||||
|
||||
p->device->write((const uint8_t*)&eod, sizeof(EndOfDirectory));
|
||||
p->device->write(p->comment);
|
||||
p->device->close();
|
||||
}
|
||||
}
|
87
src/framework/global/serialization/internal/zipcontainer.h
Normal file
87
src/framework/global/serialization/internal/zipcontainer.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
* MuseScore-CLA-applies
|
||||
*
|
||||
* MuseScore
|
||||
* Music Composition & Notation
|
||||
*
|
||||
* Copyright (C) 2021 MuseScore BVBA and others
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef MU_GLOBAL_ZIPCONTAINER_H
|
||||
#define MU_GLOBAL_ZIPCONTAINER_H
|
||||
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include "io/iodevice.h"
|
||||
|
||||
namespace mu {
|
||||
class ZipContainer
|
||||
{
|
||||
public:
|
||||
explicit ZipContainer(io::IODevice* device);
|
||||
~ZipContainer();
|
||||
|
||||
enum Status {
|
||||
NoError,
|
||||
FileOpenError,
|
||||
FileReadError,
|
||||
FileWriteError,
|
||||
FileError
|
||||
};
|
||||
|
||||
struct FileInfo
|
||||
{
|
||||
std::string filePath;
|
||||
bool isDir = false;
|
||||
bool isFile = false;
|
||||
bool isSymLink = false;
|
||||
uint crc = 0;
|
||||
int64_t size = 0;
|
||||
std::tm lastModified;
|
||||
|
||||
bool isValid() const { return isDir || isFile || isSymLink; }
|
||||
};
|
||||
|
||||
Status status() const;
|
||||
|
||||
void close();
|
||||
|
||||
// Read
|
||||
std::vector<FileInfo> fileInfoList() const;
|
||||
int count() const;
|
||||
|
||||
ByteArray fileData(const std::string& fileName) const;
|
||||
|
||||
// Write
|
||||
enum CompressionPolicy {
|
||||
AlwaysCompress,
|
||||
NeverCompress,
|
||||
AutoCompress
|
||||
};
|
||||
|
||||
void setCompressionPolicy(CompressionPolicy policy);
|
||||
CompressionPolicy compressionPolicy() const;
|
||||
|
||||
void addFile(const std::string& fileName, const ByteArray& data);
|
||||
void addDirectory(const std::string& dirName);
|
||||
|
||||
private:
|
||||
|
||||
struct Impl;
|
||||
Impl* p = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // MU_GLOBAL_ZIPCONTAINER_H
|
|
@ -21,9 +21,7 @@
|
|||
*/
|
||||
#include "zipreader.h"
|
||||
|
||||
#include <QBuffer>
|
||||
|
||||
#include "internal/qzipreader_p.h"
|
||||
#include "internal/zipcontainer.h"
|
||||
#include "io/file.h"
|
||||
|
||||
using namespace mu;
|
||||
|
@ -31,40 +29,36 @@ using namespace mu::io;
|
|||
|
||||
struct ZipReader::Impl
|
||||
{
|
||||
MQZipReader* zip = nullptr;
|
||||
ByteArray data;
|
||||
QByteArray ba;
|
||||
QBuffer buf;
|
||||
ZipContainer* zip = nullptr;
|
||||
IODevice* device = nullptr;
|
||||
bool isSelfDevice = false;
|
||||
};
|
||||
|
||||
ZipReader::ZipReader(const io::path_t& filePath)
|
||||
: m_filePath(filePath)
|
||||
{
|
||||
m_impl = new Impl();
|
||||
File f(filePath);
|
||||
if (f.open(IODevice::ReadOnly)) {
|
||||
m_impl->data = f.readAll();
|
||||
m_impl->ba = m_impl->data.toQByteArrayNoCopy();
|
||||
m_impl->device = new File(filePath);
|
||||
m_impl->isSelfDevice = true;
|
||||
if (m_impl->device->open(IODevice::ReadOnly)) {
|
||||
}
|
||||
m_impl->buf.setBuffer(&m_impl->ba);
|
||||
m_impl->buf.open(QIODevice::ReadOnly);
|
||||
m_impl->zip = new MQZipReader(&m_impl->buf);
|
||||
m_impl->zip = new ZipContainer(m_impl->device);
|
||||
}
|
||||
|
||||
ZipReader::ZipReader(IODevice* device)
|
||||
{
|
||||
m_impl = new Impl();
|
||||
m_impl->data = device->readAll();
|
||||
m_impl->ba = m_impl->data.toQByteArrayNoCopy();
|
||||
m_impl->buf.setBuffer(&m_impl->ba);
|
||||
m_impl->buf.open(QIODevice::ReadOnly);
|
||||
m_impl->zip = new MQZipReader(&m_impl->buf);
|
||||
m_impl->device = device;
|
||||
m_impl->zip = new ZipContainer(m_impl->device);
|
||||
}
|
||||
|
||||
ZipReader::~ZipReader()
|
||||
{
|
||||
close();
|
||||
delete m_impl->zip;
|
||||
if (m_impl->isSelfDevice) {
|
||||
delete m_impl->device;
|
||||
}
|
||||
delete m_impl;
|
||||
}
|
||||
|
||||
|
@ -78,17 +72,17 @@ void ZipReader::close()
|
|||
m_impl->zip->close();
|
||||
}
|
||||
|
||||
ZipReader::Status ZipReader::status() const
|
||||
bool ZipReader::hasError() const
|
||||
{
|
||||
return static_cast<Status>(m_impl->zip->status());
|
||||
return m_impl->zip->status() != ZipContainer::NoError;
|
||||
}
|
||||
|
||||
std::vector<ZipReader::FileInfo> ZipReader::fileInfoList() const
|
||||
{
|
||||
std::vector<FileInfo> ret;
|
||||
QVector<MQZipReader::FileInfo> qfis = m_impl->zip->fileInfoList();
|
||||
ret.reserve(qfis.size());
|
||||
for (const MQZipReader::FileInfo& qfi : qfis) {
|
||||
std::vector<ZipContainer::FileInfo> fis = m_impl->zip->fileInfoList();
|
||||
ret.reserve(fis.size());
|
||||
for (const ZipContainer::FileInfo& qfi : fis) {
|
||||
FileInfo fi;
|
||||
fi.filePath = qfi.filePath;
|
||||
fi.isDir = qfi.isDir;
|
||||
|
@ -104,6 +98,5 @@ std::vector<ZipReader::FileInfo> ZipReader::fileInfoList() const
|
|||
|
||||
ByteArray ZipReader::fileData(const std::string& fileName) const
|
||||
{
|
||||
QByteArray ba = m_impl->zip->fileData(QString::fromStdString(fileName));
|
||||
return ByteArray(reinterpret_cast<const uint8_t*>(ba.constData()), ba.size());
|
||||
return m_impl->zip->fileData(fileName);
|
||||
}
|
||||
|
|
|
@ -32,14 +32,6 @@ class ZipReader
|
|||
{
|
||||
public:
|
||||
|
||||
enum Status {
|
||||
NoError,
|
||||
FileReadError,
|
||||
FileOpenError,
|
||||
FilePermissionsError,
|
||||
FileError
|
||||
};
|
||||
|
||||
struct FileInfo
|
||||
{
|
||||
io::path_t filePath;
|
||||
|
@ -57,7 +49,7 @@ public:
|
|||
|
||||
bool exists() const;
|
||||
void close();
|
||||
Status status() const;
|
||||
bool hasError() const;
|
||||
|
||||
std::vector<FileInfo> fileInfoList() const;
|
||||
ByteArray fileData(const std::string& fileName) const;
|
||||
|
|
|
@ -21,10 +21,8 @@
|
|||
*/
|
||||
#include "zipwriter.h"
|
||||
|
||||
#include <QBuffer>
|
||||
|
||||
#include "internal/zipcontainer.h"
|
||||
#include "io/file.h"
|
||||
#include "internal/qzipwriter_p.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
|
@ -32,9 +30,7 @@ using namespace mu;
|
|||
|
||||
struct ZipWriter::Impl
|
||||
{
|
||||
MQZipWriter* zip = nullptr;
|
||||
QByteArray data;
|
||||
QBuffer buf;
|
||||
ZipContainer* zip = nullptr;
|
||||
bool isClosed = false;
|
||||
};
|
||||
|
||||
|
@ -47,18 +43,14 @@ ZipWriter::ZipWriter(const io::path_t& filePath)
|
|||
}
|
||||
|
||||
m_impl = new Impl();
|
||||
m_impl->buf.setBuffer(&m_impl->data);
|
||||
m_impl->buf.open(QIODevice::WriteOnly);
|
||||
m_impl->zip = new MQZipWriter(&m_impl->buf);
|
||||
m_impl->zip = new ZipContainer(m_device);
|
||||
}
|
||||
|
||||
ZipWriter::ZipWriter(io::IODevice* device)
|
||||
{
|
||||
m_device = device;
|
||||
m_impl = new Impl();
|
||||
m_impl->buf.setBuffer(&m_impl->data);
|
||||
m_impl->buf.open(QIODevice::WriteOnly);
|
||||
m_impl->zip = new MQZipWriter(&m_impl->buf);
|
||||
m_impl->zip = new ZipContainer(m_device);
|
||||
}
|
||||
|
||||
ZipWriter::~ZipWriter()
|
||||
|
@ -75,10 +67,6 @@ ZipWriter::~ZipWriter()
|
|||
|
||||
void ZipWriter::flush()
|
||||
{
|
||||
if (m_device) {
|
||||
m_device->seek(0);
|
||||
m_device->write(m_impl->data);
|
||||
}
|
||||
}
|
||||
|
||||
void ZipWriter::close()
|
||||
|
@ -96,13 +84,13 @@ void ZipWriter::close()
|
|||
m_impl->isClosed = true;
|
||||
}
|
||||
|
||||
ZipWriter::Status ZipWriter::status() const
|
||||
bool ZipWriter::hasError() const
|
||||
{
|
||||
return static_cast<Status>(m_impl->zip->status());
|
||||
return m_impl->zip->status() != ZipContainer::NoError;
|
||||
}
|
||||
|
||||
void ZipWriter::addFile(const std::string& fileName, const ByteArray& data)
|
||||
{
|
||||
m_impl->zip->addFile(QString::fromStdString(fileName), data.toQByteArrayNoCopy());
|
||||
m_impl->zip->addFile(fileName, data);
|
||||
flush();
|
||||
}
|
||||
|
|
|
@ -29,20 +29,13 @@ namespace mu {
|
|||
class ZipWriter
|
||||
{
|
||||
public:
|
||||
enum Status {
|
||||
NoError,
|
||||
FileWriteError,
|
||||
FileOpenError,
|
||||
FilePermissionsError,
|
||||
FileError
|
||||
};
|
||||
|
||||
explicit ZipWriter(const io::path_t& filePath);
|
||||
explicit ZipWriter(io::IODevice* device);
|
||||
~ZipWriter();
|
||||
|
||||
void close();
|
||||
Status status() const;
|
||||
bool hasError() const;
|
||||
|
||||
void addFile(const std::string& fileName, const ByteArray& data);
|
||||
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
|
||||
#include "io/fileinfo.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
|
||||
using namespace mu::io;
|
||||
|
||||
class Global_IO_FileInfoTests : public ::testing::Test
|
||||
|
@ -370,3 +373,15 @@ TEST_F(Global_IO_FileInfoTests, IsAbsolute)
|
|||
EXPECT_TRUE(ret);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(Global_IO_FileInfoTests, DirPath)
|
||||
{
|
||||
{
|
||||
//! GIVE Some file path
|
||||
path_t filePath = "/path/to/filename.ext1";
|
||||
//! DO
|
||||
path_t dirPath = FileInfo(filePath).dir().path();
|
||||
//! CHECK
|
||||
EXPECT_EQ(dirPath, "/path/to");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,13 +116,21 @@ char Char::toAscii(char16_t c, bool* ok)
|
|||
bool Char::isLetter(char16_t c)
|
||||
{
|
||||
//! TODO
|
||||
#ifndef GLOBAL_NO_QT_SUPPORT
|
||||
return QChar::isLetter(c);
|
||||
#else
|
||||
return std::isalpha(static_cast<unsigned char>(c));
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Char::isSpace(char16_t c)
|
||||
{
|
||||
//! TODO
|
||||
#ifndef GLOBAL_NO_QT_SUPPORT
|
||||
return QChar::isSpace(c);
|
||||
#else
|
||||
return std::isspace(static_cast<unsigned char>(c));
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Char::isDigit(char16_t c)
|
||||
|
@ -138,21 +146,33 @@ int Char::digitValue() const
|
|||
return m_ch - '0';
|
||||
}
|
||||
|
||||
bool Char::isPunct(char16_t c)
|
||||
bool Char::isPunct(char16_t ch)
|
||||
{
|
||||
return QChar::isPunct(c);
|
||||
#ifndef GLOBAL_NO_QT_SUPPORT
|
||||
return QChar::isPunct(ch);
|
||||
#else
|
||||
return std::ispunct(static_cast<unsigned char>(ch));
|
||||
#endif
|
||||
}
|
||||
|
||||
char16_t Char::toLower(char16_t ch)
|
||||
{
|
||||
//! TODO
|
||||
#ifndef GLOBAL_NO_QT_SUPPORT
|
||||
return QChar::toLower(ch);
|
||||
#else
|
||||
return std::tolower(static_cast<unsigned char>(ch));
|
||||
#endif
|
||||
}
|
||||
|
||||
char16_t Char::toUpper(char16_t ch)
|
||||
{
|
||||
//! TODO
|
||||
#ifndef GLOBAL_NO_QT_SUPPORT
|
||||
return QChar::toUpper(ch);
|
||||
#else
|
||||
return std::toupper(static_cast<unsigned char>(ch));
|
||||
#endif
|
||||
}
|
||||
|
||||
// ============================
|
||||
|
@ -419,6 +439,7 @@ std::u32string String::toStdU32String() const
|
|||
return s32;
|
||||
}
|
||||
|
||||
#ifndef GLOBAL_NO_QT_SUPPORT
|
||||
String String::fromQString(const QString& str)
|
||||
{
|
||||
const QChar* qu = str.unicode();
|
||||
|
@ -437,6 +458,8 @@ QString String::toQString() const
|
|||
return QString(reinterpret_cast<const QChar*>(u), static_cast<int>(size()));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
size_t String::size() const
|
||||
{
|
||||
return constStr().size();
|
||||
|
@ -868,7 +891,11 @@ String String::trimmed() const
|
|||
String String::simplified() const
|
||||
{
|
||||
//! TODO
|
||||
#ifndef GLOBAL_NO_QT_SUPPORT
|
||||
return String::fromQString(toQString().simplified());
|
||||
#else
|
||||
return *this;
|
||||
#endif
|
||||
}
|
||||
|
||||
String String::toXmlEscaped(char16_t c)
|
||||
|
@ -910,15 +937,29 @@ String String::toXmlEscaped() const
|
|||
String String::toLower() const
|
||||
{
|
||||
//! TODO
|
||||
#ifndef GLOBAL_NO_QT_SUPPORT
|
||||
QString qs = toQString();
|
||||
return String::fromQString(qs.toLower());
|
||||
#else
|
||||
String s = *this;
|
||||
std::u16string& us = s.mutStr();
|
||||
std::transform(us.begin(), us.end(), us.begin(), [](char16_t c){ return Char::toLower(c); });
|
||||
return s;
|
||||
#endif
|
||||
}
|
||||
|
||||
String String::toUpper() const
|
||||
{
|
||||
//! TODO
|
||||
#ifndef GLOBAL_NO_QT_SUPPORT
|
||||
QString qs = toQString();
|
||||
return String::fromQString(qs.toUpper());
|
||||
#else
|
||||
String s = *this;
|
||||
std::u16string& us = s.mutStr();
|
||||
std::transform(us.begin(), us.end(), us.begin(), [](char16_t c){ return Char::toUpper(c); });
|
||||
return s;
|
||||
#endif
|
||||
}
|
||||
|
||||
int String::toInt(bool* ok, int base) const
|
||||
|
@ -989,15 +1030,6 @@ double String::toDouble(bool* ok) const
|
|||
// ============================
|
||||
// StringList
|
||||
// ============================
|
||||
|
||||
StringList::StringList(const QStringList& l)
|
||||
{
|
||||
reserve(l.size());
|
||||
for (const QString& s : l) {
|
||||
push_back(String::fromQString(s));
|
||||
}
|
||||
}
|
||||
|
||||
StringList& StringList::append(const StringList& l)
|
||||
{
|
||||
for (const String& s : l) {
|
||||
|
@ -1051,6 +1083,15 @@ void StringList::removeAt(size_t i)
|
|||
erase(begin() + i);
|
||||
}
|
||||
|
||||
#ifndef GLOBAL_NO_QT_SUPPORT
|
||||
StringList::StringList(const QStringList& l)
|
||||
{
|
||||
reserve(l.size());
|
||||
for (const QString& s : l) {
|
||||
push_back(String::fromQString(s));
|
||||
}
|
||||
}
|
||||
|
||||
QStringList StringList::toQStringList() const
|
||||
{
|
||||
QStringList l;
|
||||
|
@ -1061,6 +1102,7 @@ QStringList StringList::toQStringList() const
|
|||
return l;
|
||||
}
|
||||
|
||||
#endif
|
||||
// ============================
|
||||
// AsciiChar
|
||||
// ============================
|
||||
|
|
|
@ -275,7 +275,6 @@ void GPConverter::fixPercussion()
|
|||
void GPConverter::setupTabDisplayStyle()
|
||||
{
|
||||
GPDomModel::GPProperties properties = _gpDom->properties();
|
||||
using parts_import_t = GPDomModel::TabImportOption;
|
||||
std::vector<GPDomModel::TabImportOption>& partsImportOpts = properties.partsImportOptions;
|
||||
bool importLinkedStaffForce = properties.createLinkedTabForce;
|
||||
|
||||
|
|
|
@ -45,8 +45,8 @@ void StaffSettingsModel::load(const QString& staffId)
|
|||
m_type = staff->staffType()->type();
|
||||
|
||||
m_voicesVisibility.clear();
|
||||
for (const QVariant& voice: staff->visibilityVoices()) {
|
||||
m_voicesVisibility << voice.toBool();
|
||||
for (const bool& voice : staff->visibilityVoices()) {
|
||||
m_voicesVisibility << voice;
|
||||
}
|
||||
|
||||
emit cutawayEnabledChanged();
|
||||
|
|
|
@ -445,7 +445,7 @@ bool Palette::writeToFile(const QString& p) const
|
|||
}
|
||||
|
||||
ZipWriter f(path);
|
||||
if (f.status() != ZipWriter::NoError) {
|
||||
if (f.hasError()) {
|
||||
showWritingPaletteError(path);
|
||||
return false;
|
||||
}
|
||||
|
@ -486,7 +486,7 @@ bool Palette::writeToFile(const QString& p) const
|
|||
f.addFile("palette.xml", cbuf1.data());
|
||||
}
|
||||
f.close();
|
||||
if (f.status() != ZipWriter::NoError) {
|
||||
if (f.hasError()) {
|
||||
showWritingPaletteError(path);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -152,14 +152,14 @@ MasterPalette::MasterPalette(QWidget* parent)
|
|||
m_symbolItem->addChild(child);
|
||||
|
||||
std::vector<String> symbols = mu::keys(mu::smuflRanges());
|
||||
for (int i = 0; i < symbols.size(); i++) {
|
||||
for (size_t i = 0; i < symbols.size(); i++) {
|
||||
QString symbol = symbols[i].toQString();
|
||||
if (symbol == mu::SMUFL_ALL_SYMBOLS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QTreeWidgetItem* chld = new QTreeWidgetItem(QStringList(symbol));
|
||||
chld->setData(0, Qt::UserRole, m_idxAllSymbols + i + 1);
|
||||
chld->setData(0, Qt::UserRole, m_idxAllSymbols + static_cast<int>(i) + 1);
|
||||
|
||||
m_symbolItem->addChild(chld);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue