2018-12-30 02:59:49 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
* Copyright (c) 2014-2019 OpenRCT2 developers
|
|
|
|
*
|
|
|
|
* For a complete list of all authors, please refer to contributors.md
|
|
|
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
|
|
*
|
|
|
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2018-12-15 11:34:03 +01:00
|
|
|
#include "Context.h"
|
2018-12-15 14:52:52 +01:00
|
|
|
#include "GameState.h"
|
2018-12-15 03:20:42 +01:00
|
|
|
#include "OpenRCT2.h"
|
2018-12-15 13:46:32 +01:00
|
|
|
#include "ParkImporter.h"
|
2018-12-15 01:37:15 +01:00
|
|
|
#include "Version.h"
|
2018-12-15 03:20:42 +01:00
|
|
|
#include "core/Crypt.h"
|
2018-12-30 02:59:49 +01:00
|
|
|
#include "core/OrcaStream.hpp"
|
2018-12-15 03:20:42 +01:00
|
|
|
#include "drawing/Drawing.h"
|
|
|
|
#include "interface/Viewport.h"
|
|
|
|
#include "interface/Window.h"
|
|
|
|
#include "localisation/Date.h"
|
2018-12-15 16:26:51 +01:00
|
|
|
#include "localisation/Localisation.h"
|
2018-12-16 01:24:35 +01:00
|
|
|
#include "management/Award.h"
|
|
|
|
#include "management/Finance.h"
|
|
|
|
#include "management/NewsItem.h"
|
2018-12-15 15:49:53 +01:00
|
|
|
#include "object/Object.h"
|
2018-12-15 13:46:32 +01:00
|
|
|
#include "object/ObjectManager.h"
|
|
|
|
#include "object/ObjectRepository.h"
|
2018-12-16 01:24:35 +01:00
|
|
|
#include "ride/ShopItem.h"
|
2018-12-15 03:20:42 +01:00
|
|
|
#include "scenario/Scenario.h"
|
2018-12-16 00:31:30 +01:00
|
|
|
#include "world/Climate.h"
|
2018-12-15 15:31:17 +01:00
|
|
|
#include "world/Entrance.h"
|
2018-12-15 03:20:42 +01:00
|
|
|
#include "world/Map.h"
|
|
|
|
#include "world/Park.h"
|
2018-12-15 01:37:15 +01:00
|
|
|
|
2018-12-15 15:49:53 +01:00
|
|
|
#include <cstdint>
|
2018-12-16 00:19:36 +01:00
|
|
|
#include <ctime>
|
2018-12-30 00:10:44 +01:00
|
|
|
#include <numeric>
|
2018-12-15 15:49:53 +01:00
|
|
|
#include <string_view>
|
|
|
|
#include <vector>
|
2018-12-15 01:37:15 +01:00
|
|
|
|
2018-12-15 15:49:53 +01:00
|
|
|
namespace OpenRCT2
|
|
|
|
{
|
|
|
|
constexpr uint32_t PARK_FILE_MAGIC = 0x4B524150; // PARK
|
2018-12-15 01:37:15 +01:00
|
|
|
|
2018-12-15 15:49:53 +01:00
|
|
|
// Current version that is saved.
|
|
|
|
constexpr uint32_t PARK_FILE_CURRENT_VERSION = 0x0;
|
2018-12-15 01:37:15 +01:00
|
|
|
|
2018-12-15 15:49:53 +01:00
|
|
|
// The minimum version that is forwards compatible with the current version.
|
|
|
|
constexpr uint32_t PARK_FILE_MIN_VERSION = 0x0;
|
2018-12-15 01:37:15 +01:00
|
|
|
|
2018-12-15 15:49:53 +01:00
|
|
|
namespace ParkFileChunkType
|
|
|
|
{
|
|
|
|
// clang-format off
|
2018-12-16 01:24:35 +01:00
|
|
|
constexpr uint32_t RESERVED_0 = 0x00;
|
|
|
|
|
|
|
|
constexpr uint32_t AUTHORING = 0x01;
|
|
|
|
constexpr uint32_t OBJECTS = 0x02;
|
|
|
|
constexpr uint32_t SCENARIO = 0x03;
|
|
|
|
constexpr uint32_t GENERAL = 0x04;
|
|
|
|
constexpr uint32_t CLIMATE = 0x05;
|
|
|
|
constexpr uint32_t PARK = 0x06;
|
|
|
|
constexpr uint32_t HISTORY = 0x07;
|
|
|
|
constexpr uint32_t RESEARCH = 0x08;
|
|
|
|
constexpr uint32_t NOTIFICATIONS = 0x09;
|
|
|
|
|
|
|
|
constexpr uint32_t INTERFACE = 0x20;
|
|
|
|
constexpr uint32_t EDITOR = 0x21;
|
|
|
|
|
|
|
|
constexpr uint32_t TILES = 0x30;
|
|
|
|
constexpr uint32_t THINGS = 0x31;
|
|
|
|
constexpr uint32_t RIDES = 0x32;
|
|
|
|
constexpr uint32_t BANNERS = 0x33;
|
|
|
|
constexpr uint32_t ANIMATIONS = 0x34;
|
|
|
|
constexpr uint32_t STAFF = 0x35;
|
|
|
|
constexpr uint32_t STRINGS = 0x36;
|
|
|
|
|
|
|
|
constexpr uint32_t DERIVED = 0x50;
|
2018-12-15 15:49:53 +01:00
|
|
|
// clang-format on
|
|
|
|
}; // namespace ParkFileChunkType
|
2018-12-15 03:20:42 +01:00
|
|
|
|
2018-12-15 15:49:53 +01:00
|
|
|
class ParkFile
|
2018-12-15 03:20:42 +01:00
|
|
|
{
|
2018-12-15 15:49:53 +01:00
|
|
|
public:
|
|
|
|
std::vector<rct_object_entry> RequiredObjects;
|
2018-12-15 01:37:15 +01:00
|
|
|
|
2018-12-15 15:49:53 +01:00
|
|
|
private:
|
2018-12-30 03:03:24 +01:00
|
|
|
std::unique_ptr<OrcaStream> _os;
|
2018-12-15 15:49:53 +01:00
|
|
|
|
2018-12-30 00:10:44 +01:00
|
|
|
public:
|
|
|
|
void Load(const std::string_view& path)
|
2018-12-15 11:34:03 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
_os = std::make_unique<OrcaStream>(path, OrcaStream::Mode::READING);
|
2018-12-30 00:10:44 +01:00
|
|
|
RequiredObjects.clear();
|
2018-12-30 03:03:24 +01:00
|
|
|
WriteObjectsChunk(*_os);
|
2018-12-15 11:34:03 +01:00
|
|
|
}
|
|
|
|
|
2018-12-30 00:10:44 +01:00
|
|
|
void Import()
|
2018-12-15 03:20:42 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
auto& os = *_os;
|
|
|
|
WriteTilesChunk(os);
|
|
|
|
WriteScenarioChunk(os);
|
|
|
|
WriteGeneralChunk(os);
|
|
|
|
WriteParkChunk(os);
|
|
|
|
WriteClimateChunk(os);
|
|
|
|
WriteResearch(os);
|
|
|
|
WriteNotifications(os);
|
|
|
|
WriteInterfaceChunk(os);
|
2018-12-30 00:10:44 +01:00
|
|
|
|
|
|
|
// Initial cash will eventually be removed
|
|
|
|
gInitialCash = gCash;
|
|
|
|
String::Set(gS6Info.name, sizeof(gS6Info.name), gScenarioName.c_str());
|
|
|
|
String::Set(gS6Info.details, sizeof(gS6Info.details), gScenarioName.c_str());
|
2018-12-15 03:20:42 +01:00
|
|
|
}
|
|
|
|
|
2018-12-15 15:49:53 +01:00
|
|
|
void Save(const std::string_view& path)
|
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
OrcaStream os(path, OrcaStream::Mode::WRITING);
|
2018-12-30 02:59:49 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
auto& header = os.GetHeader();
|
2018-12-30 02:59:49 +01:00
|
|
|
header.Magic = PARK_FILE_MAGIC;
|
|
|
|
header.TargetVersion = PARK_FILE_CURRENT_VERSION;
|
|
|
|
header.MinVersion = PARK_FILE_MIN_VERSION;
|
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
WriteAuthoringChunk(os);
|
|
|
|
WriteObjectsChunk(os);
|
|
|
|
WriteTilesChunk(os);
|
|
|
|
WriteScenarioChunk(os);
|
|
|
|
WriteGeneralChunk(os);
|
|
|
|
WriteParkChunk(os);
|
|
|
|
WriteClimateChunk(os);
|
|
|
|
WriteResearch(os);
|
|
|
|
WriteNotifications(os);
|
|
|
|
WriteInterfaceChunk(os);
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
2018-12-15 03:20:42 +01:00
|
|
|
|
2018-12-15 15:49:53 +01:00
|
|
|
private:
|
2018-12-30 03:03:24 +01:00
|
|
|
void WriteAuthoringChunk(OrcaStream& os)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
// Write-only for now
|
2018-12-30 03:03:24 +01:00
|
|
|
if (os.GetMode() == OrcaStream::Mode::WRITING)
|
2018-12-30 00:10:44 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::AUTHORING, [](OrcaStream::ChunkStream& cs) {
|
|
|
|
cs.Write(std::string_view(gVersionInfoFull));
|
2018-12-30 00:10:44 +01:00
|
|
|
std::vector<std::string> authors;
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWriteArray(authors, [](std::string& s) {});
|
|
|
|
cs.Write(std::string_view()); // custom notes that can be attached to the save
|
|
|
|
cs.Write<uint64_t>(std::time(0)); // date started
|
|
|
|
cs.Write<uint64_t>(std::time(0)); // date modified
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
|
|
|
}
|
2018-12-15 15:43:24 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
void WriteObjectsChunk(OrcaStream& os)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
if (os.GetMode() == OrcaStream::Mode::READING)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
std::vector<rct_object_entry> entries;
|
2018-12-30 03:03:24 +01:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::OBJECTS, [&entries](OrcaStream::ChunkStream& cs) {
|
|
|
|
cs.ReadWriteArray(entries, [&cs](rct_object_entry& entry) {
|
|
|
|
auto type = cs.Read<uint16_t>();
|
|
|
|
auto id = cs.Read<std::string>();
|
|
|
|
auto version = cs.Read<std::string>();
|
2018-12-30 00:10:44 +01:00
|
|
|
|
|
|
|
entry.flags = type & 0x7FFF;
|
|
|
|
strncpy(entry.name, id.c_str(), 8);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
RequiredObjects = entries;
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
2018-12-30 00:10:44 +01:00
|
|
|
else
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
std::vector<size_t> objectIds(OBJECT_ENTRY_COUNT);
|
|
|
|
std::iota(objectIds.begin(), objectIds.end(), 0);
|
2018-12-30 03:03:24 +01:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::OBJECTS, [&objectIds](OrcaStream::ChunkStream& cs) {
|
2018-12-30 00:10:44 +01:00
|
|
|
auto& objManager = GetContext()->GetObjectManager();
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWriteArray(objectIds, [&cs, &objManager](size_t& i) {
|
2018-12-30 00:10:44 +01:00
|
|
|
auto obj = objManager.GetLoadedObject(i);
|
|
|
|
if (obj != nullptr)
|
|
|
|
{
|
|
|
|
auto entry = obj->GetObjectEntry();
|
|
|
|
auto type = (uint16_t)(entry->flags & 0x0F);
|
|
|
|
type |= 0x8000; // Make as legacy object
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.Write(type);
|
|
|
|
cs.Write(std::string_view(entry->name, 8));
|
|
|
|
cs.Write("");
|
2018-12-30 00:10:44 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.Write<uint16_t>(0);
|
|
|
|
cs.Write("");
|
|
|
|
cs.Write("");
|
2018-12-30 00:10:44 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
|
|
|
}
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
void WriteScenarioChunk(OrcaStream& os)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::SCENARIO, [](OrcaStream::ChunkStream& cs) {
|
|
|
|
cs.ReadWriteAs<uint8_t, uint32_t>(gS6Info.category);
|
|
|
|
ReadWriteStringTable(cs, gScenarioName, "en-GB");
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2018-12-30 00:10:44 +01:00
|
|
|
std::string parkName(128, '\0');
|
|
|
|
format_string(parkName.data(), parkName.size(), gParkName, &gParkNameArgs);
|
2018-12-30 03:03:24 +01:00
|
|
|
ReadWriteStringTable(cs, parkName, "en-GB");
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
ReadWriteStringTable(cs, gScenarioDetails, "en-GB");
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWriteAs<uint8_t, uint32_t>(gScenarioObjectiveType);
|
|
|
|
cs.ReadWriteAs<uint8_t, uint16_t>(gScenarioObjectiveYear); // year
|
|
|
|
cs.ReadWriteAs<uint16_t, uint32_t>(gScenarioObjectiveNumGuests); // guests
|
|
|
|
cs.Write<uint16_t>(600); // rating
|
|
|
|
cs.ReadWriteAs<money32, uint16_t>(gScenarioObjectiveCurrency); // excitement
|
|
|
|
cs.ReadWriteAs<uint16_t, uint32_t>(gScenarioObjectiveNumGuests); // length
|
|
|
|
cs.ReadWrite<money32>(gScenarioObjectiveCurrency); // park value
|
|
|
|
cs.ReadWrite<money32>(gScenarioObjectiveCurrency); // ride profit
|
|
|
|
cs.ReadWrite<money32>(gScenarioObjectiveCurrency); // shop profit
|
2018-12-15 16:26:51 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gScenarioParkRatingWarningDays);
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gScenarioCompletedCompanyValue);
|
2018-12-30 00:10:44 +01:00
|
|
|
if (gScenarioCompletedCompanyValue == MONEY32_UNDEFINED
|
|
|
|
|| gScenarioCompletedCompanyValue == (money32)0x80000001)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.Write("");
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gScenarioCompletedBy);
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-15 16:26:51 +01:00
|
|
|
}
|
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
void WriteGeneralChunk(OrcaStream& os)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
auto found = os.ReadWriteChunk(ParkFileChunkType::GENERAL, [](OrcaStream::ChunkStream& cs) {
|
|
|
|
cs.ReadWriteAs<uint32_t, uint64_t>(gScenarioTicks);
|
|
|
|
cs.ReadWriteAs<uint16_t, uint32_t>(gDateMonthTicks);
|
|
|
|
cs.ReadWrite(gDateMonthsElapsed);
|
|
|
|
cs.ReadWrite(gScenarioSrand0);
|
|
|
|
cs.ReadWrite(gScenarioSrand1);
|
|
|
|
cs.ReadWrite(gGuestInitialCash);
|
|
|
|
cs.ReadWrite(gGuestInitialHunger);
|
|
|
|
cs.ReadWrite(gGuestInitialThirst);
|
|
|
|
|
|
|
|
cs.ReadWrite(gNextGuestNumber);
|
|
|
|
cs.ReadWriteArray(gPeepSpawns, [&cs](PeepSpawn& spawn) {
|
|
|
|
cs.ReadWrite(spawn.x);
|
|
|
|
cs.ReadWrite(spawn.y);
|
|
|
|
cs.ReadWrite(spawn.z);
|
|
|
|
cs.ReadWrite(spawn.direction);
|
2018-12-29 20:43:16 +01:00
|
|
|
});
|
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gLandPrice);
|
|
|
|
cs.ReadWrite(gConstructionRightsPrice);
|
|
|
|
cs.ReadWrite(gGrassSceneryTileLoopPosition); // TODO (this needs to be xy32)
|
2018-12-29 20:43:16 +01:00
|
|
|
});
|
|
|
|
if (!found)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-29 20:43:16 +01:00
|
|
|
throw std::runtime_error("No general chunk found.");
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
|
|
|
}
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
void WriteInterfaceChunk(OrcaStream& os)
|
2018-12-16 01:24:35 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::INTERFACE, [](OrcaStream::ChunkStream& cs) {
|
|
|
|
cs.ReadWrite(gSavedViewX);
|
|
|
|
cs.ReadWrite(gSavedViewY);
|
|
|
|
cs.ReadWrite(gSavedViewZoom);
|
|
|
|
cs.ReadWrite(gSavedViewRotation);
|
|
|
|
cs.ReadWriteAs<uint8_t, uint32_t>(gLastEntranceStyle);
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
void WriteClimateChunk(OrcaStream& os)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::CLIMATE, [](OrcaStream::ChunkStream& cs) {
|
|
|
|
cs.ReadWrite(gClimate);
|
|
|
|
cs.ReadWrite(gClimateUpdateTimer);
|
2018-12-15 15:49:53 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
for (const auto* cl : { &gClimateCurrent, &gClimateNext })
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(cl->Weather);
|
|
|
|
cs.ReadWrite(cl->Temperature);
|
|
|
|
cs.ReadWrite(cl->WeatherEffect);
|
|
|
|
cs.ReadWrite(cl->WeatherGloom);
|
|
|
|
cs.ReadWrite(cl->RainLevel);
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
void WriteParkChunk(OrcaStream& os)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::PARK, [](OrcaStream::ChunkStream& cs) {
|
|
|
|
cs.ReadWrite(gParkNameArgs);
|
|
|
|
cs.ReadWrite(gCash);
|
|
|
|
cs.ReadWrite(gBankLoan);
|
|
|
|
cs.ReadWrite(gMaxBankLoan);
|
|
|
|
cs.ReadWriteAs<uint8_t, uint16_t>(gBankLoanInterestRate);
|
|
|
|
cs.ReadWriteAs<uint32_t, uint64_t>(gParkFlags);
|
|
|
|
cs.ReadWriteAs<money16, money32>(gParkEntranceFee);
|
|
|
|
cs.ReadWrite(gStaffHandymanColour);
|
|
|
|
cs.ReadWrite(gStaffMechanicColour);
|
|
|
|
cs.ReadWrite(gStaffSecurityColour);
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2018-12-30 00:10:44 +01:00
|
|
|
// TODO use a uint64 or a list of active items
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gSamePriceThroughoutParkA);
|
|
|
|
cs.ReadWrite(gSamePriceThroughoutParkB);
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2018-12-30 00:10:44 +01:00
|
|
|
// Marketing
|
|
|
|
std::vector<std::tuple<uint32_t, uint32_t>> marketing;
|
2018-12-30 03:03:24 +01:00
|
|
|
if (cs.GetMode() != OrcaStream::Mode::READING)
|
2018-12-30 00:10:44 +01:00
|
|
|
{
|
|
|
|
for (size_t i = 0; i < std::size(gMarketingCampaignDaysLeft); i++)
|
|
|
|
{
|
|
|
|
marketing.push_back(std::make_tuple(gMarketingCampaignDaysLeft[i], gMarketingCampaignRideIndex[i]));
|
|
|
|
}
|
|
|
|
}
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWriteArray(marketing, [&cs](std::tuple<uint32_t, uint32_t>& m) {
|
|
|
|
cs.ReadWrite(std::get<0>(m));
|
|
|
|
cs.ReadWrite(std::get<1>(m));
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-30 03:03:24 +01:00
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
2018-12-30 00:10:44 +01:00
|
|
|
{
|
|
|
|
auto count = std::min(std::size(gMarketingCampaignDaysLeft), marketing.size());
|
|
|
|
for (size_t i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
gMarketingCampaignDaysLeft[i] = std::get<0>(marketing[i]);
|
|
|
|
gMarketingCampaignRideIndex[i] = std::get<1>(marketing[i]);
|
|
|
|
}
|
|
|
|
}
|
2018-12-15 15:31:17 +01:00
|
|
|
|
2018-12-30 00:10:44 +01:00
|
|
|
// Awards
|
|
|
|
std::vector<Award> awards(std::begin(gCurrentAwards), std::end(gCurrentAwards));
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWriteArray(awards, [&cs](Award& award) {
|
|
|
|
cs.ReadWrite(award.Time);
|
|
|
|
cs.ReadWrite(award.Type);
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-30 03:03:24 +01:00
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
2018-12-30 00:10:44 +01:00
|
|
|
{
|
|
|
|
for (size_t i = 0; i < std::size(gCurrentAwards); i++)
|
|
|
|
{
|
|
|
|
if (awards.size() > i)
|
|
|
|
{
|
|
|
|
gCurrentAwards[i] = awards[i];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gCurrentAwards[i].Time = 0;
|
|
|
|
gCurrentAwards[i].Type = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-12-15 15:31:17 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gParkRatingCasualtyPenalty);
|
|
|
|
cs.ReadWrite(gCurrentExpenditure);
|
|
|
|
cs.ReadWrite(gCurrentProfit);
|
|
|
|
cs.ReadWrite(gTotalAdmissions);
|
|
|
|
cs.ReadWrite(gTotalIncomeFromAdmissions);
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
2018-12-15 15:43:24 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
void WriteResearch(OrcaStream& os)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::RESEARCH, [](OrcaStream::ChunkStream& cs) {
|
2018-12-30 00:10:44 +01:00
|
|
|
// Research status
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gResearchFundingLevel);
|
|
|
|
cs.ReadWrite(gResearchPriorities);
|
|
|
|
cs.ReadWrite(gResearchProgressStage);
|
|
|
|
cs.ReadWrite(gResearchProgress);
|
|
|
|
cs.ReadWrite(gResearchExpectedMonth);
|
|
|
|
cs.ReadWrite(gResearchExpectedDay);
|
|
|
|
cs.ReadWrite(gResearchLastItem);
|
|
|
|
cs.ReadWrite(gResearchNextItem);
|
2018-12-30 00:10:44 +01:00
|
|
|
|
|
|
|
// Research order
|
|
|
|
// type (uint8_t)
|
|
|
|
// flags (uint8_t)
|
|
|
|
// entry (uint32_t)
|
|
|
|
});
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
2018-12-15 14:52:52 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
void WriteNotifications(OrcaStream& os)
|
2018-12-30 00:10:44 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::NOTIFICATIONS, [](OrcaStream::ChunkStream& cs) {
|
2018-12-30 00:10:44 +01:00
|
|
|
std::vector<NewsItem> notifications(std::begin(gNewsItems), std::end(gNewsItems));
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWriteArray(notifications, [&cs](NewsItem& notification) {
|
|
|
|
cs.ReadWrite(notification.Type);
|
|
|
|
cs.ReadWrite(notification.Flags);
|
|
|
|
cs.ReadWrite(notification.Assoc);
|
|
|
|
cs.ReadWrite(notification.Ticks);
|
|
|
|
cs.ReadWrite(notification.MonthYear);
|
|
|
|
cs.ReadWrite(notification.Day);
|
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
2018-12-30 00:10:44 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
auto s = cs.Read<std::string>();
|
2018-12-30 00:10:44 +01:00
|
|
|
String::Set(notification.Text, sizeof(notification.Text), s.c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.Write(std::string(notification.Text));
|
2018-12-30 00:10:44 +01:00
|
|
|
}
|
|
|
|
});
|
2018-12-30 03:03:24 +01:00
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
2018-12-15 16:26:51 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
for (size_t i = 0; i < std::size(gNewsItems); i++)
|
|
|
|
{
|
|
|
|
if (notifications.size() > i)
|
|
|
|
{
|
|
|
|
gNewsItems[i] = notifications[i];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gNewsItems[i] = {};
|
|
|
|
gNewsItems[i].Type = NEWS_ITEM_NULL;
|
|
|
|
}
|
|
|
|
}
|
2018-12-15 16:26:51 +01:00
|
|
|
}
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-15 16:26:51 +01:00
|
|
|
}
|
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
void WriteDerivedChunk(OrcaStream& os)
|
2018-12-15 16:26:51 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
if (os.GetMode() == OrcaStream::Mode::WRITING)
|
2018-12-15 16:26:51 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::NOTIFICATIONS, [](OrcaStream::ChunkStream& cs) {
|
|
|
|
cs.Write<uint32_t>(gParkSize);
|
|
|
|
cs.Write<uint32_t>(gNumGuestsInPark);
|
|
|
|
cs.Write<uint32_t>(gNumGuestsHeadingForPark);
|
|
|
|
cs.Write<uint32_t>(gCompanyValue);
|
|
|
|
cs.Write<uint32_t>(gParkValue);
|
|
|
|
cs.Write<uint32_t>(gParkRating);
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-15 16:26:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
void WriteTilesChunk(OrcaStream& os)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
auto found = os.ReadWriteChunk(ParkFileChunkType::TILES, [](OrcaStream::ChunkStream& cs) {
|
|
|
|
cs.ReadWriteAs<int16_t, uint32_t>(gMapSize); // x
|
|
|
|
cs.Write<uint32_t>(gMapSize); // y
|
2018-12-30 00:10:44 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
OpenRCT2::GetContext()->GetGameState()->InitAll(gMapSize);
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
|
|
|
|
2018-12-30 00:10:44 +01:00
|
|
|
std::vector<TileElement> tiles(std::begin(gTileElements), std::end(gTileElements));
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWriteArray(tiles, [&cs](TileElement& el) { cs.ReadWrite(&el, sizeof(TileElement)); });
|
2018-12-30 00:10:44 +01:00
|
|
|
std::copy_n(tiles.data(), std::min(tiles.size(), std::size(gTileElements)), gTileElements);
|
2018-12-15 14:52:52 +01:00
|
|
|
|
2018-12-30 00:10:44 +01:00
|
|
|
map_update_tile_pointers();
|
|
|
|
UpdateParkEntranceLocations();
|
|
|
|
});
|
|
|
|
if (!found)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
throw std::runtime_error("No tiles chunk found.");
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
|
|
|
}
|
2018-12-15 14:52:52 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
static void ReadWriteStringTable(OrcaStream::ChunkStream& cs, std::string& value, const std::string_view& lcode)
|
2018-12-16 00:31:30 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
std::vector<std::tuple<std::string, std::string>> table;
|
2018-12-30 03:03:24 +01:00
|
|
|
if (cs.GetMode() != OrcaStream::Mode::READING)
|
2018-12-16 00:31:30 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
table.push_back(std::make_tuple(std::string(lcode), value));
|
2018-12-16 00:31:30 +01:00
|
|
|
}
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWriteArray(table, [&cs](std::tuple<std::string, std::string>& v) {
|
|
|
|
cs.ReadWrite(std::get<0>(v));
|
|
|
|
cs.ReadWrite(std::get<1>(v));
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-30 03:03:24 +01:00
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
2018-12-17 00:24:04 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
auto fr = std::find_if(table.begin(), table.end(), [&lcode](const std::tuple<std::string, std::string>& v) {
|
|
|
|
return std::get<0>(v) == lcode;
|
|
|
|
});
|
|
|
|
if (fr != table.end())
|
2018-12-17 00:24:04 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
value = std::get<1>(*fr);
|
2018-12-17 00:24:04 +01:00
|
|
|
}
|
2018-12-30 00:10:44 +01:00
|
|
|
else if (table.size() > 0)
|
2018-12-17 00:24:04 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
value = std::get<1>(table[0]);
|
2018-12-17 00:24:04 +01:00
|
|
|
}
|
2018-12-30 00:10:44 +01:00
|
|
|
else
|
2018-12-17 00:24:04 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
value = "";
|
2018-12-17 00:24:04 +01:00
|
|
|
}
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // namespace OpenRCT2
|
2018-12-15 14:52:52 +01:00
|
|
|
|
2018-12-15 03:20:42 +01:00
|
|
|
enum : uint32_t
|
|
|
|
{
|
|
|
|
S6_SAVE_FLAG_EXPORT = 1 << 0,
|
|
|
|
S6_SAVE_FLAG_SCENARIO = 1 << 1,
|
|
|
|
S6_SAVE_FLAG_AUTOMATIC = 1u << 31,
|
|
|
|
};
|
|
|
|
|
|
|
|
int32_t scenario_save(const utf8* path, int32_t flags)
|
|
|
|
{
|
|
|
|
if (flags & S6_SAVE_FLAG_SCENARIO)
|
|
|
|
{
|
|
|
|
log_verbose("saving scenario");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
log_verbose("saving game");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(flags & S6_SAVE_FLAG_AUTOMATIC))
|
|
|
|
{
|
|
|
|
window_close_construction_windows();
|
|
|
|
}
|
|
|
|
|
|
|
|
map_reorganise_elements();
|
|
|
|
viewport_set_saved_view();
|
|
|
|
|
|
|
|
bool result = false;
|
2018-12-15 15:49:53 +01:00
|
|
|
auto parkFile = std::make_unique<OpenRCT2::ParkFile>();
|
2018-12-15 03:20:42 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
// if (flags & S6_SAVE_FLAG_EXPORT)
|
|
|
|
// {
|
|
|
|
// auto& objManager = OpenRCT2::GetContext()->GetObjectManager();
|
|
|
|
// s6exporter->ExportObjectsList = objManager.GetPackableObjects();
|
|
|
|
// }
|
|
|
|
// s6exporter->RemoveTracklessRides = true;
|
|
|
|
// s6exporter->Export();
|
|
|
|
if (flags & S6_SAVE_FLAG_SCENARIO)
|
|
|
|
{
|
|
|
|
// s6exporter->SaveScenario(path);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// s6exporter->SaveGame(path);
|
|
|
|
}
|
|
|
|
parkFile->Save(path);
|
|
|
|
result = true;
|
|
|
|
}
|
|
|
|
catch (const std::exception&)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
gfx_invalidate_screen();
|
|
|
|
|
|
|
|
if (result && !(flags & S6_SAVE_FLAG_AUTOMATIC))
|
2018-12-15 01:37:15 +01:00
|
|
|
{
|
2018-12-15 03:20:42 +01:00
|
|
|
gScreenAge = 0;
|
2018-12-15 01:37:15 +01:00
|
|
|
}
|
2018-12-15 03:20:42 +01:00
|
|
|
return result;
|
2018-12-15 01:37:15 +01:00
|
|
|
}
|
2018-12-15 13:46:32 +01:00
|
|
|
|
|
|
|
class ParkFileImporter : public IParkImporter
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
const IObjectRepository& _objectRepository;
|
2018-12-15 15:49:53 +01:00
|
|
|
std::unique_ptr<OpenRCT2::ParkFile> _parkFile;
|
2018-12-15 13:46:32 +01:00
|
|
|
|
|
|
|
public:
|
|
|
|
ParkFileImporter(IObjectRepository& objectRepository)
|
|
|
|
: _objectRepository(objectRepository)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
ParkLoadResult Load(const utf8* path) override
|
|
|
|
{
|
2018-12-15 15:49:53 +01:00
|
|
|
_parkFile = std::make_unique<OpenRCT2::ParkFile>();
|
2018-12-15 14:52:52 +01:00
|
|
|
_parkFile->Load(path);
|
|
|
|
return ParkLoadResult(std::move(_parkFile->RequiredObjects));
|
2018-12-15 13:46:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ParkLoadResult LoadSavedGame(const utf8* path, bool skipObjectCheck = false) override
|
|
|
|
{
|
|
|
|
return Load(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
ParkLoadResult LoadScenario(const utf8* path, bool skipObjectCheck = false) override
|
|
|
|
{
|
|
|
|
return Load(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
ParkLoadResult LoadFromStream(
|
|
|
|
IStream* stream, bool isScenario, bool skipObjectCheck = false, const utf8* path = String::Empty) override
|
|
|
|
{
|
|
|
|
return Load(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Import() override
|
|
|
|
{
|
2018-12-15 14:52:52 +01:00
|
|
|
_parkFile->Import();
|
2018-12-15 13:46:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool GetDetails(scenario_index_entry* dst) override
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
std::unique_ptr<IParkImporter> ParkImporter::CreateParkFile(IObjectRepository& objectRepository)
|
|
|
|
{
|
|
|
|
return std::make_unique<ParkFileImporter>(objectRepository);
|
|
|
|
}
|