1
0

unzip.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /*
  2. * unzip.cpp
  3. * ---------
  4. * Purpose: Implementation file for extracting modules from .zip archives, making use of MiniZip (from the zlib contrib package)
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "stdafx.h"
  10. #include "../common/FileReader.h"
  11. #include "unzip.h"
  12. #include "../common/misc_util.h"
  13. #include <algorithm>
  14. #include <vector>
  15. #if defined(MPT_WITH_ZLIB) && defined(MPT_WITH_MINIZIP)
  16. #include <contrib/minizip/unzip.h>
  17. #elif defined(MPT_WITH_MINIZ)
  18. #include <miniz/miniz.h>
  19. #endif
  20. OPENMPT_NAMESPACE_BEGIN
  21. #if defined(MPT_WITH_ZLIB) && defined(MPT_WITH_MINIZIP)
  22. // Low-level file abstractions for in-memory file handling
  23. struct ZipFileAbstraction
  24. {
  25. static voidpf ZCALLBACK fopen64_mem(voidpf opaque, const void *, int mode)
  26. {
  27. FileReader &file = *static_cast<FileReader *>(opaque);
  28. if((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_WRITE)
  29. {
  30. return nullptr;
  31. } else
  32. {
  33. file.Rewind();
  34. return opaque;
  35. }
  36. }
  37. static uLong ZCALLBACK fread_mem(voidpf opaque, voidpf, void *buf, uLong size)
  38. {
  39. FileReader &file = *static_cast<FileReader *>(opaque);
  40. return mpt::saturate_cast<uLong>(file.ReadRaw(mpt::span(mpt::void_cast<std::byte*>(buf), size)).size());
  41. }
  42. static uLong ZCALLBACK fwrite_mem(voidpf, voidpf, const void *, uLong)
  43. {
  44. return 0;
  45. }
  46. static ZPOS64_T ZCALLBACK ftell64_mem(voidpf opaque, voidpf)
  47. {
  48. FileReader &file = *static_cast<FileReader *>(opaque);
  49. return file.GetPosition();
  50. }
  51. static long ZCALLBACK fseek64_mem(voidpf opaque, voidpf, ZPOS64_T offset, int origin)
  52. {
  53. FileReader &file = *static_cast<FileReader *>(opaque);
  54. ZPOS64_T destination = 0;
  55. switch(origin)
  56. {
  57. case ZLIB_FILEFUNC_SEEK_CUR:
  58. destination = static_cast<ZPOS64_T>(file.GetPosition()) + offset;
  59. break;
  60. case ZLIB_FILEFUNC_SEEK_END:
  61. destination = static_cast<ZPOS64_T>(file.GetLength()) + offset;
  62. break;
  63. case ZLIB_FILEFUNC_SEEK_SET:
  64. destination = offset;
  65. break;
  66. default:
  67. return -1;
  68. }
  69. if(!mpt::in_range<FileReader::off_t>(destination))
  70. {
  71. return 1;
  72. }
  73. return (file.Seek(static_cast<FileReader::off_t>(destination)) ? 0 : 1);
  74. }
  75. static int ZCALLBACK fclose_mem(voidpf, voidpf)
  76. {
  77. return 0;
  78. }
  79. static int ZCALLBACK ferror_mem(voidpf, voidpf)
  80. {
  81. return 0;
  82. }
  83. };
  84. CZipArchive::CZipArchive(FileReader &file)
  85. : ArchiveBase(file)
  86. , zipFile(nullptr)
  87. {
  88. zlib_filefunc64_def functions =
  89. {
  90. ZipFileAbstraction::fopen64_mem,
  91. ZipFileAbstraction::fread_mem,
  92. ZipFileAbstraction::fwrite_mem,
  93. ZipFileAbstraction::ftell64_mem,
  94. ZipFileAbstraction::fseek64_mem,
  95. ZipFileAbstraction::fclose_mem,
  96. ZipFileAbstraction::ferror_mem,
  97. &inFile
  98. };
  99. zipFile = unzOpen2_64(nullptr, &functions);
  100. if(zipFile == nullptr)
  101. {
  102. return;
  103. }
  104. // read comment
  105. {
  106. unz_global_info info;
  107. if(unzGetGlobalInfo(zipFile, &info) == UNZ_OK)
  108. {
  109. if(info.size_comment > 0)
  110. {
  111. if(info.size_comment < Util::MaxValueOfType(info.size_comment))
  112. {
  113. info.size_comment++;
  114. }
  115. std::vector<char> commentData(info.size_comment);
  116. if(unzGetGlobalComment(zipFile, commentData.data(), info.size_comment) >= 0)
  117. {
  118. commentData[info.size_comment - 1] = '\0';
  119. comment = mpt::ToUnicode(mpt::IsUTF8(commentData.data()) ? mpt::Charset::UTF8 : mpt::Charset::CP437, commentData.data());
  120. }
  121. }
  122. }
  123. }
  124. // read contents
  125. unz_file_pos curFile;
  126. int status = unzGoToFirstFile(zipFile);
  127. unzGetFilePos(zipFile, &curFile);
  128. while(status == UNZ_OK)
  129. {
  130. ArchiveFileInfo fileinfo;
  131. fileinfo.type = ArchiveFileType::Normal;
  132. unz_file_info info;
  133. char name[256];
  134. unzGetCurrentFileInfo(zipFile, &info, name, sizeof(name), nullptr, 0, nullptr, 0);
  135. fileinfo.name = mpt::PathString::FromUnicode(mpt::ToUnicode((info.flag & (1<<11)) ? mpt::Charset::UTF8 : mpt::Charset::CP437, std::string(name)));
  136. fileinfo.size = info.uncompressed_size;
  137. unzGetFilePos(zipFile, &curFile);
  138. fileinfo.cookie1 = curFile.pos_in_zip_directory;
  139. fileinfo.cookie2 = curFile.num_of_file;
  140. contents.push_back(fileinfo);
  141. status = unzGoToNextFile(zipFile);
  142. }
  143. }
  144. CZipArchive::~CZipArchive()
  145. {
  146. unzClose(zipFile);
  147. }
  148. bool CZipArchive::ExtractFile(std::size_t index)
  149. {
  150. if(index >= contents.size())
  151. {
  152. return false;
  153. }
  154. data.clear();
  155. unz_file_pos bestFile;
  156. unz_file_info info;
  157. bestFile.pos_in_zip_directory = static_cast<uLong>(contents[index].cookie1);
  158. bestFile.num_of_file = static_cast<uLong>(contents[index].cookie2);
  159. if(unzGoToFilePos(zipFile, &bestFile) == UNZ_OK && unzOpenCurrentFile(zipFile) == UNZ_OK)
  160. {
  161. unzGetCurrentFileInfo(zipFile, &info, nullptr, 0, nullptr, 0, nullptr, 0);
  162. try
  163. {
  164. data.resize(info.uncompressed_size);
  165. } catch(...)
  166. {
  167. unzCloseCurrentFile(zipFile);
  168. return false;
  169. }
  170. unzReadCurrentFile(zipFile, data.data(), info.uncompressed_size);
  171. unzCloseCurrentFile(zipFile);
  172. return true;
  173. }
  174. return false;
  175. }
  176. #elif defined(MPT_WITH_MINIZ)
  177. CZipArchive::CZipArchive(FileReader &file) : ArchiveBase(file)
  178. {
  179. zipFile = new mz_zip_archive();
  180. mz_zip_archive *zip = static_cast<mz_zip_archive*>(zipFile);
  181. (*zip) = {};
  182. const auto fileData = file.GetRawData();
  183. if(!mz_zip_reader_init_mem(zip, fileData.data(), fileData.size(), 0))
  184. {
  185. delete zip;
  186. zip = nullptr;
  187. zipFile = nullptr;
  188. }
  189. if(!zip)
  190. {
  191. return;
  192. }
  193. for(mz_uint i = 0; i < mz_zip_reader_get_num_files(zip); ++i)
  194. {
  195. ArchiveFileInfo info;
  196. info.type = ArchiveFileType::Invalid;
  197. mz_zip_archive_file_stat stat = {};
  198. if(mz_zip_reader_file_stat(zip, i, &stat))
  199. {
  200. info.type = ArchiveFileType::Normal;
  201. info.name = mpt::PathString::FromUnicode(mpt::ToUnicode((stat.m_bit_flag & (1<<11)) ? mpt::Charset::UTF8 : mpt::Charset::CP437, stat.m_filename));
  202. info.size = stat.m_uncomp_size;
  203. }
  204. if(mz_zip_reader_is_file_a_directory(zip, i))
  205. {
  206. info.type = ArchiveFileType::Special;
  207. } else if(mz_zip_reader_is_file_encrypted(zip, i))
  208. {
  209. info.type = ArchiveFileType::Special;
  210. }
  211. contents.push_back(info);
  212. }
  213. }
  214. CZipArchive::~CZipArchive()
  215. {
  216. mz_zip_archive *zip = static_cast<mz_zip_archive*>(zipFile);
  217. if(zip)
  218. {
  219. mz_zip_reader_end(zip);
  220. delete zip;
  221. zipFile = nullptr;
  222. }
  223. }
  224. bool CZipArchive::ExtractFile(std::size_t index)
  225. {
  226. mz_zip_archive *zip = static_cast<mz_zip_archive*>(zipFile);
  227. if(index >= contents.size())
  228. {
  229. return false;
  230. }
  231. mz_uint bestFile = index;
  232. mz_zip_archive_file_stat stat = {};
  233. mz_zip_reader_file_stat(zip, bestFile, &stat);
  234. if(stat.m_uncomp_size >= std::numeric_limits<std::size_t>::max())
  235. {
  236. return false;
  237. }
  238. try
  239. {
  240. data.resize(static_cast<std::size_t>(stat.m_uncomp_size));
  241. } catch(...)
  242. {
  243. return false;
  244. }
  245. if(!mz_zip_reader_extract_to_mem(zip, bestFile, data.data(), static_cast<std::size_t>(stat.m_uncomp_size), 0))
  246. {
  247. return false;
  248. }
  249. comment = mpt::ToUnicode(mpt::Charset::CP437, std::string(stat.m_comment, stat.m_comment + stat.m_comment_size));
  250. return true;
  251. }
  252. #endif // MPT_WITH_ZLIB || MPT_WITH_MINIZ
  253. OPENMPT_NAMESPACE_END