ResourcePack: dont scan for individual files
walking the zip prevents minizip from re-reading the same data repeatedly from the actual backing filesystem. also improves most usages of minizip to allow for >4GB, files altho we probably don't need it
This commit is contained in:
parent
7b2b559743
commit
9bb8315441
|
@ -13,29 +13,36 @@
|
||||||
namespace Common
|
namespace Common
|
||||||
{
|
{
|
||||||
// Reads all of the current file. destination must be big enough to fit the whole file.
|
// Reads all of the current file. destination must be big enough to fit the whole file.
|
||||||
template <typename ContiguousContainer>
|
inline bool ReadFileFromZip(unzFile file, u8* destination, u64 len)
|
||||||
bool ReadFileFromZip(unzFile file, ContiguousContainer* destination)
|
|
||||||
{
|
{
|
||||||
const u32 MAX_BUFFER_SIZE = 65535;
|
const u64 MAX_BUFFER_SIZE = 65535;
|
||||||
|
|
||||||
if (unzOpenCurrentFile(file) != UNZ_OK)
|
if (unzOpenCurrentFile(file) != UNZ_OK)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Common::ScopeGuard guard{[&] { unzCloseCurrentFile(file); }};
|
Common::ScopeGuard guard{[&] { unzCloseCurrentFile(file); }};
|
||||||
|
|
||||||
u32 bytes_to_go = static_cast<u32>(destination->size());
|
u64 bytes_to_go = len;
|
||||||
while (bytes_to_go > 0)
|
while (bytes_to_go > 0)
|
||||||
{
|
{
|
||||||
const int bytes_read =
|
// NOTE: multiples of 4G can't cause read_len == 0 && bytes_to_go > 0, as MAX_BUFFER_SIZE is
|
||||||
unzReadCurrentFile(file, &(*destination)[destination->size() - bytes_to_go],
|
// small.
|
||||||
std::min(bytes_to_go, MAX_BUFFER_SIZE));
|
const u32 read_len = static_cast<u32>(std::min(bytes_to_go, MAX_BUFFER_SIZE));
|
||||||
|
const int rv = unzReadCurrentFile(file, destination, read_len);
|
||||||
if (bytes_read < 0)
|
if (rv < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bytes_to_go -= static_cast<u32>(bytes_read);
|
const u32 bytes_read = static_cast<u32>(rv);
|
||||||
|
bytes_to_go -= bytes_read;
|
||||||
|
destination += bytes_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return unzEndOfFile(file) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ContiguousContainer>
|
||||||
|
bool ReadFileFromZip(unzFile file, ContiguousContainer* destination)
|
||||||
|
{
|
||||||
|
return ReadFileFromZip(file, reinterpret_cast<u8*>(destination->data()), destination->size());
|
||||||
}
|
}
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -4,12 +4,15 @@
|
||||||
#include "UICommon/ResourcePack/ResourcePack.h"
|
#include "UICommon/ResourcePack/ResourcePack.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <mz_compat.h>
|
#include <mz_compat.h>
|
||||||
|
#include <mz_os.h>
|
||||||
|
|
||||||
#include "Common/CommonPaths.h"
|
#include "Common/CommonPaths.h"
|
||||||
#include "Common/FileSearch.h"
|
#include "Common/FileSearch.h"
|
||||||
#include "Common/FileUtil.h"
|
#include "Common/FileUtil.h"
|
||||||
|
#include "Common/IOFile.h"
|
||||||
#include "Common/MinizipUtil.h"
|
#include "Common/MinizipUtil.h"
|
||||||
#include "Common/ScopeGuard.h"
|
#include "Common/ScopeGuard.h"
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
|
@ -40,8 +43,8 @@ ResourcePack::ResourcePack(const std::string& path) : m_path(path)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
unz_file_info manifest_info;
|
unz_file_info64 manifest_info{};
|
||||||
unzGetCurrentFileInfo(file, &manifest_info, nullptr, 0, nullptr, 0, nullptr, 0);
|
unzGetCurrentFileInfo64(file, &manifest_info, nullptr, 0, nullptr, 0, nullptr, 0);
|
||||||
|
|
||||||
std::string manifest_contents(manifest_info.uncompressed_size, '\0');
|
std::string manifest_contents(manifest_info.uncompressed_size, '\0');
|
||||||
if (!Common::ReadFileFromZip(file, &manifest_contents))
|
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)
|
if (unzLocateFile(file, "logo.png", 0) != UNZ_END_OF_LIST_OF_FILE)
|
||||||
{
|
{
|
||||||
unz_file_info logo_info;
|
unz_file_info64 logo_info{};
|
||||||
|
unzGetCurrentFileInfo64(file, &logo_info, nullptr, 0, nullptr, 0, nullptr, 0);
|
||||||
unzGetCurrentFileInfo(file, &logo_info, nullptr, 0, nullptr, 0, nullptr, 0);
|
|
||||||
|
|
||||||
m_logo_data.resize(logo_info.uncompressed_size);
|
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');
|
std::string filename(256, '\0');
|
||||||
|
|
||||||
unz_file_info texture_info;
|
unz_file_info64 texture_info{};
|
||||||
unzGetCurrentFileInfo(file, &texture_info, filename.data(), static_cast<u16>(filename.size()),
|
unzGetCurrentFileInfo64(file, &texture_info, filename.data(), static_cast<u16>(filename.size()),
|
||||||
nullptr, 0, nullptr, 0);
|
nullptr, 0, nullptr, 0);
|
||||||
|
|
||||||
if (filename.compare(0, 9, "textures/") != 0 || texture_info.uncompressed_size == 0)
|
if (filename.compare(0, 9, "textures/") != 0 || texture_info.uncompressed_size == 0)
|
||||||
continue;
|
continue;
|
||||||
|
@ -140,13 +142,44 @@ bool ResourcePack::Install(const std::string& path)
|
||||||
}
|
}
|
||||||
|
|
||||||
auto file = unzOpen(m_path.c_str());
|
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); }};
|
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
|
// 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))
|
for (const auto& pack : GetHigherPriorityPacks(*this))
|
||||||
{
|
{
|
||||||
if (std::find(pack->GetTextures().begin(), pack->GetTextures().end(), texture) !=
|
if (std::find(pack->GetTextures().begin(), pack->GetTextures().end(), texture) !=
|
||||||
|
@ -156,47 +189,40 @@ bool ResourcePack::Install(const std::string& path)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (provided_by_other_pack)
|
if (provided_by_other_pack)
|
||||||
continue;
|
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;
|
const std::string texture_path = path + TEXTURE_PATH + texture;
|
||||||
std::string m_full_dir;
|
std::string texture_full_dir;
|
||||||
SplitPath(texture_path, &m_full_dir, nullptr, nullptr);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
unz_file_info texture_info;
|
const size_t data_size = static_cast<size_t>(texture_info.uncompressed_size);
|
||||||
unzGetCurrentFileInfo(file, &texture_info, nullptr, 0, nullptr, 0, nullptr, 0);
|
auto data = std::make_unique<u8[]>(data_size);
|
||||||
|
if (!Common::ReadFileFromZip(file, data.get(), data_size))
|
||||||
std::vector<char> data(texture_info.uncompressed_size);
|
|
||||||
if (!Common::ReadFileFromZip(file, &data))
|
|
||||||
{
|
{
|
||||||
m_error = "Failed to read texture " + texture;
|
m_error = "Failed to read texture " + texture;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ofstream out(texture_path, std::ios::trunc | std::ios::binary);
|
File::IOFile out(texture_path, "wb");
|
||||||
|
if (!out)
|
||||||
if (!out.good())
|
{
|
||||||
|
m_error = "Failed to open " + texture;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!out.WriteBytes(data.get(), data_size))
|
||||||
{
|
{
|
||||||
m_error = "Failed to write " + texture;
|
m_error = "Failed to write " + texture;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} while (unzGoToNextFile(file) == MZ_OK);
|
||||||
out.write(data.data(), data.size());
|
|
||||||
out.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
SetInstalled(*this, true);
|
SetInstalled(*this, true);
|
||||||
return true;
|
return true;
|
||||||
|
|
Loading…
Reference in New Issue