Support loading images from zip

This commit is contained in:
Ted John 2018-05-12 16:11:51 +01:00
parent 0f0bb021d6
commit 4e86d18dad
4 changed files with 135 additions and 43 deletions

View File

@ -52,7 +52,7 @@ public:
size_t GetNumFiles() const override
{
return zip_get_num_files(_zip);
return zip_get_num_entries(_zip, 0);
}
std::string GetFileName(size_t index) const override
@ -82,11 +82,11 @@ public:
std::vector<uint8> GetFileData(const std::string_view& path) const override
{
std::vector<uint8> result;
auto index = (size_t)zip_name_locate(_zip, path.data(), 0);
auto index = GetIndexFromPath(path);
auto dataSize = GetFileSize(index);
if (dataSize > 0 && dataSize < SIZE_MAX)
{
auto zipFile = zip_fopen(_zip, path.data(), 0);
auto zipFile = zip_fopen_index(_zip, index, 0);
if (zipFile != nullptr)
{
result.resize((size_t)dataSize);
@ -110,7 +110,7 @@ public:
const auto& writeBuffer = *_writeBuffers.rbegin();
auto source = zip_source_buffer(_zip, writeBuffer.data(), writeBuffer.size(), 0);
auto index = zip_name_locate(_zip, path.data(), 0);
auto index = GetIndexFromPath(path);
if (index == -1)
{
zip_add(_zip, path.data(), source);
@ -123,15 +123,55 @@ public:
void DeleteFile(const std::string_view& path) override
{
auto index = zip_name_locate(_zip, path.data(), 0);
auto index = GetIndexFromPath(path);
zip_delete(_zip, index);
}
void RenameFile(const std::string_view& path, const std::string_view& newPath) override
{
auto index = zip_name_locate(_zip, path.data(), 0);
auto index = GetIndexFromPath(path);
zip_file_rename(_zip, index, newPath.data(), ZIP_FL_ENC_GUESS);
}
private:
/**
* Normalises both the given path and the stored paths and finds the first match.
*/
zip_int64_t GetIndexFromPath(const std::string_view& path) const
{
auto normalisedPath = NormalisePath(path);
if (!normalisedPath.empty())
{
auto numFiles = zip_get_num_entries(_zip, 0);
for (zip_int64_t i = 0; i < numFiles; i++)
{
auto normalisedZipPath = NormalisePath(zip_get_name(_zip, i, ZIP_FL_ENC_GUESS));
if (normalisedZipPath == normalisedPath)
{
return i;
}
}
}
return -1;
}
static std::string NormalisePath(const std::string_view& path)
{
std::string result;
if (!path.empty())
{
// Convert back slashes to forward slashes
result = std::move(std::string(path));
for (auto ch = result.data(); *ch != '\0'; ch++)
{
if (*ch == '\\')
{
*ch = '/';
}
}
}
return result;
}
};
namespace Zip

View File

@ -41,10 +41,52 @@
#include "WallObject.h"
#include "WaterObject.h"
interface IFileDataRetriever
{
virtual ~IFileDataRetriever() = default;
virtual std::vector<uint8> GetData(const std::string_view& path) const abstract;
};
class FileSystemDataRetriever : public IFileDataRetriever
{
private:
std::string _basePath;
public:
FileSystemDataRetriever(const std::string_view& basePath)
: _basePath(basePath)
{
}
std::vector<uint8> GetData(const std::string_view& path) const override
{
auto absolutePath = Path::Combine(_basePath, path.data());
return File::ReadAllBytes(absolutePath);
}
};
class ZipDataRetriever : public IFileDataRetriever
{
private:
const IZipArchive& _zipArchive;
public:
ZipDataRetriever(const IZipArchive& zipArchive)
: _zipArchive(zipArchive)
{
}
std::vector<uint8> GetData(const std::string_view& path) const override
{
return _zipArchive.GetFileData(path);
}
};
class ReadObjectContext : public IReadObjectContext
{
private:
IObjectRepository& _objectRepository;
const IFileDataRetriever * _fileDataRetriever;
std::string _objectName;
bool _loadImages;
@ -56,11 +98,15 @@ public:
bool WasWarning() const { return _wasWarning; }
bool WasError() const { return _wasError; }
ReadObjectContext(IObjectRepository& objectRepository, const std::string &objectName, bool loadImages, const std::string& basePath)
ReadObjectContext(
IObjectRepository& objectRepository,
const std::string &objectName,
bool loadImages,
const IFileDataRetriever * fileDataRetriever)
: _objectRepository(objectRepository),
_objectName(objectName),
_loadImages(loadImages),
_basePath(basePath)
_fileDataRetriever(fileDataRetriever)
{
}
@ -76,8 +122,11 @@ public:
std::vector<uint8> GetData(const std::string_view& path) override
{
auto absolutePath = Path::Combine(_basePath, path.data());
return File::ReadAllBytes(absolutePath);
if (_fileDataRetriever != nullptr)
{
return _fileDataRetriever->GetData(path);
}
return {};
}
void LogWarning(uint32 code, const utf8 * text) override
@ -103,7 +152,7 @@ public:
namespace ObjectFactory
{
static Object * CreateObjectFromJson(IObjectRepository& objectRepository, const json_t * jRoot, const std::string_view& basePath);
static Object * CreateObjectFromJson(IObjectRepository& objectRepository, const json_t * jRoot, const IFileDataRetriever * fileRetriever);
static void ReadObjectLegacy(Object * object, IReadObjectContext * context, IStream * stream)
{
@ -143,7 +192,7 @@ namespace ObjectFactory
log_verbose(" size: %zu", chunk->GetLength());
auto chunkStream = MemoryStream(chunk->GetData(), chunk->GetLength());
auto readContext = ReadObjectContext(objectRepository, objectName, !gOpenRCT2Headless, "");
auto readContext = ReadObjectContext(objectRepository, objectName, !gOpenRCT2Headless, nullptr);
ReadObjectLegacy(result, &readContext, &chunkStream);
if (readContext.WasError())
{
@ -169,7 +218,7 @@ namespace ObjectFactory
utf8 objectName[DAT_NAME_LENGTH + 1];
object_entry_get_name_fixed(objectName, sizeof(objectName), entry);
auto readContext = ReadObjectContext(objectRepository, objectName, !gOpenRCT2Headless, "");
auto readContext = ReadObjectContext(objectRepository, objectName, !gOpenRCT2Headless, nullptr);
auto chunkStream = MemoryStream(data, dataSize);
ReadObjectLegacy(result, &readContext, &chunkStream);
@ -241,12 +290,12 @@ namespace ObjectFactory
return 0xFF;
}
Object * CreateObjectFromZipFile(IObjectRepository& objectRepository, const std::string &path)
Object * CreateObjectFromZipFile(IObjectRepository& objectRepository, const std::string_view& path)
{
Object * result = nullptr;
try
{
auto archive = Zip::Open(path.c_str(), ZIP_ACCESS::READ);
auto archive = Zip::Open(path, ZIP_ACCESS::READ);
auto jsonBytes = archive->GetFileData("object.json");
if (jsonBytes.empty())
{
@ -254,17 +303,18 @@ namespace ObjectFactory
}
json_error_t jsonLoadError;
auto jRoot = json_loads((const char *)jsonBytes.data(), 0, &jsonLoadError);
auto jRoot = json_loadb((const char *)jsonBytes.data(), jsonBytes.size(), 0, &jsonLoadError);
if (jRoot == nullptr)
{
throw JsonException(&jsonLoadError);
}
return CreateObjectFromJson(objectRepository, jRoot, "");
auto fileDataRetriever = ZipDataRetriever(*archive);
return CreateObjectFromJson(objectRepository, jRoot, &fileDataRetriever);
}
catch (const std::exception& e)
{
Console::Error::WriteLine("Unable to open or read '%s': %s", path.c_str(), e.what());
Console::Error::WriteLine("Unable to open or read '%s': %s", path.data(), e.what());
delete result;
result = nullptr;
@ -280,7 +330,8 @@ namespace ObjectFactory
try
{
auto jRoot = Json::ReadFromFile(path.c_str());
result = CreateObjectFromJson(objectRepository, jRoot, Path::GetDirectory(path));
auto fileDataRetriever = FileSystemDataRetriever(Path::GetDirectory(path));
result = CreateObjectFromJson(objectRepository, jRoot, &fileDataRetriever);
json_decref(jRoot);
}
catch (const std::runtime_error &err)
@ -293,7 +344,7 @@ namespace ObjectFactory
return result;
}
Object * CreateObjectFromJson(IObjectRepository& objectRepository, const json_t * jRoot, const std::string_view& basePath)
Object * CreateObjectFromJson(IObjectRepository& objectRepository, const json_t * jRoot, const IFileDataRetriever * fileRetriever)
{
log_verbose("CreateObjectFromJson(...)");
@ -319,7 +370,7 @@ namespace ObjectFactory
memcpy(entry.name, originalName.c_str(), minLength);
result = CreateObject(entry);
auto readContext = ReadObjectContext(objectRepository, id, !gOpenRCT2Headless, basePath.data());
auto readContext = ReadObjectContext(objectRepository, id, !gOpenRCT2Headless, fileRetriever);
result->ReadJson(&readContext, jRoot);
if (readContext.WasError())
{

View File

@ -16,6 +16,7 @@
#pragma once
#include <string_view>
#include "../common.h"
interface IObjectRepository;
@ -26,6 +27,7 @@ namespace ObjectFactory
{
Object * CreateObjectFromLegacyFile(IObjectRepository& objectRepository, const utf8 * path);
Object * CreateObjectFromLegacyData(IObjectRepository& objectRepository, const rct_object_entry * entry, const void * data, size_t dataSize);
Object * CreateObjectFromZipFile(IObjectRepository& objectRepository, const std::string_view& path);
Object * CreateObject(const rct_object_entry &entry);
Object * CreateObjectFromJsonFile(IObjectRepository& objectRepository, const std::string &path);

View File

@ -103,34 +103,29 @@ public:
public:
std::tuple<bool, ObjectRepositoryItem> Create(sint32 language, const std::string &path) const override
{
Object * object = nullptr;
auto extension = Path::GetExtension(path);
if (String::Equals(extension, ".json", true))
{
auto object = ObjectFactory::CreateObjectFromJsonFile(_objectRepository, path);
if (object != nullptr)
{
ObjectRepositoryItem item = { 0 };
item.ObjectEntry = *object->GetObjectEntry();
item.Path = String::Duplicate(path);
item.Name = String::Duplicate(object->GetName(language));
object->SetRepositoryItem(&item);
delete object;
return std::make_tuple(true, item);
}
object = ObjectFactory::CreateObjectFromJsonFile(_objectRepository, path);
}
else if (String::Equals(extension, ".parkobj", true))
{
object = ObjectFactory::CreateObjectFromZipFile(_objectRepository, path);
}
else
{
auto object = ObjectFactory::CreateObjectFromLegacyFile(_objectRepository, path.c_str());
if (object != nullptr)
{
ObjectRepositoryItem item = { 0 };
item.ObjectEntry = *object->GetObjectEntry();
item.Path = String::Duplicate(path);
item.Name = String::Duplicate(object->GetName());
object->SetRepositoryItem(&item);
delete object;
return std::make_tuple(true, item);
}
object = ObjectFactory::CreateObjectFromLegacyFile(_objectRepository, path.c_str());
}
if (object != nullptr)
{
ObjectRepositoryItem item = { 0 };
item.ObjectEntry = *object->GetObjectEntry();
item.Path = String::Duplicate(path);
item.Name = String::Duplicate(object->GetName());
object->SetRepositoryItem(&item);
delete object;
return std::make_tuple(true, item);
}
return std::make_tuple(false, ObjectRepositoryItem());
}
@ -285,6 +280,10 @@ public:
{
return ObjectFactory::CreateObjectFromJsonFile(*this, ori->Path);
}
else if (String::Equals(extension, ".parkobj", true))
{
return ObjectFactory::CreateObjectFromZipFile(*this, ori->Path);
}
else
{
return ObjectFactory::CreateObjectFromLegacyFile(*this, ori->Path);