mirror of https://github.com/OpenRCT2/OpenRCT2.git
Merge pull request #7512 from IntelOrca/feature/parkobj
Introduce parkobj and json object .png support
This commit is contained in:
commit
afdcff8f9a
|
@ -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::string(path);
|
||||
for (auto ch = result.data(); *ch != '\0'; ch++)
|
||||
{
|
||||
if (*ch == '\\')
|
||||
{
|
||||
*ch = '/';
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
namespace Zip
|
||||
|
|
|
@ -23,12 +23,6 @@
|
|||
using namespace OpenRCT2::Drawing;
|
||||
using ImportResult = ImageImporter::ImportResult;
|
||||
|
||||
struct RLECode
|
||||
{
|
||||
uint8 NumPixels{};
|
||||
uint8 OffsetX{};
|
||||
};
|
||||
|
||||
constexpr sint32 PALETTE_TRANSPARENT = -1;
|
||||
|
||||
ImportResult ImageImporter::Import(
|
||||
|
@ -48,18 +42,34 @@ ImportResult ImageImporter::Import(
|
|||
throw std::invalid_argument("Image is not palletted, it has bit depth of " + std::to_string(image.Depth));
|
||||
}
|
||||
|
||||
if (!(flags & IMPORT_FLAGS::RLE))
|
||||
{
|
||||
throw std::invalid_argument("Only RLE image import is currently supported.");
|
||||
}
|
||||
|
||||
const auto width = image.Width;
|
||||
const auto height = image.Height;
|
||||
const auto pixels = image.Pixels.data();
|
||||
|
||||
auto buffer = (uint8 *)std::malloc((height * 2) + (width * height * 16));
|
||||
std::memset(buffer, 0, (height * 2) + (width * height * 16));
|
||||
auto yOffsets = (uint16 *)buffer;
|
||||
auto pixels = GetPixels(image.Pixels.data(), width, height, flags, mode);
|
||||
auto [buffer, bufferLength] = flags & IMPORT_FLAGS::RLE ?
|
||||
EncodeRLE(pixels.data(), width, height) :
|
||||
EncodeRaw(pixels.data(), width, height);
|
||||
|
||||
rct_g1_element outElement;
|
||||
outElement.offset = (uint8 *)buffer;
|
||||
outElement.width = width;
|
||||
outElement.height = height;
|
||||
outElement.flags = (flags & IMPORT_FLAGS::RLE ? G1_FLAG_RLE_COMPRESSION : G1_FLAG_BMP);
|
||||
outElement.x_offset = offsetX;
|
||||
outElement.y_offset = offsetY;
|
||||
outElement.zoomed_offset = 0;
|
||||
|
||||
ImportResult result;
|
||||
result.Element = outElement;
|
||||
result.Buffer = buffer;
|
||||
result.BufferLength = bufferLength;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<sint32> ImageImporter::GetPixels(const uint8 * pixels, uint32 width, uint32 height, IMPORT_FLAGS flags, IMPORT_MODE mode)
|
||||
{
|
||||
std::vector<sint32> buffer;
|
||||
buffer.reserve(width * height);
|
||||
|
||||
// A larger range is needed for proper dithering
|
||||
auto palettedSrc = pixels;
|
||||
|
@ -78,18 +88,8 @@ ImportResult ImageImporter::Import(
|
|||
}
|
||||
}
|
||||
|
||||
auto dst = buffer + (height * 2);
|
||||
for (uint32 y = 0; y < height; y++)
|
||||
{
|
||||
yOffsets[y] = (uint16)(dst - buffer);
|
||||
|
||||
auto previousCode = (RLECode *)nullptr;
|
||||
auto currentCode = (RLECode *)dst;
|
||||
dst += 2;
|
||||
|
||||
auto startX = 0;
|
||||
auto npixels = 0;
|
||||
bool pushRun = false;
|
||||
for (uint32 x = 0; x < width; x++)
|
||||
{
|
||||
sint32 paletteIndex;
|
||||
|
@ -110,13 +110,63 @@ ImportResult ImageImporter::Import(
|
|||
rgbaSrc += 4;
|
||||
palettedSrc += 1;
|
||||
|
||||
buffer.push_back(paletteIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::tuple<void *, size_t> ImageImporter::EncodeRaw(const sint32 * pixels, uint32 width, uint32 height)
|
||||
{
|
||||
auto bufferLength = width * height;
|
||||
auto buffer = (uint8 *)std::malloc(bufferLength);
|
||||
for (size_t i = 0; i < bufferLength; i++)
|
||||
{
|
||||
auto p = pixels[i];
|
||||
buffer[i] = (p == PALETTE_TRANSPARENT ? 0 : (uint8)p);
|
||||
}
|
||||
return std::make_tuple(buffer, bufferLength);
|
||||
}
|
||||
|
||||
std::tuple<void *, size_t> ImageImporter::EncodeRLE(const sint32 * pixels, uint32 width, uint32 height)
|
||||
{
|
||||
struct RLECode
|
||||
{
|
||||
uint8 NumPixels{};
|
||||
uint8 OffsetX{};
|
||||
};
|
||||
|
||||
auto src = pixels;
|
||||
auto buffer = (uint8 *)std::malloc((height * 2) + (width * height * 16));
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
std::memset(buffer, 0, (height * 2) + (width * height * 16));
|
||||
auto yOffsets = (uint16 *)buffer;
|
||||
auto dst = buffer + (height * 2);
|
||||
for (uint32 y = 0; y < height; y++)
|
||||
{
|
||||
yOffsets[y] = (uint16)(dst - buffer);
|
||||
|
||||
auto previousCode = (RLECode *)nullptr;
|
||||
auto currentCode = (RLECode *)dst;
|
||||
dst += 2;
|
||||
|
||||
auto startX = 0;
|
||||
auto npixels = 0;
|
||||
bool pushRun = false;
|
||||
for (uint32 x = 0; x < width; x++)
|
||||
{
|
||||
sint32 paletteIndex = *src++;
|
||||
if (paletteIndex == PALETTE_TRANSPARENT)
|
||||
{
|
||||
if (npixels != 0)
|
||||
{
|
||||
x--;
|
||||
rgbaSrc -= 4;
|
||||
palettedSrc -= 1;
|
||||
src--;
|
||||
pushRun = true;
|
||||
}
|
||||
}
|
||||
|
@ -173,22 +223,12 @@ ImportResult ImageImporter::Import(
|
|||
}
|
||||
|
||||
auto bufferLength = (size_t)(dst - buffer);
|
||||
buffer = (uint8 *)realloc(buffer, bufferLength);
|
||||
|
||||
rct_g1_element outElement;
|
||||
outElement.offset = buffer;
|
||||
outElement.width = width;
|
||||
outElement.height = height;
|
||||
outElement.flags = G1_FLAG_RLE_COMPRESSION;
|
||||
outElement.x_offset = offsetX;
|
||||
outElement.y_offset = offsetY;
|
||||
outElement.zoomed_offset = 0;
|
||||
|
||||
ImportResult result;
|
||||
result.Element = outElement;
|
||||
result.Buffer = buffer;
|
||||
result.BufferLength = bufferLength;
|
||||
return result;
|
||||
buffer = (uint8 * )realloc(buffer, bufferLength);
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
return std::make_tuple(buffer, bufferLength);
|
||||
}
|
||||
|
||||
sint32 ImageImporter::CalculatePaletteIndex(IMPORT_MODE mode, sint16 * rgbaSrc, sint32 x, sint32 y, sint32 width, sint32 height)
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#pragma endregion
|
||||
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include "../core/Imaging.h"
|
||||
#include "Drawing.h"
|
||||
|
||||
|
@ -59,6 +60,10 @@ namespace OpenRCT2::Drawing
|
|||
private:
|
||||
static const PaletteBGRA StandardPalette[256];
|
||||
|
||||
static std::vector<sint32> GetPixels(const uint8 * pixels, uint32 width, uint32 height, IMPORT_FLAGS flags, IMPORT_MODE mode);
|
||||
static std::tuple<void *, size_t> EncodeRaw(const sint32 * pixels, uint32 width, uint32 height);
|
||||
static std::tuple<void *, size_t> EncodeRLE(const sint32 * pixels, uint32 width, uint32 height);
|
||||
|
||||
static sint32 CalculatePaletteIndex(IMPORT_MODE mode, sint16 * rgbaSrc, sint32 x, sint32 y, sint32 width, sint32 height);
|
||||
static sint32 GetPaletteIndex(const PaletteBGRA * palette, sint16 * colour);
|
||||
static bool IsTransparentPixel(const sint16 * colour);
|
||||
|
|
|
@ -736,8 +736,12 @@ void FASTCALL gfx_draw_sprite_raw_masked_software(rct_drawpixelinfo *dpi, sint32
|
|||
return;
|
||||
}
|
||||
|
||||
assert(imgMask->flags & G1_FLAG_BMP);
|
||||
assert(imgColour->flags & G1_FLAG_BMP);
|
||||
// Only BMP format is supported for masking
|
||||
if (!(imgMask->flags & G1_FLAG_BMP) || !(imgColour->flags & G1_FLAG_BMP))
|
||||
{
|
||||
gfx_draw_sprite_software(dpi, colourImage, x, y, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dpi->zoom_level != 0) {
|
||||
// TODO: Implement other zoom levels (probably not used though)
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include "../common.h"
|
||||
#include "../core/Json.hpp"
|
||||
#include "ImageTable.h"
|
||||
|
@ -122,6 +124,7 @@ interface IReadObjectContext
|
|||
|
||||
virtual IObjectRepository& GetObjectRepository() abstract;
|
||||
virtual bool ShouldLoadImages() abstract;
|
||||
virtual std::vector<uint8> GetData(const std::string_view& path) abstract;
|
||||
|
||||
virtual void LogWarning(uint32 code, const utf8 * text) abstract;
|
||||
virtual void LogError(uint32 code, const utf8 * text) abstract;
|
||||
|
|
|
@ -15,11 +15,14 @@
|
|||
#pragma endregion
|
||||
|
||||
#include "../core/Console.hpp"
|
||||
#include "../core/File.h"
|
||||
#include "../core/FileStream.hpp"
|
||||
#include "../core/Json.hpp"
|
||||
#include "../core/Memory.hpp"
|
||||
#include "../core/MemoryStream.h"
|
||||
#include "../core/Path.hpp"
|
||||
#include "../core/String.hpp"
|
||||
#include "../core/Zip.h"
|
||||
#include "../OpenRCT2.h"
|
||||
#include "../rct12/SawyerChunkReader.h"
|
||||
#include "BannerObject.h"
|
||||
|
@ -38,13 +41,56 @@
|
|||
#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;
|
||||
std::string _basePath;
|
||||
bool _wasWarning = false;
|
||||
bool _wasError = false;
|
||||
|
||||
|
@ -52,8 +98,13 @@ public:
|
|||
bool WasWarning() const { return _wasWarning; }
|
||||
bool WasError() const { return _wasError; }
|
||||
|
||||
ReadObjectContext(IObjectRepository& objectRepository, const std::string &objectName, bool loadImages)
|
||||
ReadObjectContext(
|
||||
IObjectRepository& objectRepository,
|
||||
const std::string &objectName,
|
||||
bool loadImages,
|
||||
const IFileDataRetriever * fileDataRetriever)
|
||||
: _objectRepository(objectRepository),
|
||||
_fileDataRetriever(fileDataRetriever),
|
||||
_objectName(objectName),
|
||||
_loadImages(loadImages)
|
||||
{
|
||||
|
@ -69,6 +120,15 @@ public:
|
|||
return _loadImages;
|
||||
}
|
||||
|
||||
std::vector<uint8> GetData(const std::string_view& path) override
|
||||
{
|
||||
if (_fileDataRetriever != nullptr)
|
||||
{
|
||||
return _fileDataRetriever->GetData(path);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void LogWarning(uint32 code, const utf8 * text) override
|
||||
{
|
||||
_wasWarning = true;
|
||||
|
@ -92,6 +152,8 @@ public:
|
|||
|
||||
namespace ObjectFactory
|
||||
{
|
||||
static Object * CreateObjectFromJson(IObjectRepository& objectRepository, const json_t * jRoot, const IFileDataRetriever * fileRetriever);
|
||||
|
||||
static void ReadObjectLegacy(Object * object, IReadObjectContext * context, IStream * stream)
|
||||
{
|
||||
try
|
||||
|
@ -130,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())
|
||||
{
|
||||
|
@ -156,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);
|
||||
|
||||
|
@ -228,6 +290,38 @@ namespace ObjectFactory
|
|||
return 0xFF;
|
||||
}
|
||||
|
||||
Object * CreateObjectFromZipFile(IObjectRepository& objectRepository, const std::string_view& path)
|
||||
{
|
||||
Object * result = nullptr;
|
||||
try
|
||||
{
|
||||
auto archive = Zip::Open(path, ZIP_ACCESS::READ);
|
||||
auto jsonBytes = archive->GetFileData("object.json");
|
||||
if (jsonBytes.empty())
|
||||
{
|
||||
throw std::runtime_error("Unable to open object.json.");
|
||||
}
|
||||
|
||||
json_error_t jsonLoadError;
|
||||
auto jRoot = json_loadb((const char *)jsonBytes.data(), jsonBytes.size(), 0, &jsonLoadError);
|
||||
if (jRoot == nullptr)
|
||||
{
|
||||
throw JsonException(&jsonLoadError);
|
||||
}
|
||||
|
||||
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.data(), e.what());
|
||||
|
||||
delete result;
|
||||
result = nullptr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Object * CreateObjectFromJsonFile(IObjectRepository& objectRepository, const std::string &path)
|
||||
{
|
||||
log_verbose("CreateObjectFromJsonFile(\"%s\")", path.c_str());
|
||||
|
@ -236,35 +330,8 @@ namespace ObjectFactory
|
|||
try
|
||||
{
|
||||
auto jRoot = Json::ReadFromFile(path.c_str());
|
||||
auto jObjectType = json_object_get(jRoot, "objectType");
|
||||
if (json_is_string(jObjectType))
|
||||
{
|
||||
auto objectType = ParseObjectType(json_string_value(jObjectType));
|
||||
if (objectType != 0xFF)
|
||||
{
|
||||
auto id = json_string_value(json_object_get(jRoot, "id"));
|
||||
|
||||
rct_object_entry entry = { 0 };
|
||||
auto originalId = String::ToStd(json_string_value(json_object_get(jRoot, "originalId")));
|
||||
auto originalName = originalId;
|
||||
if (originalId.length() == 8 + 1 + 8 + 1 + 8)
|
||||
{
|
||||
entry.flags = std::stoul(originalId.substr(0, 8), 0, 16);
|
||||
originalName = originalId.substr(9, 8);
|
||||
entry.checksum = std::stoul(originalId.substr(18, 8), 0, 16);
|
||||
}
|
||||
auto minLength = std::min<size_t>(8, originalName.length());
|
||||
memcpy(entry.name, originalName.c_str(), minLength);
|
||||
|
||||
result = CreateObject(entry);
|
||||
auto readContext = ReadObjectContext(objectRepository, id, !gOpenRCT2Headless);
|
||||
result->ReadJson(&readContext, jRoot);
|
||||
if (readContext.WasError())
|
||||
{
|
||||
throw std::runtime_error("Object has errors");
|
||||
}
|
||||
}
|
||||
}
|
||||
auto fileDataRetriever = FileSystemDataRetriever(Path::GetDirectory(path));
|
||||
result = CreateObjectFromJson(objectRepository, jRoot, &fileDataRetriever);
|
||||
json_decref(jRoot);
|
||||
}
|
||||
catch (const std::runtime_error &err)
|
||||
|
@ -276,4 +343,41 @@ namespace ObjectFactory
|
|||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace ObjectFactory
|
||||
|
||||
Object * CreateObjectFromJson(IObjectRepository& objectRepository, const json_t * jRoot, const IFileDataRetriever * fileRetriever)
|
||||
{
|
||||
log_verbose("CreateObjectFromJson(...)");
|
||||
|
||||
Object * result = nullptr;
|
||||
auto jObjectType = json_object_get(jRoot, "objectType");
|
||||
if (json_is_string(jObjectType))
|
||||
{
|
||||
auto objectType = ParseObjectType(json_string_value(jObjectType));
|
||||
if (objectType != 0xFF)
|
||||
{
|
||||
auto id = json_string_value(json_object_get(jRoot, "id"));
|
||||
|
||||
rct_object_entry entry = { 0 };
|
||||
auto originalId = String::ToStd(json_string_value(json_object_get(jRoot, "originalId")));
|
||||
auto originalName = originalId;
|
||||
if (originalId.length() == 8 + 1 + 8 + 1 + 8)
|
||||
{
|
||||
entry.flags = std::stoul(originalId.substr(0, 8), 0, 16);
|
||||
originalName = originalId.substr(9, 8);
|
||||
entry.checksum = std::stoul(originalId.substr(18, 8), 0, 16);
|
||||
}
|
||||
auto minLength = std::min<size_t>(8, originalName.length());
|
||||
memcpy(entry.name, originalName.c_str(), minLength);
|
||||
|
||||
result = CreateObject(entry);
|
||||
auto readContext = ReadObjectContext(objectRepository, id, !gOpenRCT2Headless, fileRetriever);
|
||||
result->ReadJson(&readContext, jRoot);
|
||||
if (readContext.WasError())
|
||||
{
|
||||
throw std::runtime_error("Object has errors");
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "../core/Memory.hpp"
|
||||
#include "../core/Path.hpp"
|
||||
#include "../core/String.hpp"
|
||||
#include "../drawing/ImageImporter.h"
|
||||
#include "../interface/Cursors.h"
|
||||
#include "../localisation/Language.h"
|
||||
#include "../PlatformEnvironment.h"
|
||||
|
@ -35,6 +36,7 @@
|
|||
#include "ObjectJsonHelpers.h"
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Drawing;
|
||||
|
||||
namespace ObjectJsonHelpers
|
||||
{
|
||||
|
@ -316,6 +318,63 @@ namespace ObjectJsonHelpers
|
|||
result = LoadObjectImages(context, name, range);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
auto imageData = context->GetData(s);
|
||||
auto image = Imaging::ReadFromBuffer(imageData, IMAGE_FORMAT::PNG_32);
|
||||
|
||||
ImageImporter importer;
|
||||
auto importResult = importer.Import(image, 0, 0, ImageImporter::IMPORT_FLAGS::RLE);
|
||||
|
||||
result.push_back(importResult.Element);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
auto msg = String::StdFormat("Unable to load image '%s': %s", s.c_str(), e.what());
|
||||
context->LogWarning(OBJECT_ERROR_BAD_IMAGE_TABLE, msg.c_str());
|
||||
|
||||
rct_g1_element emptyg1 = { 0 };
|
||||
result.push_back(emptyg1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::vector<rct_g1_element> ParseImages(IReadObjectContext * context, json_t * el)
|
||||
{
|
||||
auto path = GetString(el, "path");
|
||||
auto x = GetInteger(el, "x");
|
||||
auto y = GetInteger(el, "y");
|
||||
auto raw = (GetString(el, "format") == "raw");
|
||||
|
||||
std::vector<rct_g1_element> result;
|
||||
try
|
||||
{
|
||||
auto flags = ImageImporter::IMPORT_FLAGS::NONE;
|
||||
if (!raw)
|
||||
{
|
||||
flags = (ImageImporter::IMPORT_FLAGS)(flags | ImageImporter::IMPORT_FLAGS::RLE);
|
||||
}
|
||||
auto imageData = context->GetData(path);
|
||||
auto image = Imaging::ReadFromBuffer(imageData, IMAGE_FORMAT::PNG_32);
|
||||
|
||||
ImageImporter importer;
|
||||
auto importResult = importer.Import(image, 0, 0, flags);
|
||||
auto g1Element = importResult.Element;
|
||||
g1Element.x_offset = x;
|
||||
g1Element.y_offset = y;
|
||||
result.push_back(g1Element);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
auto msg = String::StdFormat("Unable to load image '%s': %s", path.c_str(), e.what());
|
||||
context->LogWarning(OBJECT_ERROR_BAD_IMAGE_TABLE, msg.c_str());
|
||||
|
||||
rct_g1_element emptyg1 = { 0 };
|
||||
result.push_back(emptyg1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -359,10 +418,20 @@ namespace ObjectJsonHelpers
|
|||
if (context->ShouldLoadImages())
|
||||
{
|
||||
auto jsonImages = json_object_get(root, "images");
|
||||
auto imageElements = GetJsonStringArray(jsonImages);
|
||||
for (const auto &ie : imageElements)
|
||||
size_t i;
|
||||
json_t * el;
|
||||
json_array_foreach(jsonImages, i, el)
|
||||
{
|
||||
auto images = ParseImages(context, ie);
|
||||
std::vector<rct_g1_element> images;
|
||||
if (json_is_string(el))
|
||||
{
|
||||
auto s = json_string_value(el);
|
||||
images = ParseImages(context, s);
|
||||
}
|
||||
else if (json_is_object(el))
|
||||
{
|
||||
images = ParseImages(context, el);
|
||||
}
|
||||
for (const auto &g1 : images)
|
||||
{
|
||||
imageTable.AddImage(&g1);
|
||||
|
|
|
@ -82,7 +82,7 @@ class ObjectFileIndex final : public FileIndex<ObjectRepositoryItem>
|
|||
private:
|
||||
static constexpr uint32 MAGIC_NUMBER = 0x5844494F; // OIDX
|
||||
static constexpr uint16 VERSION = 17;
|
||||
static constexpr auto PATTERN = "*.dat;*.pob;*.json";
|
||||
static constexpr auto PATTERN = "*.dat;*.pob;*.json;*.parkobj";
|
||||
|
||||
IObjectRepository& _objectRepository;
|
||||
|
||||
|
@ -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