Sync DrDump crash handler with TortoiseSVN codebase
[TortoiseGit.git] / ext / CrashServer / CommonLibs / Zlib / ZipUnzip.cpp
blobfa44f2ec171400ff32c023aade22beccb10b6cf7
1 // Copyright 2014 Idol Software, Inc.
2 //
3 // This file is part of Doctor Dump SDK.
4 //
5 // Doctor Dump SDK is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU Lesser General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
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 Lesser General Public License for more details.
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include <atlstr.h>
19 #include <atlfile.h>
20 #include <stdexcept>
21 #include "ZipUnzip.h"
22 #include "contrib\minizip\zip.h"
23 #include "contrib\minizip\unzip.h"
24 #include "contrib\minizip\iowin32.h"
26 using namespace std;
28 extern "C" voidpf ZCALLBACK win32_open64_file_funcW(voidpf opaque,const void* filename,int mode);
30 Zip::Zip(LPCWSTR pszFilename, bool append)
32 zlib_filefunc_def ffunc;
33 fill_win32_filefunc(&ffunc);
34 #if ZLIB128
35 // Hack used to force Unicode version of CreateFile in ANSI zipOpen2 function
36 ffunc.zopen_file = (open_file_func) win32_open64_file_funcW;
37 m_zf = zipOpen2((const char*)pszFilename, append ? APPEND_STATUS_ADDINZIP : APPEND_STATUS_CREATE, NULL, &ffunc);
38 #else
39 m_zf = zipOpen2((const char*)CW2A(pszFilename), append ? APPEND_STATUS_ADDINZIP : APPEND_STATUS_CREATE, NULL, &ffunc);
40 #endif
42 if (!m_zf)
43 throw runtime_error(append ? "failed to append zip file" : "failed to create zip file");
46 Zip::~Zip(void)
48 zipClose(m_zf, NULL);
51 void Zip::AddFile(LPCWSTR pszFilename, LPCWSTR pszFilenameInZip, bool* cancel)
53 zip_fileinfo zi = {};
55 WIN32_FIND_DATAW ff;
56 HANDLE hFind = FindFirstFileW(pszFilename, &ff);
57 if (hFind == INVALID_HANDLE_VALUE)
58 throw runtime_error("file to add to zip not found");
60 if (!pszFilenameInZip)
62 LPCWSTR slashPos = wcsrchr(pszFilename, L'\\');
63 if (!slashPos)
64 slashPos = wcsrchr(pszFilename, L'/');
65 pszFilenameInZip = slashPos ? slashPos + 1 : pszFilename;
68 FILETIME ftLocal;
69 FileTimeToLocalFileTime(&(ff.ftLastWriteTime), &ftLocal);
70 FileTimeToDosDateTime(&ftLocal, ((LPWORD)&zi.dosDate)+1, ((LPWORD)&zi.dosDate)+0);
71 FindClose(hFind);
73 int err = zipOpenNewFileInZip3(m_zf, CW2A(pszFilenameInZip), &zi,
74 NULL, 0, NULL, 0, NULL /* comment*/,
75 Z_DEFLATED, Z_BEST_COMPRESSION, 0,
76 -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
77 NULL, 0);
79 if (err != ZIP_OK)
80 throw runtime_error("failed to create file in zip");
83 CAtlFile hFile(CreateFileW(pszFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
84 if (hFile == INVALID_HANDLE_VALUE)
85 throw runtime_error("file to add to zip not found");
87 BYTE buf[1024*8];
88 DWORD dwReaded;
89 while (SUCCEEDED(hFile.Read(buf, _countof(buf), dwReaded)) && dwReaded != 0)
91 err = zipWriteInFileInZip(m_zf, buf, dwReaded);
92 if (err != ZIP_OK)
93 throw runtime_error("failed to write to zip");
94 if (cancel && *cancel)
95 throw std::runtime_error("canceled");
98 if (ZIP_OK != zipCloseFileInZip(m_zf))
99 throw runtime_error("failed to finalize file in zip");
102 static unzFile Unzip_Open(LPCWSTR pszFilename)
104 zlib_filefunc_def ffunc;
105 fill_win32_filefunc(&ffunc);
106 unzFile uf = unzOpen2(CW2CT(pszFilename), &ffunc);
107 if (!uf)
108 throw runtime_error("failed to open zip file");
109 return uf;
112 static CStringW Unzip_GetCurentFilePath(unzFile uf, FILETIME& ftLocal)
114 unz_file_info file_info;
115 CStringA pathA;
116 const int pathLen = 256;
117 if (UNZ_OK != unzGetCurrentFileInfo(uf, &file_info, pathA.GetBuffer(pathLen), pathLen, NULL, 0, NULL, 0))
118 throw runtime_error("failed to unzGetCurrentFileInfo");
119 pathA.ReleaseBuffer(-1);
120 DosDateTimeToFileTime((WORD)(file_info.dosDate>>16), (WORD)file_info.dosDate, &ftLocal);
121 return CStringW(pathA);
124 static void Unzip_ExtractFile(unzFile uf, const CStringW& path, DWORD flagsAndAttributes, FILETIME ftLocal)
128 if (UNZ_OK != unzOpenCurrentFilePassword(uf, NULL))
129 throw runtime_error("failed to unzOpenCurrentFilePassword");
131 CAtlFile file(CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, flagsAndAttributes, NULL));
132 if (file == INVALID_HANDLE_VALUE)
133 throw runtime_error("failed to CreateFile");
135 vector<BYTE> buf(8192);
136 while (1)
138 int size = unzReadCurrentFile(uf, &buf[0], (unsigned int)buf.size());
139 if (size < 0)
140 throw runtime_error("failed to unzReadCurrentFile");
141 if (size == 0)
142 break;
144 if (FAILED(file.Write(&buf[0], size)))
145 throw runtime_error("failed to WriteFile");
148 FILETIME ftm, ftCreate, ftLastAcc, ftLastWrite;
149 GetFileTime(file, &ftCreate, &ftLastAcc, &ftLastWrite);
150 LocalFileTimeToFileTime(&ftLocal, &ftm);
151 SetFileTime(file, &ftm, &ftLastAcc, &ftm);
153 if (UNZ_OK != unzCloseCurrentFile(uf))
154 throw runtime_error("failed to unzCloseCurrentFile");
156 catch (...)
158 DeleteFileW(path);
162 std::vector<CStringW> Unzip::Extract(LPCWSTR pszFilename, LPCWSTR pszFolder, std::function<bool(LPCWSTR filePath, DWORD& flagsAndAttributes)> predicate)
164 std::vector<CStringW> result;
166 unzFile uf = NULL;
169 uf = Unzip_Open(pszFilename);
171 unz_global_info gi;
172 if (UNZ_OK != unzGetGlobalInfo(uf, &gi))
173 throw runtime_error("failed to unzGetGlobalInfo");
175 for (uLong i = 0; i < gi.number_entry; ++i)
177 FILETIME ftLocal;
178 CStringW path = CStringW(pszFolder) + L'\\' + Unzip_GetCurentFilePath(uf, ftLocal);
180 CStringW filename = path.Mid(path.ReverseFind(L'\\') + 1);
181 if (filename.IsEmpty())
183 if (!CreateDirectoryW(path, NULL))
184 throw runtime_error("failed to CreateDirectory");
185 continue;
188 DWORD flagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
189 if (predicate(path, flagsAndAttributes))
191 Unzip_ExtractFile(uf, path, flagsAndAttributes, ftLocal);
192 result.push_back(path);
195 if (i + 1 < gi.number_entry && UNZ_OK != unzGoToNextFile(uf))
196 throw runtime_error("failed to unzGoToNextFile");
198 unzClose(uf);
200 return result;
202 catch (...)
204 if (uf != NULL)
205 unzClose(uf);
206 for each (auto file in result)
207 DeleteFileW(file);
208 throw;
212 std::vector<CStringW> Unzip::Extract(LPCWSTR pszFilename, LPCWSTR pszFolder)
214 return Extract(pszFilename, pszFolder, [](LPCWSTR filePath, DWORD& flagsAndAttributes) { return true; });
217 bool DeflateBuffer(const BYTE* buffer, size_t bufferLen, std::vector<BYTE>& outBuffer, const char* dictionary)
219 z_stream stream = {};
220 deflateInit(&stream, Z_BEST_COMPRESSION);
221 outBuffer.resize(deflateBound(&stream, static_cast<uLong>(bufferLen)));
222 if (dictionary)
223 deflateSetDictionary(&stream, (const Bytef*)dictionary, static_cast<uInt>(strlen(dictionary)));
224 stream.next_in = (Bytef*)buffer;
225 stream.avail_in = static_cast<uInt>(bufferLen);
226 stream.next_out = &outBuffer[0];
227 stream.avail_out = static_cast<uInt>(outBuffer.size());
228 while (1)
230 switch (deflate(&stream, Z_FINISH))
232 case Z_STREAM_END:
233 outBuffer.resize(outBuffer.size() - stream.avail_out);
234 deflateEnd(&stream);
235 return true;
236 case Z_OK:
237 stream.avail_out += static_cast<uInt>(outBuffer.size());
238 outBuffer.resize(outBuffer.size() * 2);
239 stream.next_out = &outBuffer[outBuffer.size() - stream.avail_out];
240 break;
241 default:
242 deflateEnd(&stream);
243 return false;
248 bool InflateBuffer(const BYTE* buffer, size_t bufferLen, std::vector<BYTE>& outBuffer, const char* dictionary)
250 z_stream stream = {};
251 stream.next_in = (Bytef*)buffer;
252 stream.avail_in = static_cast<uInt>(bufferLen);
253 inflateInit(&stream);
254 outBuffer.resize(2*bufferLen);
255 stream.next_out = &outBuffer[0];
256 stream.avail_out = static_cast<uInt>(outBuffer.size());
257 while (1)
259 switch (inflate(&stream, 0))
261 case Z_NEED_DICT:
262 if (!dictionary || Z_OK != inflateSetDictionary(&stream, (const Bytef*)dictionary, static_cast<uInt>(strlen(dictionary))))
264 inflateEnd(&stream);
265 return false;
267 break;
268 case Z_STREAM_END:
269 outBuffer.resize(outBuffer.size() - stream.avail_out);
270 inflateEnd(&stream);
271 return true;
272 case Z_OK:
273 stream.avail_out += static_cast<uInt>(outBuffer.size());
274 outBuffer.resize(outBuffer.size() * 2);
275 stream.next_out = &outBuffer[outBuffer.size() - stream.avail_out];
276 break;
277 default:
278 inflateEnd(&stream);
279 return false;