Parkobj loads rct_gx (#16806)

* allow loading of lgx rct_gx packed images within parkobjs

* add changelog entry

* allow loading of lgx without range
This commit is contained in:
spacek531 2022-03-19 11:02:02 -07:00 committed by GitHub
parent c84da5512c
commit a3700c832a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 103 additions and 1 deletions

View File

@ -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 theyre sheltered.
- Improved: [#10664, #16072] Visibility status can be modified directly in the Tile Inspector's list.

View File

@ -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<rct_gx> GfxLoadGx(const std::vector<uint8_t>& buffer)
{
try
{
OpenRCT2::MemoryStream istream(buffer.data(), buffer.size());
rct_gx gx;
gx.header = istream.ReadValue<rct_g1_header>();
// 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<uint8_t>(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<PaletteMap> FASTCALL gfx_draw_sprite_get_palette(ImageId imageId)
{
if (!imageId.HasSecondary())

View File

@ -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<rct_gx> GfxLoadGx(const std::vector<uint8_t>& 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);

View File

@ -137,6 +137,22 @@ std::vector<std::unique_ptr<ImageTable::RequiredImage>> 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<std::unique_ptr<ImageTable::RequiredImage>> ImageTable::ParseImages(
return result;
}
std::vector<std::unique_ptr<ImageTable::RequiredImage>> ImageTable::LoadImageArchiveImages(
IReadObjectContext* context, const std::string& path, const std::vector<int32_t>& range)
{
std::vector<std::unique_ptr<RequiredImage>> result;
auto gxRaw = context->GetData(path);
std::optional<rct_gx> 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<uintptr_t>(gxData->data.get());
}
if (range.size() > 0)
{
size_t placeHoldersAdded = 0;
for (auto i : range)
{
if (i >= 0 && (i < static_cast<int32_t>(gxData->header.num_entries)))
{
result.push_back(std::make_unique<RequiredImage>(gxData->elements[i]));
}
else
{
result.push_back(std::make_unique<RequiredImage>());
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<int32_t>(gxData->header.num_entries); i++)
result.push_back(std::make_unique<RequiredImage>(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<RequiredImage>());
}
}
return result;
}
std::vector<std::unique_ptr<ImageTable::RequiredImage>> ImageTable::LoadObjectImages(
IReadObjectContext* context, const std::string& name, const std::vector<int32_t>& range)
{

View File

@ -45,6 +45,8 @@ private:
IReadObjectContext* context, const std::string& name, const std::vector<int32_t>& range);
[[nodiscard]] static std::vector<int32_t> ParseRange(std::string s);
[[nodiscard]] static std::string FindLegacyObject(const std::string& name);
[[nodiscard]] static std::vector<std::unique_ptr<ImageTable::RequiredImage>> LoadImageArchiveImages(
IReadObjectContext* context, const std::string& path, const std::vector<int32_t>& range = {});
public:
ImageTable() = default;