diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index c7752540df..0dd69b6c43 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -82,6 +82,7 @@ add_library(common MemArena.h MemoryUtil.cpp MemoryUtil.h + MinizipUtil.h MsgHandler.cpp MsgHandler.h NandPaths.cpp @@ -134,6 +135,7 @@ PUBLIC enet fmt::fmt ${MBEDTLS_LIBRARIES} + minizip PRIVATE ${CURL_LIBRARIES} diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj index 4fd4119418..f1b56e99f1 100644 --- a/Source/Core/Common/Common.vcxproj +++ b/Source/Core/Common/Common.vcxproj @@ -139,6 +139,7 @@ + diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters index 5849e34f66..6a0261032f 100644 --- a/Source/Core/Common/Common.vcxproj.filters +++ b/Source/Core/Common/Common.vcxproj.filters @@ -59,6 +59,7 @@ + diff --git a/Source/Core/Common/MinizipUtil.h b/Source/Core/Common/MinizipUtil.h new file mode 100644 index 0000000000..8d8bc7d692 --- /dev/null +++ b/Source/Core/Common/MinizipUtil.h @@ -0,0 +1,42 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include + +#include "Common/CommonTypes.h" +#include "Common/ScopeGuard.h" + +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) +{ + const u32 MAX_BUFFER_SIZE = 65535; + + if (unzOpenCurrentFile(file) != UNZ_OK) + return false; + + Common::ScopeGuard guard{[&] { unzCloseCurrentFile(file); }}; + + u32 bytes_to_go = static_cast(destination->size()); + 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) + return false; + + bytes_to_go -= static_cast(bytes_read); + } + + return true; +} +} // namespace Common diff --git a/Source/Core/DiscIO/CMakeLists.txt b/Source/Core/DiscIO/CMakeLists.txt index ca5fdd3ddd..bfeab923fb 100644 --- a/Source/Core/DiscIO/CMakeLists.txt +++ b/Source/Core/DiscIO/CMakeLists.txt @@ -45,6 +45,7 @@ add_library(discio target_link_libraries(discio PRIVATE + minizip pugixml ZLIB::ZLIB ) diff --git a/Source/Core/DiscIO/VolumeVerifier.cpp b/Source/Core/DiscIO/VolumeVerifier.cpp index b0d4e8c3c5..de100bfb76 100644 --- a/Source/Core/DiscIO/VolumeVerifier.cpp +++ b/Source/Core/DiscIO/VolumeVerifier.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -25,7 +26,9 @@ #include "Common/CommonTypes.h" #include "Common/FileUtil.h" #include "Common/Logging/Log.h" +#include "Common/MinizipUtil.h" #include "Common/MsgHandler.h" +#include "Common/ScopeGuard.h" #include "Common/StringUtil.h" #include "Common/Swap.h" #include "Core/IOS/Device.h" @@ -46,9 +49,9 @@ namespace DiscIO void RedumpVerifier::Start(const Volume& volume) { if (volume.GetVolumeType() == Platform::GameCubeDisc) - m_dat_filename = "gamecube.dat"; + m_platform = "gc"; else if (volume.GetVolumeType() == Platform::WiiDisc) - m_dat_filename = "wii.dat"; + m_platform = "wii"; else m_result.status = Status::Error; @@ -62,7 +65,35 @@ void RedumpVerifier::Start(const Volume& volume) m_disc_number = volume.GetDiscNumber().value_or(0); m_size = volume.GetSize(); - m_future = std::async(std::launch::async, [this] { return ScanXML(); }); + m_future = std::async(std::launch::async, [this] { return ScanDatfile(ReadDatfile()); }); +} + +std::vector RedumpVerifier::ReadDatfile() +{ + const std::string path = File::GetUserPath(D_REDUMPCACHE_IDX) + DIR_SEP + m_platform + ".zip"; + + unzFile file = unzOpen(path.c_str()); + if (!file) + return {}; + + Common::ScopeGuard file_guard{[&] { unzClose(file); }}; + + // Check that the zip file contains exactly one file + if (unzGoToFirstFile(file) != UNZ_OK) + return {}; + if (unzGoToNextFile(file) != UNZ_END_OF_LIST_OF_FILE) + return {}; + + // Read the file + if (unzGoToFirstFile(file) != UNZ_OK) + return {}; + unz_file_info file_info; + unzGetCurrentFileInfo(file, &file_info, nullptr, 0, nullptr, 0, nullptr, 0); + std::vector data(file_info.uncompressed_size); + if (!Common::ReadFileFromZip(file, &data)) + return {}; + + return data; } static u8 ParseHexDigit(char c) @@ -93,18 +124,13 @@ static std::vector ParseHash(const char* str) return hash; } -std::vector RedumpVerifier::ScanXML() +std::vector RedumpVerifier::ScanDatfile(const std::vector& data) { - const std::string path = File::GetUserPath(D_REDUMPCACHE_IDX) + DIR_SEP + m_dat_filename; - pugi::xml_document doc; + if (!doc.load_buffer(data.data(), data.size())) { - std::string data; - if (!File::ReadFileToString(path, data) || !doc.load_buffer(data.data(), data.size())) - { - m_result = {Status::Error, Common::GetStringT("Failed to parse Redump.org data")}; - return {}; - } + m_result = {Status::Error, Common::GetStringT("Failed to parse Redump.org data")}; + return {}; } std::vector potential_matches; diff --git a/Source/Core/DiscIO/VolumeVerifier.h b/Source/Core/DiscIO/VolumeVerifier.h index 0b908811f5..d10c5c97ff 100644 --- a/Source/Core/DiscIO/VolumeVerifier.h +++ b/Source/Core/DiscIO/VolumeVerifier.h @@ -71,9 +71,10 @@ private: Hashes> hashes; }; - std::vector ScanXML(); + std::vector ReadDatfile(); + std::vector ScanDatfile(const std::vector& data); - std::string m_dat_filename; + std::string m_platform; std::string m_game_id; u16 m_revision; u8 m_disc_number; diff --git a/Source/Core/UICommon/ResourcePack/ResourcePack.cpp b/Source/Core/UICommon/ResourcePack/ResourcePack.cpp index 24c6f7c76e..b0587f3ca5 100644 --- a/Source/Core/UICommon/ResourcePack/ResourcePack.cpp +++ b/Source/Core/UICommon/ResourcePack/ResourcePack.cpp @@ -10,6 +10,7 @@ #include "Common/FileSearch.h" #include "Common/FileUtil.h" +#include "Common/MinizipUtil.h" #include "Common/ScopeGuard.h" #include "Common/StringUtil.h" @@ -20,35 +21,6 @@ namespace ResourcePack { constexpr char TEXTURE_PATH[] = "Load/Textures/"; -// Since minzip doesn't provide a way to unzip a file of a length > 65535, we have to implement -// this ourselves -template -static bool ReadCurrentFileUnlimited(unzFile file, ContiguousContainer& destination) -{ - const u32 MAX_BUFFER_SIZE = 65535; - - if (unzOpenCurrentFile(file) != UNZ_OK) - return false; - - Common::ScopeGuard guard{[&] { unzCloseCurrentFile(file); }}; - - auto bytes_to_go = static_cast(destination.size()); - 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) - { - return false; - } - - bytes_to_go -= static_cast(bytes_read); - } - - return true; -} - ResourcePack::ResourcePack(const std::string& path) : m_path(path) { auto file = unzOpen(path.c_str()); @@ -72,7 +44,7 @@ ResourcePack::ResourcePack(const std::string& path) : m_path(path) unzGetCurrentFileInfo(file, &manifest_info, nullptr, 0, nullptr, 0, nullptr, 0); std::string manifest_contents(manifest_info.uncompressed_size, '\0'); - if (!ReadCurrentFileUnlimited(file, manifest_contents)) + if (!Common::ReadFileFromZip(file, &manifest_contents)) { m_valid = false; m_error = "Failed to read manifest.json"; @@ -96,7 +68,7 @@ ResourcePack::ResourcePack(const std::string& path) : m_path(path) m_logo_data.resize(logo_info.uncompressed_size); - if (!ReadCurrentFileUnlimited(file, m_logo_data)) + if (!Common::ReadFileFromZip(file, &m_logo_data)) { m_valid = false; m_error = "Failed to read logo.png"; @@ -208,7 +180,7 @@ bool ResourcePack::Install(const std::string& path) unzGetCurrentFileInfo(file, &texture_info, nullptr, 0, nullptr, 0, nullptr, 0); std::vector data(texture_info.uncompressed_size); - if (!ReadCurrentFileUnlimited(file, data)) + if (!Common::ReadFileFromZip(file, &data)) { m_error = "Failed to read texture " + texture; return false;