Pencil2D Animation
Download Community News Docs Contribute
  • Overview
  • Articles
  • Code
  •  
  • Class List
  • Class Index
  • Class Hierarchy
  • Class Members
  • File List
Loading...
Searching...
No Matches
  • core_lib
  • src
qminiz.cpp
1/*
2
3Pencil2D - Traditional Animation Software
4Copyright (C) 2012-2020 Matthew Chiawen Chang
5
6This program is free software; you can redistribute it and/or
7modify it under the terms of the GNU General Public License
8as published by the Free Software Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15*/
16#include "qminiz.h"
17
18#include <sstream>
19#include <QFileInfo>
20#include <QDir>
21#include <QDebug>
22#include <QDirIterator>
23#include "util.h"
24
25
26Status MiniZ::sanityCheck(const QString& sZipFilePath)
27{
28 mz_zip_archive* mz = new mz_zip_archive;
29 OnScopeExit(delete mz);
30 mz_zip_zero_struct(mz);
31 QByteArray utf8Bytes = sZipFilePath.toUtf8();
32 mz_bool readOk = mz_zip_reader_init_file(mz, utf8Bytes.constData(), 0);
33
34 mz_zip_error read_err = mz_zip_get_last_error(mz);
35
36 mz_bool closeOk = mz_zip_reader_end(mz);
37
38 mz_zip_error close_err = mz_zip_get_last_error(mz);
39
40 if (!readOk || !closeOk) {
41 DebugDetails dd;
42
43 dd << "\n[Miniz sanity check]\n";
44 if (read_err != MZ_ZIP_NO_ERROR) {
45 dd << QString("Found an error while reading the file. Error code: %2, reason: %3").arg(static_cast<int>(read_err)).arg(mz_zip_get_error_string(read_err));
46 }
47 if (close_err != MZ_ZIP_NO_ERROR) {
48 dd << QString("Found an error while closing the file. Error code: %2, reason: %3").arg(static_cast<int>(close_err)).arg(mz_zip_get_error_string(close_err));
49 }
50 return Status(Status::ERROR_MINIZ_FAIL, dd);
51 }
52
53
54 return Status::OK;
55}
56
57size_t MiniZ::istreamReadCallback(void *pOpaque, mz_uint64 file_ofs, void * pBuf, size_t n)
58{
59 std::istream *stream = static_cast<std::istream*>(pOpaque);
60 mz_int64 cur_ofs = stream->tellg();
61 if ((mz_int64)file_ofs < 0 || (cur_ofs != (mz_int64)file_ofs && stream->seekg((mz_int64)file_ofs, std::ios_base::beg)))
62 return 0;
63 stream->read(static_cast<char*>(pBuf), n);
64 return stream->gcount();
65}
66
67// ReSharper disable once CppInconsistentNaming
68Status MiniZ::compressFolder(QString zipFilePath, QString srcFolderPath, const QStringList& fileList, QString mimetype)
69{
70 DebugDetails dd;
71 dd << "\n[Miniz COMPRESSION diagnostics]\n";
72 dd << QString("Creating Zip %1 from folder %2").arg(zipFilePath, srcFolderPath);
73
74 if (!srcFolderPath.endsWith("/"))
75 {
76 dd << "Adding / to path";
77 srcFolderPath.append("/");
78 }
79
80 mz_zip_archive* mz = new mz_zip_archive;
81 ScopeGuard mzScopeGuard([&] {
82 delete mz;
83 });
84
85 mz_zip_zero_struct(mz);
86
87 mz_bool ok = mz_zip_writer_init_file(mz, zipFilePath.toUtf8().data(), 0);
88
89 if (!ok)
90 {
91 mz_zip_error err = mz_zip_get_last_error(mz);
92 dd << QString("Error: Failed to init writer. Error code: %1, reason: %2").arg(static_cast<int>(err)).arg(mz_zip_get_error_string(err));
93 return Status(Status::FAIL, dd);
94 }
95 ScopeGuard mzScopeGuard2([&] {
96 mz_zip_writer_end(mz);
97 });
98
99 // Add special uncompressed mimetype file to help with the identification of projects
100 {
101 QByteArray mimeData = mimetype.toUtf8();
102 std::stringstream mimeStream(mimeData.toStdString());
103 ok = mz_zip_writer_add_read_buf_callback(mz, "mimetype", MiniZ::istreamReadCallback, &mimeStream, mimeData.length(),
104 0, "", 0, MZ_NO_COMPRESSION, 0, 0,
105 0, 0);
106 if (!ok)
107 {
108 mz_zip_error err = mz_zip_get_last_error(mz);
109 dd << QString("ERROR: Unable to add mimetype. Error code: %1, reason: %2").arg(static_cast<int>(err)).arg(mz_zip_get_error_string(err));
110 return Status(Status::FAIL, dd);
111 }
112 }
113
114 QString canonicalFolder = QFileInfo(srcFolderPath).canonicalFilePath();
115 if (canonicalFolder.isEmpty())
116 {
117 dd << QString("Error: Source folder does not exist: %1").arg(srcFolderPath);
118 return Status(Status::FAIL, dd);
119 }
120 QDir baseDir(canonicalFolder);
121 for (const QString& filePath : fileList)
122 {
123 QString canonicalFilePath = QFileInfo(filePath).canonicalFilePath();
124 if (canonicalFilePath.isEmpty())
125 {
126 dd << QString("Error: File does not exist: %1").arg(filePath);
127 return Status(Status::FAIL, dd);
128 }
129 QString sRelativePath = baseDir.relativeFilePath(canonicalFilePath);
130 if (sRelativePath.startsWith("../") || sRelativePath == "..")
131 {
132 dd << QString("Error: File is outside the base folder: %1").arg(filePath);
133 return Status(Status::FAIL, dd);
134 }
135 if (sRelativePath == "mimetype") continue;
136
137 dd << QString("Add file to zip: ").append(sRelativePath);
138
139 ok = mz_zip_writer_add_file(mz,
140 sRelativePath.toUtf8().data(),
141 filePath.toUtf8().data(),
142 "", 0, MZ_BEST_SPEED);
143 if (!ok)
144 {
145 mz_zip_error err = mz_zip_get_last_error(mz);
146 dd << QString("Error: Unable to add file: %3. Error code: %1, reason: %2 - Aborting!").arg(static_cast<int>(err)).arg(mz_zip_get_error_string(err), sRelativePath);
147 return Status(Status::FAIL, dd);
148 }
149 }
150 ok &= mz_zip_writer_finalize_archive(mz);
151 if (!ok)
152 {
153 mz_zip_error err = mz_zip_get_last_error(mz);
154 dd << QString("Error: Failed to finalize archive. Error code %1, reason: %2").arg(static_cast<int>(err)).arg(mz_zip_get_error_string(err));
155 return Status(Status::FAIL, dd);
156 }
157
158 return Status::OK;
159}
160
161Status MiniZ::uncompressFolder(QString zipFilePath, QString destPath)
162{
163 DebugDetails dd;
164 dd << "\n[Miniz EXTRACTION diagnostics]\n";
165 dd << QString("Unzip file %1 to folder %2").arg(zipFilePath, destPath);
166
167 if (!QFile::exists(zipFilePath))
168 {
169 dd << QString("Error: Zip file does not exist.");
170 return Status::FILE_NOT_FOUND;
171 }
172
173 QString sBaseDir = QFileInfo(destPath).absolutePath();
174 QDir baseDir(sBaseDir);
175 if (!baseDir.exists())
176 {
177 bool ok = baseDir.mkpath(".");
178 Q_ASSERT(ok);
179 }
180
181 baseDir.makeAbsolute();
182
183 mz_zip_archive* mz = new mz_zip_archive;
184 ScopeGuard mzScopeGuard([&] {
185 delete mz;
186 });
187
188 mz_zip_zero_struct(mz);
189
190 mz_bool ok = mz_zip_reader_init_file(mz, zipFilePath.toUtf8().data(), 0);
191 if (!ok) {
192 mz_zip_error err = mz_zip_get_last_error(mz);
193 dd << QString("Error: Failed to init reader. Error code: %1, reason: %2").arg(static_cast<int>(err)).arg(mz_zip_get_error_string(err));
194 return Status(Status::FAIL, dd);
195 }
196 ScopeGuard mzScopeGuard2([&] {
197 mz_zip_reader_end(mz);
198 });
199
200 int num = mz_zip_reader_get_num_files(mz);
201
202 mz_zip_archive_file_stat* stat = new mz_zip_archive_file_stat;
203 OnScopeExit(delete stat);
204
205 for (int i = 0; i < num; ++i)
206 {
207 ok &= mz_zip_reader_file_stat(mz, i, stat);
208
209 if (stat->m_is_directory)
210 {
211 QString sFolderPath = QString::fromUtf8(stat->m_filename);
212 dd << QString("Make Dir: ").append(sFolderPath);
213
214 bool mkDirOK = baseDir.mkpath(sFolderPath);
215 Q_ASSERT(mkDirOK);
216 if (!mkDirOK)
217 dd << "Make Dir failed.";
218 }
219 }
220
221 for (int i = 0; i < num; ++i)
222 {
223 ok &= mz_zip_reader_file_stat(mz, i, stat);
224
225 if (!stat->m_is_directory)
226 {
227 if (QString(stat->m_filename) == "mimetype") continue;
228 QString sFullPath = baseDir.filePath(QString::fromUtf8(stat->m_filename));
229 dd << QString("Unzip file: ").append(sFullPath);
230 bool b = QFileInfo(sFullPath).absoluteDir().mkpath(".");
231 Q_ASSERT(b);
232
233 bool extractOK = mz_zip_reader_extract_to_file(mz, i, sFullPath.toUtf8(), 0);
234 if (!extractOK)
235 {
236 ok = false;
237 mz_zip_error err = mz_zip_get_last_error(mz);
238 dd << QString("WARNING: Unable to extract file. Error code: %1, reason: %2").arg(static_cast<int>(err)).arg(mz_zip_get_error_string(err));
239 }
240 }
241 }
242
243 if (!ok)
244 {
245 return Status(Status::FAIL, dd);
246 }
247 return Status::OK;
248}
DebugDetails
Definition: pencilerror.h:25
ScopeGuard
Definition: util.h:46
Status
Definition: pencilerror.h:40
QByteArray
QByteArray::constData
const char * constData() const const
QByteArray::data
char * data()
QByteArray::length
int length() const const
QByteArray::toStdString
int toStdString() const const
QDir
QDir::mkpath
bool mkpath(const QString &dirPath) const const
QFileInfo
QFileInfo::absoluteDir
QDir absoluteDir() const const
QFileInfo::absolutePath
QString absolutePath() const const
QFileInfo::canonicalFilePath
QString canonicalFilePath() const const
QString
QString::append
QString & append(QChar ch)
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString::endsWith
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString::fromUtf8
QString fromUtf8(const char *str, int size)
QString::isEmpty
bool isEmpty() const const
QString::startsWith
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString::toUtf8
QByteArray toUtf8() const const
QStringList
mz_zip_archive_file_stat
Definition: miniz.h:1076
mz_zip_archive
Definition: miniz.h:1214
Generated on Mon May 4 2026 07:50:47 for Pencil2D by doxygen 1.9.6 based on revision 3ed50cdd696e72315cedf30508d3572536c3876e