All Classes Namespaces Functions Variables Enumerations Properties Pages
qminiz.cpp
1 /*
2 
3 Pencil2D - Traditional Animation Software
4 Copyright (C) 2012-2020 Matthew Chiawen Chang
5 
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; version 2 of the License.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU 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 
26 Status MiniZ::sanityCheck(const QString& sZipFilePath)
27 {
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  if (read_err != MZ_ZIP_NO_ERROR) {
43  dd << QString("Miniz found an error while reading the file. - %1, %2").arg(static_cast<int>(read_err)).arg(mz_zip_get_error_string(read_err));
44  }
45  if (close_err != MZ_ZIP_NO_ERROR) {
46  dd << QString("Miniz found an error while closing file file. - %1, %2").arg(static_cast<int>(close_err)).arg(mz_zip_get_error_string(close_err));
47  }
48  return Status(Status::ERROR_MINIZ_FAIL, dd);
49  }
50 
51 
52  return Status::OK;
53 }
54 
55 size_t MiniZ::istreamReadCallback(void *pOpaque, mz_uint64 file_ofs, void * pBuf, size_t n)
56 {
57  std::istream *stream = static_cast<std::istream*>(pOpaque);
58  mz_int64 cur_ofs = stream->tellg();
59  if ((mz_int64)file_ofs < 0 || (cur_ofs != (mz_int64)file_ofs && stream->seekg((mz_int64)file_ofs, std::ios_base::beg)))
60  return 0;
61  stream->read(static_cast<char*>(pBuf), n);
62  return stream->gcount();
63 }
64 
65 // ReSharper disable once CppInconsistentNaming
66 Status MiniZ::compressFolder(QString zipFilePath, QString srcFolderPath, const QStringList& fileList, QString mimetype)
67 {
68  DebugDetails dd;
69  dd << QString("Creating Zip %1 from folder %2").arg(zipFilePath, srcFolderPath);
70 
71  if (!srcFolderPath.endsWith("/"))
72  {
73  srcFolderPath.append("/");
74  }
75 
77  mz_zip_zero_struct(mz);
78 
79  mz_bool ok = mz_zip_writer_init_file(mz, zipFilePath.toUtf8().data(), 0);
80 
81  ScopeGuard mzScopeGuard([&] {
82  mz_zip_writer_end(mz);
83  delete mz;
84  });
85 
86  if (!ok)
87  {
88  mz_zip_error err = mz_zip_get_last_error(mz);
89  dd << QString("Miniz writer init failed: error %1, %2").arg(static_cast<int>(err)).arg(mz_zip_get_error_string(err));;
90  }
91 
92  // Add special uncompressed mimetype file to help with the identification of projects
93  {
94  QByteArray mimeData = mimetype.toUtf8();
95  std::stringstream mimeStream(mimeData.toStdString());
96  ok = mz_zip_writer_add_read_buf_callback(mz, "mimetype", MiniZ::istreamReadCallback, &mimeStream, mimeData.length(),
97  0, "", 0, MZ_NO_COMPRESSION, 0, 0,
98  0, 0);
99  if (!ok)
100  {
101  mz_zip_error err = mz_zip_get_last_error(mz);
102  dd << QString("Cannot add mimetype: error %1").arg(static_cast<int>(err)).arg(mz_zip_get_error_string(err));
103  }
104  }
105 
106  //qDebug() << "SrcFolder=" << srcFolderPath;
107  for (const QString& filePath : fileList)
108  {
109  QString sRelativePath = filePath;
110  sRelativePath.remove(srcFolderPath);
111  if (sRelativePath == "mimetype") continue;
112 
113  dd << QString("Add file to zip: ").append(sRelativePath);
114 
115  ok = mz_zip_writer_add_file(mz,
116  sRelativePath.toUtf8().data(),
117  filePath.toUtf8().data(),
118  "", 0, MZ_BEST_SPEED);
119  if (!ok)
120  {
121  mz_zip_error err = mz_zip_get_last_error(mz);
122  dd << QString("Cannot add %3: error %1, %2").arg(static_cast<int>(err)).arg(mz_zip_get_error_string(err), sRelativePath);
123  }
124  }
125  ok &= mz_zip_writer_finalize_archive(mz);
126  if (!ok)
127  {
128  mz_zip_error err = mz_zip_get_last_error(mz);
129  dd << QString("Miniz finalize archive failed: error %1, %2").arg(static_cast<int>(err)).arg(mz_zip_get_error_string(err));
130  return Status(Status::FAIL, dd);
131  }
132 
133  ok &= mz_zip_writer_end(mz);
134 
135  mzScopeGuard.dismiss();
136  ScopeGuard mzScopeGuard2([&] { delete mz; });
137 
138  if (!ok)
139  {
140  mz_zip_error err = mz_zip_get_last_error(mz);
141  dd << QString("Miniz writer end failed: error %1, %2").arg(static_cast<int>(err)).arg(mz_zip_get_error_string(err));
142  return Status(Status::FAIL, dd);
143  }
144 
145  return Status::OK;
146 }
147 
148 Status MiniZ::uncompressFolder(QString zipFilePath, QString destPath)
149 {
150  DebugDetails dd;
151  dd << QString("Unzip file %1 to folder %2").arg(zipFilePath, destPath);
152 
153  if (!QFile::exists(zipFilePath))
154  {
155  return Status::FILE_NOT_FOUND;
156  }
157 
158  QString sBaseDir = QFileInfo(destPath).absolutePath();
159  QDir baseDir(sBaseDir);
160  if (!baseDir.exists())
161  {
162  bool ok = baseDir.mkpath(".");
163  Q_ASSERT(ok);
164  }
165 
166  baseDir.makeAbsolute();
167 
168  mz_zip_archive* mz = new mz_zip_archive;
169  mz_zip_zero_struct(mz);
170 
171  mz_bool ok = mz_zip_reader_init_file(mz, zipFilePath.toUtf8().data(), 0);
172 
173  ScopeGuard mzScopeGuard([&] {
174  mz_zip_reader_end(mz);
175  delete mz;
176  });
177 
178  if (!ok)
179  return Status(Status::FAIL, dd);
180 
181  int num = mz_zip_reader_get_num_files(mz);
182 
184  OnScopeExit(delete stat);
185 
186  for (int i = 0; i < num; ++i)
187  {
188  ok &= mz_zip_reader_file_stat(mz, i, stat);
189 
190  if (stat->m_is_directory)
191  {
192  QString sFolderPath = QString::fromUtf8(stat->m_filename);
193  dd << QString("Make Dir: ").append(sFolderPath);
194 
195  bool mkDirOK = baseDir.mkpath(sFolderPath);
196  Q_ASSERT(mkDirOK);
197  if (!mkDirOK)
198  dd << "Make Dir failed.";
199  }
200  }
201 
202  for (int i = 0; i < num; ++i)
203  {
204  ok &= mz_zip_reader_file_stat(mz, i, stat);
205 
206  if (!stat->m_is_directory)
207  {
208  if (QString(stat->m_filename) == "mimetype") continue;
209  QString sFullPath = baseDir.filePath(QString::fromUtf8(stat->m_filename));
210  dd << QString("Unzip file: ").append(sFullPath);
211  bool b = QFileInfo(sFullPath).absoluteDir().mkpath(".");
212  Q_ASSERT(b);
213 
214  bool extractOK = mz_zip_reader_extract_to_file(mz, i, sFullPath.toUtf8(), 0);
215  if (!extractOK)
216  {
217  ok = false;
218  dd << "File extraction failed.";
219  }
220  }
221  }
222 
223  ok &= mz_zip_reader_end(mz);
224 
225  mzScopeGuard.dismiss();
226  ScopeGuard mzScopeGuard2([&] {
227  delete mz;
228  });
229 
230  if (!ok)
231  {
232  dd << "Unzip error!";
233  }
234  return Status::OK;
235 }
QString & append(QChar ch)
int length() const const
bool exists() const const
QString & remove(int position, int n)
std::string toStdString() const const
QString fromUtf8(const char *str, int size)
const char * constData() const const
QDir absoluteDir() const const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
char * data()
QString absolutePath() const const
bool mkpath(const QString &dirPath) const const
QByteArray toUtf8() const const