diff --git a/Source/Core/Common/MinizipUtil.h b/Source/Core/Common/MinizipUtil.h index 6057632ddd..29b365c028 100644 --- a/Source/Core/Common/MinizipUtil.h +++ b/Source/Core/Common/MinizipUtil.h @@ -13,29 +13,36 @@ namespace Common { // Reads all of the current file. destination must be big enough to fit the whole file. -template -bool ReadFileFromZip(unzFile file, ContiguousContainer* destination) +inline bool ReadFileFromZip(unzFile file, u8* destination, u64 len) { - const u32 MAX_BUFFER_SIZE = 65535; + const u64 MAX_BUFFER_SIZE = 65535; if (unzOpenCurrentFile(file) != UNZ_OK) return false; Common::ScopeGuard guard{[&] { unzCloseCurrentFile(file); }}; - u32 bytes_to_go = static_cast(destination->size()); + u64 bytes_to_go = len; while (bytes_to_go > 0) { - const int bytes_read = - unzReadCurrentFile(file, &(*destination)[destination->size() - bytes_to_go], - std::min(bytes_to_go, MAX_BUFFER_SIZE)); - - if (bytes_read < 0) + // NOTE: multiples of 4G can't cause read_len == 0 && bytes_to_go > 0, as MAX_BUFFER_SIZE is + // small. + const u32 read_len = static_cast(std::min(bytes_to_go, MAX_BUFFER_SIZE)); + const int rv = unzReadCurrentFile(file, destination, read_len); + if (rv < 0) return false; - bytes_to_go -= static_cast(bytes_read); + const u32 bytes_read = static_cast(rv); + bytes_to_go -= bytes_read; + destination += bytes_read; } - return true; + return unzEndOfFile(file) == 1; +} + +template +bool ReadFileFromZip(unzFile file, ContiguousContainer* destination) +{ + return ReadFileFromZip(file, reinterpret_cast(destination->data()), destination->size()); } } // namespace Common diff --git a/Source/Core/UICommon/ResourcePack/ResourcePack.cpp b/Source/Core/UICommon/ResourcePack/ResourcePack.cpp index 585559029d..355dc8acf2 100644 --- a/Source/Core/UICommon/ResourcePack/ResourcePack.cpp +++ b/Source/Core/UICommon/ResourcePack/ResourcePack.cpp @@ -4,12 +4,15 @@ #include "UICommon/ResourcePack/ResourcePack.h" #include +#include #include +#include #include "Common/CommonPaths.h" #include "Common/FileSearch.h" #include "Common/FileUtil.h" +#include "Common/IOFile.h" #include "Common/MinizipUtil.h" #include "Common/ScopeGuard.h" #include "Common/StringUtil.h" @@ -40,8 +43,8 @@ ResourcePack::ResourcePack(const std::string& path) : m_path(path) return; } - unz_file_info manifest_info; - unzGetCurrentFileInfo(file, &manifest_info, nullptr, 0, nullptr, 0, nullptr, 0); + unz_file_info64 manifest_info{}; + unzGetCurrentFileInfo64(file, &manifest_info, nullptr, 0, nullptr, 0, nullptr, 0); std::string manifest_contents(manifest_info.uncompressed_size, '\0'); if (!Common::ReadFileFromZip(file, &manifest_contents)) @@ -62,9 +65,8 @@ ResourcePack::ResourcePack(const std::string& path) : m_path(path) if (unzLocateFile(file, "logo.png", 0) != UNZ_END_OF_LIST_OF_FILE) { - unz_file_info logo_info; - - unzGetCurrentFileInfo(file, &logo_info, nullptr, 0, nullptr, 0, nullptr, 0); + unz_file_info64 logo_info{}; + unzGetCurrentFileInfo64(file, &logo_info, nullptr, 0, nullptr, 0, nullptr, 0); m_logo_data.resize(logo_info.uncompressed_size); @@ -82,9 +84,9 @@ ResourcePack::ResourcePack(const std::string& path) : m_path(path) { std::string filename(256, '\0'); - unz_file_info texture_info; - unzGetCurrentFileInfo(file, &texture_info, filename.data(), static_cast(filename.size()), - nullptr, 0, nullptr, 0); + unz_file_info64 texture_info{}; + unzGetCurrentFileInfo64(file, &texture_info, filename.data(), static_cast(filename.size()), + nullptr, 0, nullptr, 0); if (filename.compare(0, 9, "textures/") != 0 || texture_info.uncompressed_size == 0) continue; @@ -140,13 +142,44 @@ bool ResourcePack::Install(const std::string& path) } auto file = unzOpen(m_path.c_str()); + if (file == nullptr) + { + m_valid = false; + m_error = "Failed to open resource pack"; + return false; + } Common::ScopeGuard file_guard{[&] { unzClose(file); }}; - for (const auto& texture : m_textures) + if (unzGoToFirstFile(file) != MZ_OK) + return false; + + std::string texture_zip_path; + do { - bool provided_by_other_pack = false; + texture_zip_path.resize(UINT16_MAX + 1, '\0'); + unz_file_info64 texture_info{}; + if (unzGetCurrentFileInfo64(file, &texture_info, texture_zip_path.data(), UINT16_MAX, nullptr, + 0, nullptr, 0) != MZ_OK) + { + return false; + } + TruncateToCString(&texture_zip_path); + + const std::string texture_zip_path_prefix = "textures/"; + if (!texture_zip_path.starts_with(texture_zip_path_prefix)) + continue; + const std::string texture_name = texture_zip_path.substr(texture_zip_path_prefix.size()); + + auto texture_it = std::find_if( + m_textures.cbegin(), m_textures.cend(), [&texture_name](const std::string& texture) { + return mz_path_compare_wc(texture.c_str(), texture_name.c_str(), 1) == MZ_OK; + }); + if (texture_it == m_textures.cend()) + continue; + const auto texture = *texture_it; // Check if a higher priority pack already provides a given texture, don't overwrite it + bool provided_by_other_pack = false; for (const auto& pack : GetHigherPriorityPacks(*this)) { if (std::find(pack->GetTextures().begin(), pack->GetTextures().end(), texture) != @@ -156,47 +189,40 @@ bool ResourcePack::Install(const std::string& path) break; } } - if (provided_by_other_pack) continue; - if (unzLocateFile(file, ("textures/" + texture).c_str(), 0) != UNZ_OK) - { - m_error = "Failed to locate texture " + texture; - return false; - } - const std::string texture_path = path + TEXTURE_PATH + texture; - std::string m_full_dir; - SplitPath(texture_path, &m_full_dir, nullptr, nullptr); + std::string texture_full_dir; + if (!SplitPath(texture_path, &texture_full_dir, nullptr, nullptr)) + continue; - if (!File::CreateFullPath(m_full_dir)) + if (!File::CreateFullPath(texture_full_dir)) { - m_error = "Failed to create full path " + m_full_dir; + m_error = "Failed to create full path " + texture_full_dir; return false; } - unz_file_info texture_info; - unzGetCurrentFileInfo(file, &texture_info, nullptr, 0, nullptr, 0, nullptr, 0); - - std::vector data(texture_info.uncompressed_size); - if (!Common::ReadFileFromZip(file, &data)) + const size_t data_size = static_cast(texture_info.uncompressed_size); + auto data = std::make_unique(data_size); + if (!Common::ReadFileFromZip(file, data.get(), data_size)) { m_error = "Failed to read texture " + texture; return false; } - std::ofstream out(texture_path, std::ios::trunc | std::ios::binary); - - if (!out.good()) + File::IOFile out(texture_path, "wb"); + if (!out) + { + m_error = "Failed to open " + texture; + return false; + } + if (!out.WriteBytes(data.get(), data_size)) { m_error = "Failed to write " + texture; return false; } - - out.write(data.data(), data.size()); - out.flush(); - } + } while (unzGoToNextFile(file) == MZ_OK); SetInstalled(*this, true); return true;