From a3700c832af1a860d3b787d0585237df71ec63e7 Mon Sep 17 00:00:00 2001 From: spacek531 Date: Sat, 19 Mar 2022 11:02:02 -0700 Subject: [PATCH] Parkobj loads rct_gx (#16806) * allow loading of lgx rct_gx packed images within parkobjs * add changelog entry * allow loading of lgx without range --- distribution/changelog.txt | 1 + src/openrct2/drawing/Drawing.Sprite.cpp | 26 +++++++++ src/openrct2/drawing/Drawing.h | 4 +- src/openrct2/object/ImageTable.cpp | 71 +++++++++++++++++++++++++ src/openrct2/object/ImageTable.h | 2 + 5 files changed, 103 insertions(+), 1 deletion(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index d68628d6f9..762c819353 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -16,6 +16,7 @@ - Feature: [#16132, #16389] The Corkscrew, Twister and Vertical Drop Roller Coasters can now draw inline twists. - Feature: [#16144] [Plugin] Add ImageManager to API. - Feature: [#16731] [Plugin] New API for fetching and manipulating a staff member's patrol area. +- Feature: [#16806] Parkobj can load sprites from RCT image archives. - Improved: [#3517] Cheats are now saved with the park. - Improved: [#10150] Ride stations are now properly checked if they’re sheltered. - Improved: [#10664, #16072] Visibility status can be modified directly in the Tile Inspector's list. diff --git a/src/openrct2/drawing/Drawing.Sprite.cpp b/src/openrct2/drawing/Drawing.Sprite.cpp index 2aa8091295..bc9fd6a29f 100644 --- a/src/openrct2/drawing/Drawing.Sprite.cpp +++ b/src/openrct2/drawing/Drawing.Sprite.cpp @@ -14,6 +14,7 @@ #include "../PlatformEnvironment.h" #include "../config/Config.h" #include "../core/FileStream.h" +#include "../core/MemoryStream.h" #include "../core/Path.hpp" #include "../platform/Platform.h" #include "../sprites.h" @@ -361,6 +362,31 @@ bool gfx_load_csg() } } +std::optional GfxLoadGx(const std::vector& buffer) +{ + try + { + OpenRCT2::MemoryStream istream(buffer.data(), buffer.size()); + rct_gx gx; + + gx.header = istream.ReadValue(); + + // Read element headers + gx.elements.resize(gx.header.num_entries); + read_and_convert_gxdat(&istream, gx.header.num_entries, false, gx.elements.data()); + + // Read element data + gx.data = istream.ReadArray(gx.header.total_size); + + return std::make_optional(std::move(gx)); + } + catch (const std::exception&) + { + log_verbose("Unable to load rct_gx graphics"); + } + return std::nullopt; +} + static std::optional FASTCALL gfx_draw_sprite_get_palette(ImageId imageId) { if (!imageId.HasSecondary()) diff --git a/src/openrct2/drawing/Drawing.h b/src/openrct2/drawing/Drawing.h index 69abfb9052..cd85e9c197 100644 --- a/src/openrct2/drawing/Drawing.h +++ b/src/openrct2/drawing/Drawing.h @@ -28,7 +28,8 @@ struct ScreenRect; namespace OpenRCT2 { struct IPlatformEnvironment; -} + struct IStream; +} // namespace OpenRCT2 namespace OpenRCT2::Drawing { @@ -525,6 +526,7 @@ void gfx_unload_csg(); const rct_g1_element* gfx_get_g1_element(ImageId imageId); const rct_g1_element* gfx_get_g1_element(ImageIndex image_id); void gfx_set_g1_element(ImageIndex imageId, const rct_g1_element* g1); +std::optional GfxLoadGx(const std::vector& buffer); bool is_csg_loaded(); void FASTCALL gfx_sprite_to_buffer(rct_drawpixelinfo& dpi, const DrawSpriteArgs& args); void FASTCALL gfx_bmp_sprite_to_buffer(rct_drawpixelinfo& dpi, const DrawSpriteArgs& args); diff --git a/src/openrct2/object/ImageTable.cpp b/src/openrct2/object/ImageTable.cpp index db78511872..98602dd3dd 100644 --- a/src/openrct2/object/ImageTable.cpp +++ b/src/openrct2/object/ImageTable.cpp @@ -137,6 +137,22 @@ std::vector> ImageTable::ParseImages( result = LoadObjectImages(context, name, range); } } + else if (String::StartsWith(s, "$LGX:")) + { + auto name = s.substr(5); + auto rangeStart = name.find('['); + if (rangeStart != std::string::npos) + { + auto rangeString = name.substr(rangeStart); + auto range = ParseRange(name.substr(rangeStart)); + name = name.substr(0, rangeStart); + result = LoadImageArchiveImages(context, name, range); + } + else + { + result = LoadImageArchiveImages(context, name); + } + } else { try @@ -219,6 +235,61 @@ std::vector> ImageTable::ParseImages( return result; } +std::vector> ImageTable::LoadImageArchiveImages( + IReadObjectContext* context, const std::string& path, const std::vector& range) +{ + std::vector> result; + auto gxRaw = context->GetData(path); + std::optional gxData = GfxLoadGx(gxRaw); + if (gxData.has_value()) + { + // Fix entry data offsets + for (uint32_t i = 0; i < gxData->header.num_entries; i++) + { + gxData->elements[i].offset += reinterpret_cast(gxData->data.get()); + } + + if (range.size() > 0) + { + size_t placeHoldersAdded = 0; + for (auto i : range) + { + if (i >= 0 && (i < static_cast(gxData->header.num_entries))) + { + result.push_back(std::make_unique(gxData->elements[i])); + } + else + { + result.push_back(std::make_unique()); + placeHoldersAdded++; + } + } + + // Log place holder information + if (placeHoldersAdded > 0) + { + std::string msg = "Adding " + std::to_string(placeHoldersAdded) + " placeholders"; + context->LogWarning(ObjectError::InvalidProperty, msg.c_str()); + } + } + else + { + for (int i = 0; i < static_cast(gxData->header.num_entries); i++) + result.push_back(std::make_unique(gxData->elements[i])); + } + } + else + { + auto msg = String::StdFormat("Unable to load rct_gx '%s'", path.c_str()); + context->LogWarning(ObjectError::BadImageTable, msg.c_str()); + for (size_t i = 0; i < range.size(); i++) + { + result.push_back(std::make_unique()); + } + } + return result; +} + std::vector> ImageTable::LoadObjectImages( IReadObjectContext* context, const std::string& name, const std::vector& range) { diff --git a/src/openrct2/object/ImageTable.h b/src/openrct2/object/ImageTable.h index 5164016b54..65db55981f 100644 --- a/src/openrct2/object/ImageTable.h +++ b/src/openrct2/object/ImageTable.h @@ -45,6 +45,8 @@ private: IReadObjectContext* context, const std::string& name, const std::vector& range); [[nodiscard]] static std::vector ParseRange(std::string s); [[nodiscard]] static std::string FindLegacyObject(const std::string& name); + [[nodiscard]] static std::vector> LoadImageArchiveImages( + IReadObjectContext* context, const std::string& path, const std::vector& range = {}); public: ImageTable() = default;