2
0

CZipLoader.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. #include "StdInc.h"
  2. #include "CZipLoader.h"
  3. #include "../ScopeGuard.h"
  4. /*
  5. * CZipLoader.cpp, part of VCMI engine
  6. *
  7. * Authors: listed in file AUTHORS in main folder
  8. *
  9. * License: GNU General Public License v2.0 or later
  10. * Full text of license available in license.txt file, in main folder
  11. *
  12. */
  13. ///CZipStream
  14. CZipStream::CZipStream(std::shared_ptr<CIOApi> api, const std::string & archive, unz_file_pos filepos)
  15. {
  16. zlib_filefunc64_def zlibApi;
  17. zlibApi = api->getApiStructure();
  18. file = unzOpen2_64(archive.c_str(), &zlibApi);
  19. unzGoToFilePos(file, &filepos);
  20. unzOpenCurrentFile(file);
  21. }
  22. CZipStream::~CZipStream()
  23. {
  24. unzCloseCurrentFile(file);
  25. unzClose(file);
  26. }
  27. si64 CZipStream::readMore(ui8 * data, si64 size)
  28. {
  29. return unzReadCurrentFile(file, data, size);
  30. }
  31. si64 CZipStream::getSize()
  32. {
  33. unz_file_info info;
  34. unzGetCurrentFileInfo (file, &info, nullptr, 0, nullptr, 0, nullptr, 0);
  35. return info.uncompressed_size;
  36. }
  37. ui32 CZipStream::calculateCRC32()
  38. {
  39. unz_file_info info;
  40. unzGetCurrentFileInfo (file, &info, nullptr, 0, nullptr, 0, nullptr, 0);
  41. return info.crc;
  42. }
  43. ///CZipLoader
  44. CZipLoader::CZipLoader(const std::string & mountPoint, const std::string & archive, std::shared_ptr<CIOApi> api):
  45. ioApi(api),
  46. zlibApi(ioApi->getApiStructure()),
  47. archiveName(archive),
  48. mountPoint(mountPoint),
  49. files(listFiles(mountPoint, archive))
  50. {
  51. logGlobal->traceStream() << "Zip archive loaded, " << files.size() << " files found";
  52. }
  53. std::unordered_map<ResourceID, unz_file_pos> CZipLoader::listFiles(const std::string & mountPoint, const std::string & archive)
  54. {
  55. std::unordered_map<ResourceID, unz_file_pos> ret;
  56. unzFile file = unzOpen2_64(archive.c_str(), &zlibApi);
  57. if (unzGoToFirstFile(file) == UNZ_OK)
  58. {
  59. do
  60. {
  61. unz_file_info info;
  62. std::vector<char> filename;
  63. // Fill unz_file_info structure with current file info
  64. unzGetCurrentFileInfo (file, &info, nullptr, 0, nullptr, 0, nullptr, 0);
  65. filename.resize(info.size_filename);
  66. // Get name of current file. Contrary to docs "info" parameter can't be null
  67. unzGetCurrentFileInfo (file, &info, filename.data(), filename.size(), nullptr, 0, nullptr, 0);
  68. std::string filenameString(filename.data(), filename.size());
  69. unzGetFilePos(file, &ret[ResourceID(mountPoint + filenameString)]);
  70. }
  71. while (unzGoToNextFile(file) == UNZ_OK);
  72. }
  73. unzClose(file);
  74. return ret;
  75. }
  76. std::unique_ptr<CInputStream> CZipLoader::load(const ResourceID & resourceName) const
  77. {
  78. return std::unique_ptr<CInputStream>(new CZipStream(ioApi, archiveName, files.at(resourceName)));
  79. }
  80. bool CZipLoader::existsResource(const ResourceID & resourceName) const
  81. {
  82. return files.count(resourceName) != 0;
  83. }
  84. std::string CZipLoader::getMountPoint() const
  85. {
  86. return mountPoint;
  87. }
  88. std::unordered_set<ResourceID> CZipLoader::getFilteredFiles(std::function<bool(const ResourceID &)> filter) const
  89. {
  90. std::unordered_set<ResourceID> foundID;
  91. for (auto & file : files)
  92. {
  93. if (filter(file.first))
  94. foundID.insert(file.first);
  95. }
  96. return foundID;
  97. }
  98. /// extracts currently selected file from zip into stream "where"
  99. static bool extractCurrent(unzFile file, std::ostream & where)
  100. {
  101. std::array<char, 8 * 1024> buffer;
  102. unzOpenCurrentFile(file);
  103. while (1)
  104. {
  105. int readSize = unzReadCurrentFile(file, buffer.data(), buffer.size());
  106. if (readSize < 0) // error
  107. break;
  108. if (readSize == 0) // end-of-file. Also performs CRC check
  109. return unzCloseCurrentFile(file) == UNZ_OK;
  110. if (readSize > 0) // successful read
  111. {
  112. where.write(buffer.data(), readSize);
  113. if (!where.good())
  114. break;
  115. }
  116. }
  117. // extraction failed. Close file and exit
  118. unzCloseCurrentFile(file);
  119. return false;
  120. }
  121. std::vector<std::string> ZipArchive::listFiles(std::string filename)
  122. {
  123. std::vector<std::string> ret;
  124. unzFile file = unzOpen(filename.c_str());
  125. if (unzGoToFirstFile(file) == UNZ_OK)
  126. {
  127. do
  128. {
  129. unz_file_info info;
  130. std::vector<char> filename;
  131. unzGetCurrentFileInfo (file, &info, nullptr, 0, nullptr, 0, nullptr, 0);
  132. filename.resize(info.size_filename);
  133. // Get name of current file. Contrary to docs "info" parameter can't be null
  134. unzGetCurrentFileInfo (file, &info, filename.data(), filename.size(), nullptr, 0, nullptr, 0);
  135. ret.push_back(std::string(filename.data(), filename.size()));
  136. }
  137. while (unzGoToNextFile(file) == UNZ_OK);
  138. }
  139. unzClose(file);
  140. return ret;
  141. }
  142. bool ZipArchive::extract(std::string from, std::string where)
  143. {
  144. // Note: may not be fast enough for large archives (should NOT happen with mods)
  145. // because locating each file by name may be slow. Unlikely slower than decompression though
  146. return extract(from, where, listFiles(from));
  147. }
  148. bool ZipArchive::extract(std::string from, std::string where, std::vector<std::string> what)
  149. {
  150. unzFile archive = unzOpen(from.c_str());
  151. auto onExit = vstd::makeScopeGuard([&]()
  152. {
  153. unzClose(archive);
  154. });
  155. for (std::string & file : what)
  156. {
  157. if (unzLocateFile(archive, file.c_str(), 1) != UNZ_OK)
  158. return false;
  159. std::string fullName = where + '/' + file;
  160. std::string fullPath = fullName.substr(0, fullName.find_last_of("/"));
  161. boost::filesystem::create_directories(fullPath);
  162. // directory. No file to extract
  163. // TODO: better way to detect directory? Probably check return value of unzOpenCurrentFile?
  164. if (boost::algorithm::ends_with(file, "/"))
  165. continue;
  166. std::ofstream destFile(fullName, std::ofstream::binary);
  167. if (!destFile.good())
  168. return false;
  169. if (!extractCurrent(archive, destFile))
  170. return false;
  171. }
  172. return true;
  173. }