diff --git a/src/openrct2/core/Zip.cpp b/src/openrct2/core/Zip.cpp index 73dad5268a..737373c4e4 100644 --- a/src/openrct2/core/Zip.cpp +++ b/src/openrct2/core/Zip.cpp @@ -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 GetFileData(const std::string_view& path) const override { std::vector 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 diff --git a/src/openrct2/drawing/ImageImporter.cpp b/src/openrct2/drawing/ImageImporter.cpp index 07f5747a0c..7c51543ffb 100644 --- a/src/openrct2/drawing/ImageImporter.cpp +++ b/src/openrct2/drawing/ImageImporter.cpp @@ -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 ImageImporter::GetPixels(const uint8 * pixels, uint32 width, uint32 height, IMPORT_FLAGS flags, IMPORT_MODE mode) +{ + std::vector 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 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 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) diff --git a/src/openrct2/drawing/ImageImporter.h b/src/openrct2/drawing/ImageImporter.h index 6d4ba9d50b..4d579bf165 100644 --- a/src/openrct2/drawing/ImageImporter.h +++ b/src/openrct2/drawing/ImageImporter.h @@ -15,6 +15,7 @@ #pragma endregion #include +#include #include "../core/Imaging.h" #include "Drawing.h" @@ -59,6 +60,10 @@ namespace OpenRCT2::Drawing private: static const PaletteBGRA StandardPalette[256]; + static std::vector GetPixels(const uint8 * pixels, uint32 width, uint32 height, IMPORT_FLAGS flags, IMPORT_MODE mode); + static std::tuple EncodeRaw(const sint32 * pixels, uint32 width, uint32 height); + static std::tuple 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); diff --git a/src/openrct2/drawing/Sprite.cpp b/src/openrct2/drawing/Sprite.cpp index 2d800ce902..6dfd43ccf3 100644 --- a/src/openrct2/drawing/Sprite.cpp +++ b/src/openrct2/drawing/Sprite.cpp @@ -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) diff --git a/src/openrct2/object/Object.h b/src/openrct2/object/Object.h index 69b279660d..016e8dbec8 100644 --- a/src/openrct2/object/Object.h +++ b/src/openrct2/object/Object.h @@ -16,6 +16,8 @@ #pragma once +#include +#include #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 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; diff --git a/src/openrct2/object/ObjectFactory.cpp b/src/openrct2/object/ObjectFactory.cpp index 7d99386e68..e8a01a99e9 100644 --- a/src/openrct2/object/ObjectFactory.cpp +++ b/src/openrct2/object/ObjectFactory.cpp @@ -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 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 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 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 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(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(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; + } +} diff --git a/src/openrct2/object/ObjectFactory.h b/src/openrct2/object/ObjectFactory.h index 4a05b87bef..85b1009c61 100644 --- a/src/openrct2/object/ObjectFactory.h +++ b/src/openrct2/object/ObjectFactory.h @@ -16,6 +16,7 @@ #pragma once +#include #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); diff --git a/src/openrct2/object/ObjectJsonHelpers.cpp b/src/openrct2/object/ObjectJsonHelpers.cpp index 6ff28aefa1..7a63121f1c 100644 --- a/src/openrct2/object/ObjectJsonHelpers.cpp +++ b/src/openrct2/object/ObjectJsonHelpers.cpp @@ -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 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 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 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); diff --git a/src/openrct2/object/ObjectRepository.cpp b/src/openrct2/object/ObjectRepository.cpp index a5af9b9e2e..16dc9a9851 100644 --- a/src/openrct2/object/ObjectRepository.cpp +++ b/src/openrct2/object/ObjectRepository.cpp @@ -82,7 +82,7 @@ class ObjectFileIndex final : public FileIndex 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 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);