| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 | 
							- /*
 
-  * CZipLoader.cpp, part of VCMI engine
 
-  *
 
-  * Authors: listed in file AUTHORS in main folder
 
-  *
 
-  * License: GNU General Public License v2.0 or later
 
-  * Full text of license available in license.txt file, in main folder
 
-  *
 
-  */
 
- #include "StdInc.h"
 
- #include "CZipLoader.h"
 
- #include "../ScopeGuard.h"
 
- VCMI_LIB_NAMESPACE_BEGIN
 
- CZipStream::CZipStream(const std::shared_ptr<CIOApi> & api, const boost::filesystem::path & archive, unz64_file_pos filepos)
 
- {
 
- 	zlib_filefunc64_def zlibApi;
 
- 	zlibApi = api->getApiStructure();
 
- 	file = unzOpen2_64(archive.c_str(), &zlibApi);
 
- 	unzGoToFilePos64(file, &filepos);
 
- 	unzOpenCurrentFile(file);
 
- }
 
- CZipStream::~CZipStream()
 
- {
 
- 	unzCloseCurrentFile(file);
 
- 	unzClose(file);
 
- }
 
- si64 CZipStream::readMore(ui8 * data, si64 size)
 
- {
 
- 	return unzReadCurrentFile(file, data, static_cast<unsigned int>(size));
 
- }
 
- si64 CZipStream::getSize()
 
- {
 
- 	unz_file_info64 info;
 
- 	unzGetCurrentFileInfo64 (file, &info, nullptr, 0, nullptr, 0, nullptr, 0);
 
- 	return info.uncompressed_size;
 
- }
 
- ui32 CZipStream::calculateCRC32()
 
- {
 
- 	unz_file_info64 info;
 
- 	unzGetCurrentFileInfo64 (file, &info, nullptr, 0, nullptr, 0, nullptr, 0);
 
- 	return info.crc;
 
- }
 
- ///CZipLoader
 
- CZipLoader::CZipLoader(const std::string & mountPoint, const boost::filesystem::path & archive, std::shared_ptr<CIOApi> api):
 
- 	ioApi(std::move(api)),
 
- 	zlibApi(ioApi->getApiStructure()),
 
- 	archiveName(archive),
 
- 	mountPoint(mountPoint),
 
- 	files(listFiles(mountPoint, archive))
 
- {
 
- 	logGlobal->trace("Zip archive loaded, %d files found", files.size());
 
- }
 
- std::unordered_map<ResourcePath, unz64_file_pos> CZipLoader::listFiles(const std::string & mountPoint, const boost::filesystem::path & archive)
 
- {
 
- 	std::unordered_map<ResourcePath, unz64_file_pos> ret;
 
- 	unzFile file = unzOpen2_64(archive.c_str(), &zlibApi);
 
- 	if(file == nullptr)
 
- 		logGlobal->error("%s failed to open", archive.string());
 
- 	if (unzGoToFirstFile(file) == UNZ_OK)
 
- 	{
 
- 		do
 
- 		{
 
- 			unz_file_info64 info;
 
- 			std::vector<char> filename;
 
- 			// Fill unz_file_info structure with current file info
 
- 			unzGetCurrentFileInfo64 (file, &info, nullptr, 0, nullptr, 0, nullptr, 0);
 
- 			filename.resize(info.size_filename);
 
- 			// Get name of current file. Contrary to docs "info" parameter can't be null
 
- 			unzGetCurrentFileInfo64(file, &info, filename.data(), static_cast<uLong>(filename.size()), nullptr, 0, nullptr, 0);
 
- 			std::string filenameString(filename.data(), filename.size());
 
- 			unzGetFilePos64(file, &ret[ResourcePath(mountPoint + filenameString)]);
 
- 		}
 
- 		while (unzGoToNextFile(file) == UNZ_OK);
 
- 	}
 
- 	unzClose(file);
 
- 	return ret;
 
- }
 
- std::unique_ptr<CInputStream> CZipLoader::load(const ResourcePath & resourceName) const
 
- {
 
- 	return std::unique_ptr<CInputStream>(new CZipStream(ioApi, archiveName, files.at(resourceName)));
 
- }
 
- bool CZipLoader::existsResource(const ResourcePath & resourceName) const
 
- {
 
- 	return files.count(resourceName) != 0;
 
- }
 
- std::string CZipLoader::getMountPoint() const
 
- {
 
- 	return mountPoint;
 
- }
 
- std::unordered_set<ResourcePath> CZipLoader::getFilteredFiles(std::function<bool(const ResourcePath &)> filter) const
 
- {
 
- 	std::unordered_set<ResourcePath> foundID;
 
- 	for(const auto & file : files)
 
- 	{
 
- 		if (filter(file.first))
 
- 			foundID.insert(file.first);
 
- 	}
 
- 	return foundID;
 
- }
 
- /// extracts currently selected file from zip into stream "where"
 
- static bool extractCurrent(unzFile file, std::ostream & where)
 
- {
 
- 	std::array<char, 8 * 1024> buffer{};
 
- 	unzOpenCurrentFile(file);
 
- 	while(true)
 
- 	{
 
- 		int readSize = unzReadCurrentFile(file, buffer.data(), static_cast<unsigned int>(buffer.size()));
 
- 		if (readSize < 0) // error
 
- 			break;
 
- 		if (readSize == 0) // end-of-file. Also performs CRC check
 
- 			return unzCloseCurrentFile(file) == UNZ_OK;
 
- 		if (readSize > 0) // successful read
 
- 		{
 
- 			where.write(buffer.data(), readSize);
 
- 			if (!where.good())
 
- 				break;
 
- 		}
 
- 	}
 
- 	// extraction failed. Close file and exit
 
- 	unzCloseCurrentFile(file);
 
- 	return false;
 
- }
 
- std::vector<std::string> ZipArchive::listFiles(const boost::filesystem::path & filename)
 
- {
 
- 	std::vector<std::string> ret;
 
- 	CDefaultIOApi zipAPI;
 
- 	auto zipStructure = zipAPI.getApiStructure();
 
- 	unzFile file = unzOpen2_64(filename.c_str(), &zipStructure);
 
- 	if (file == nullptr)
 
- 	{
 
- 		logGlobal->error("Failed to open file '%s'! Unable to list files!", filename.string());
 
- 		return {};
 
- 	}
 
- 	int result = unzGoToFirstFile(file);
 
- 	if (result == UNZ_OK)
 
- 	{
 
- 		do
 
- 		{
 
- 			unz_file_info64 info;
 
- 			std::vector<char> zipFilename;
 
- 			unzGetCurrentFileInfo64 (file, &info, nullptr, 0, nullptr, 0, nullptr, 0);
 
- 			zipFilename.resize(info.size_filename);
 
- 			// Get name of current file. Contrary to docs "info" parameter can't be null
 
- 			unzGetCurrentFileInfo64(file, &info, zipFilename.data(), static_cast<uLong>(zipFilename.size()), nullptr, 0, nullptr, 0);
 
- 			ret.emplace_back(zipFilename.data(), zipFilename.size());
 
- 			result = unzGoToNextFile(file);
 
- 		}
 
- 		while (result == UNZ_OK);
 
- 		if (result != UNZ_OK && result != UNZ_END_OF_LIST_OF_FILE)
 
- 		{
 
- 			logGlobal->error("Failed to list file from '%s'! Error code %d", filename.string(), result);
 
- 		}
 
- 	}
 
- 	else
 
- 	{
 
- 		logGlobal->error("Failed to list files from '%s'! Error code %d", filename.string(), result);
 
- 	}
 
- 	unzClose(file);
 
- 	return ret;
 
- }
 
- bool ZipArchive::extract(const boost::filesystem::path & from, const boost::filesystem::path & where)
 
- {
 
- 	// Note: may not be fast enough for large archives (should NOT happen with mods)
 
- 	// because locating each file by name may be slow. Unlikely slower than decompression though
 
- 	return extract(from, where, listFiles(from));
 
- }
 
- bool ZipArchive::extract(const boost::filesystem::path & from, const boost::filesystem::path & where, const std::vector<std::string> & what)
 
- {
 
- 	CDefaultIOApi zipAPI;
 
- 	auto zipStructure = zipAPI.getApiStructure();
 
- 	unzFile archive = unzOpen2_64(from.c_str(), &zipStructure);
 
- 	auto onExit = vstd::makeScopeGuard([&]()
 
- 	{
 
- 		unzClose(archive);
 
- 	});
 
- 	for (const std::string & file : what)
 
- 	{
 
- 		if (unzLocateFile(archive, file.c_str(), 1) != UNZ_OK)
 
- 			return false;
 
- 		const boost::filesystem::path fullName = where / file;
 
- 		const boost::filesystem::path fullPath = fullName.parent_path();
 
- 		boost::filesystem::create_directories(fullPath);
 
- 		// directory. No file to extract
 
- 		// TODO: better way to detect directory? Probably check return value of unzOpenCurrentFile?
 
- 		if (boost::algorithm::ends_with(file, "/"))
 
- 			continue;
 
- 		std::fstream destFile(fullName.c_str(), std::ios::out | std::ios::binary);
 
- 		if (!destFile.good())
 
- 			return false;
 
- 		if (!extractCurrent(archive, destFile))
 
- 			return false;
 
- 	}
 
- 	return true;
 
- }
 
- VCMI_LIB_NAMESPACE_END
 
 
  |