CZipLoader.cpp 5.0 KB

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