mirror of https://github.com/OpenRCT2/OpenRCT2.git
Support loading images from zip
This commit is contained in:
parent
0f0bb021d6
commit
4e86d18dad
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue