Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parkobj loads rct_gx #16806

Merged
merged 11 commits into from
Mar 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions distribution/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
26 changes: 26 additions & 0 deletions src/openrct2/drawing/Drawing.Sprite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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())
Expand Down
4 changes: 3 additions & 1 deletion src/openrct2/drawing/Drawing.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ struct ScreenRect;
namespace OpenRCT2
{
struct IPlatformEnvironment;
}
struct IStream;
} // namespace OpenRCT2

namespace OpenRCT2::Drawing
{
Expand Down Expand Up @@ -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);
Expand Down
71 changes: 71 additions & 0 deletions src/openrct2/object/ImageTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
spacek531 marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down Expand Up @@ -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)
{
Expand Down
2 changes: 2 additions & 0 deletions src/openrct2/object/ImageTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class ImageTable
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;
Expand Down