diff --git a/src/openrct2/drawing/Drawing.Sprite.cpp b/src/openrct2/drawing/Drawing.Sprite.cpp index bc9fd6a29f..050f3d8bcf 100644 --- a/src/openrct2/drawing/Drawing.Sprite.cpp +++ b/src/openrct2/drawing/Drawing.Sprite.cpp @@ -14,7 +14,6 @@ #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" @@ -362,22 +361,25 @@ bool gfx_load_csg() } } -std::optional GfxLoadGx(const std::vector& buffer) +std::optional GfxLoadGx(OpenRCT2::IStream* stream) { try { - OpenRCT2::MemoryStream istream(buffer.data(), buffer.size()); rct_gx gx; - gx.header = istream.ReadValue(); + gx.header = stream->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_and_convert_gxdat(stream, gx.header.num_entries, false, gx.elements.data()); // Read element data - gx.data = istream.ReadArray(gx.header.total_size); - + gx.data = std::make_unique(gx.header.total_size); + const auto readBytes = stream->TryRead(gx.data.get(), gx.header.total_size); + if (readBytes != gx.header.total_size) + { + log_verbose("GXdata size shorter than expected."); + } return std::make_optional(std::move(gx)); } catch (const std::exception&) diff --git a/src/openrct2/drawing/Drawing.h b/src/openrct2/drawing/Drawing.h index cd85e9c197..9d0435da6f 100644 --- a/src/openrct2/drawing/Drawing.h +++ b/src/openrct2/drawing/Drawing.h @@ -526,7 +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); +std::optional GfxLoadGx(OpenRCT2::IStream* stream); 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 98602dd3dd..8937c9f90e 100644 --- a/src/openrct2/object/ImageTable.cpp +++ b/src/openrct2/object/ImageTable.cpp @@ -16,6 +16,7 @@ #include "../core/FileScanner.h" #include "../core/IStream.hpp" #include "../core/Json.hpp" +#include "../core/MemoryStream.h" #include "../core/Path.hpp" #include "../core/String.hpp" #include "../drawing/ImageImporter.h" @@ -240,7 +241,8 @@ std::vector> ImageTable::LoadImageArc { std::vector> result; auto gxRaw = context->GetData(path); - std::optional gxData = GfxLoadGx(gxRaw); + OpenRCT2::MemoryStream stream(gxRaw.data(), gxRaw.size()); + std::optional gxData = GfxLoadGx(&stream); if (gxData.has_value()) { // Fix entry data offsets @@ -428,63 +430,19 @@ void ImageTable::Read(IReadObjectContext* context, OpenRCT2::IStream* stream) { return; } + auto gxData = GfxLoadGx(stream); - try + if (gxData.has_value()) { - uint32_t numImages = stream->ReadValue(); - uint32_t imageDataSize = stream->ReadValue(); - - uint64_t headerTableSize = numImages * 16; - uint64_t remainingBytes = stream->GetLength() - stream->GetPosition() - headerTableSize; - if (remainingBytes > imageDataSize) + _data = std::move(gxData->data); + // Fix entry data offsets + for (auto& entry : gxData->elements) { - context->LogVerbose(ObjectError::BadImageTable, "Image table size longer than expected."); - imageDataSize = static_cast(remainingBytes); + entry.offset += reinterpret_cast(_data.get()); } - - auto dataSize = static_cast(imageDataSize); - auto data = std::make_unique(dataSize); - if (data == nullptr) - { - context->LogError(ObjectError::BadImageTable, "Image table too large."); - throw std::runtime_error("Image table too large."); - } - - // Read g1 element headers - uintptr_t imageDataBase = reinterpret_cast(data.get()); - std::vector newEntries; - for (uint32_t i = 0; i < numImages; i++) - { - rct_g1_element g1Element{}; - - uintptr_t imageDataOffset = static_cast(stream->ReadValue()); - g1Element.offset = reinterpret_cast(imageDataBase + imageDataOffset); - - g1Element.width = stream->ReadValue(); - g1Element.height = stream->ReadValue(); - g1Element.x_offset = stream->ReadValue(); - g1Element.y_offset = stream->ReadValue(); - g1Element.flags = stream->ReadValue(); - g1Element.zoomed_offset = stream->ReadValue(); - - newEntries.push_back(std::move(g1Element)); - } - - // Read g1 element data - size_t readBytes = static_cast(stream->TryRead(data.get(), dataSize)); - - // If data is shorter than expected (some custom objects are unfortunately like that) - size_t unreadBytes = dataSize - readBytes; - if (unreadBytes > 0) - { - std::fill_n(data.get() + readBytes, unreadBytes, 0); - context->LogWarning(ObjectError::BadImageTable, "Image table size shorter than expected."); - } - - _data = std::move(data); - _entries.insert(_entries.end(), newEntries.begin(), newEntries.end()); + _entries.insert(_entries.end(), gxData->elements.begin(), gxData->elements.end()); } - catch (const std::exception&) + else { context->LogError(ObjectError::BadImageTable, "Bad image table."); throw;