diff --git a/src/openrct2/core/FileStream.hpp b/src/openrct2/core/FileStream.hpp
index f9a0660e0b..de10f7857e 100644
--- a/src/openrct2/core/FileStream.hpp
+++ b/src/openrct2/core/FileStream.hpp
@@ -73,6 +73,26 @@ public:
_fileSize = SDL_RWsize(_file);
}
+ FileStream(SDL_RWops * ops, sint32 fileMode)
+ {
+ _file = ops;
+ switch (fileMode) {
+ case FILE_MODE_OPEN:
+ _canRead = true;
+ _canWrite = false;
+ break;
+ case FILE_MODE_WRITE:
+ _canRead = false;
+ _canWrite = true;
+ break;
+ default:
+ throw;
+ }
+
+ _disposed = false;
+ _fileSize = SDL_RWsize(_file);
+ }
+
~FileStream()
{
if (!_disposed)
diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj
index d9c83a17b6..06650fd1a9 100644
--- a/src/openrct2/libopenrct2.vcxproj
+++ b/src/openrct2/libopenrct2.vcxproj
@@ -547,7 +547,6 @@
-
diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp
index 11026d6860..7738f3ef2b 100644
--- a/src/openrct2/rct1/S4Importer.cpp
+++ b/src/openrct2/rct1/S4Importer.cpp
@@ -161,7 +161,8 @@ public:
std::unique_ptr decodedData = std::unique_ptr(Memory::Allocate(sizeof(rct1_s4)));
size_t decodedSize;
- if (isScenario)
+ sint32 fileType = sawyercoding_detect_file_type(data.get(), dataSize);
+ if (isScenario && (fileType & FILE_VERSION_MASK) != FILE_VERSION_RCT1)
{
decodedSize = sawyercoding_decode_sc4(data.get(), decodedData.get(), dataSize, sizeof(rct1_s4));
}
diff --git a/src/openrct2/rct12/SawyerEncoding.cpp b/src/openrct2/rct12/SawyerEncoding.cpp
index 68971a2b59..d1b161010e 100644
--- a/src/openrct2/rct12/SawyerEncoding.cpp
+++ b/src/openrct2/rct12/SawyerEncoding.cpp
@@ -14,7 +14,9 @@
*****************************************************************************/
#pragma endregion
+#include
#include "../core/IStream.hpp"
+#include "../core/Math.hpp"
#include "SawyerEncoding.h"
extern "C"
@@ -24,6 +26,47 @@ extern "C"
namespace SawyerEncoding
{
+ void ReadChunk(void * dst, size_t expectedSize, IStream * stream)
+ {
+ if (!TryReadChunk(dst, expectedSize, stream))
+ {
+ throw IOException("Invalid or incorrect chunk size.");
+ }
+ }
+
+ void ReadChunkTolerant(void * dst, size_t expectedSize, IStream * stream)
+ {
+ uint64 originalPosition = stream->GetPosition();
+
+ auto header = stream->ReadValue();
+ switch (header.encoding) {
+ case CHUNK_ENCODING_NONE:
+ case CHUNK_ENCODING_RLE:
+ case CHUNK_ENCODING_RLECOMPRESSED:
+ case CHUNK_ENCODING_ROTATE:
+ {
+ std::unique_ptr compressedData = std::unique_ptr(Memory::Allocate(header.length));
+ if (stream->TryRead(compressedData.get(), header.length) != header.length)
+ {
+ throw IOException("Corrupt chunk size.");
+ }
+
+ // Allow 16MiB for chunk data
+ size_t bufferSize = 16 * 1024 * 1024;
+ std::unique_ptr buffer = std::unique_ptr(Memory::Allocate(bufferSize));
+ size_t uncompressedLength = sawyercoding_read_chunk_buffer(buffer.get(), compressedData.get(), header, bufferSize);
+ size_t copyLength = Math::Min(uncompressedLength, expectedSize);
+
+ Memory::Set(dst, 0, expectedSize);
+ Memory::Copy(dst, buffer.get(), copyLength);
+ break;
+ }
+ default:
+ stream->SetPosition(originalPosition);
+ throw IOException("Invalid chunk encoding.");
+ }
+ }
+
bool TryReadChunk(void * dst, size_t expectedSize, IStream * stream)
{
uint64 originalPosition = stream->GetPosition();
diff --git a/src/openrct2/rct12/SawyerEncoding.h b/src/openrct2/rct12/SawyerEncoding.h
index 188ebed2db..ace15db533 100644
--- a/src/openrct2/rct12/SawyerEncoding.h
+++ b/src/openrct2/rct12/SawyerEncoding.h
@@ -22,6 +22,8 @@ interface IStream;
namespace SawyerEncoding
{
+ void ReadChunk(void * dst, size_t expectedSize, IStream * stream);
+ void ReadChunkTolerant(void * dst, size_t expectedSize, IStream * stream);
bool TryReadChunk(void * dst, size_t expectedSize, IStream * stream);
template
diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp
index dddb46ed79..b0f74d93d3 100644
--- a/src/openrct2/rct2/S6Importer.cpp
+++ b/src/openrct2/rct2/S6Importer.cpp
@@ -15,10 +15,14 @@
#pragma endregion
#include "../core/Exception.hpp"
+#include "../core/FileStream.hpp"
#include "../core/IStream.hpp"
+#include "../core/Path.hpp"
+#include "../core/String.hpp"
#include "../management/award.h"
#include "../network/network.h"
-#include "S6Importer.h"
+#include "../ParkImporter.h"
+#include "../rct12/SawyerEncoding.h"
extern "C"
{
@@ -50,350 +54,358 @@ public:
explicit ObjectLoadException(const char * message) : Exception(message) { }
};
-S6Importer::S6Importer()
+/**
+ * Class to import RollerCoaster Tycoon 2 scenarios (*.SC6) and saved games (*.SV6).
+ */
+class S6Importer final : public IParkImporter
{
- FixIssues = false;
- memset(&_s6, 0, sizeof(_s6));
-}
+private:
+ const utf8 * _s6Path = nullptr;
+ rct_s6_data _s6 = { 0 };
+ uint8 _gameVersion = 0;
-void S6Importer::LoadSavedGame(const utf8 * path)
-{
- SDL_RWops * rw = SDL_RWFromFile(path, "rb");
- if (rw == nullptr)
+public:
+ void Load(const utf8 * path) override
{
- throw IOException("Unable to open SV6.");
+ const utf8 * extension = Path::GetExtension(path);
+ if (String::Equals(extension, ".sc6", true))
+ {
+ LoadScenario(path);
+ }
+ else if (String::Equals(extension, ".sv6", true))
+ {
+ LoadSavedGame(path);
+ }
+ else
+ {
+ throw Exception("Invalid RCT2 park extension.");
+ }
}
- if (!sawyercoding_validate_checksum(rw))
+ void LoadSavedGame(const utf8 * path) override
{
- gErrorType = ERROR_TYPE_FILE_LOAD;
- gGameCommandErrorTitle = STR_FILE_CONTAINS_INVALID_DATA;
+ // if (!sawyercoding_validate_checksum(rw))
+ // {
+ // gErrorType = ERROR_TYPE_FILE_LOAD;
+ // gGameCommandErrorTitle = STR_FILE_CONTAINS_INVALID_DATA;
+ //
+ // log_error("failed to load saved game, invalid checksum");
+ // throw IOException("Invalid SV6 checksum.");
+ // }
- log_error("failed to load saved game, invalid checksum");
- throw IOException("Invalid SV6 checksum.");
+ auto fs = FileStream(path, FILE_MODE_OPEN);
+ LoadFromStream(&fs, false);
+ _s6Path = path;
}
- LoadSavedGame(rw);
-
- SDL_RWclose(rw);
-
- _s6Path = path;
-}
-
-void S6Importer::LoadScenario(const utf8 * path)
-{
- SDL_RWops * rw = SDL_RWFromFile(path, "rb");
- if (rw == nullptr)
+ void LoadScenario(const utf8 * path) override
{
- throw IOException("Unable to open SV6.");
+ // if (!gConfigGeneral.allow_loading_with_incorrect_checksum && !sawyercoding_validate_checksum(rw))
+ // {
+ // SDL_RWclose(rw);
+ //
+ // gErrorType = ERROR_TYPE_FILE_LOAD;
+ // gErrorStringId = STR_FILE_CONTAINS_INVALID_DATA;
+ //
+ // log_error("failed to load scenario, invalid checksum");
+ // throw IOException("Invalid SC6 checksum.");
+ // }
+
+ auto fs = FileStream(path, FILE_MODE_OPEN);
+ LoadFromStream(&fs, true);
+ _s6Path = path;
}
- if (!gConfigGeneral.allow_loading_with_incorrect_checksum && !sawyercoding_validate_checksum(rw))
+ void LoadFromStream(IStream * stream, bool isScenario)
{
- SDL_RWclose(rw);
+ SawyerEncoding::ReadChunkTolerant(&_s6.header, sizeof(_s6.header), stream);
- gErrorType = ERROR_TYPE_FILE_LOAD;
- gErrorStringId = STR_FILE_CONTAINS_INVALID_DATA;
+ log_verbose("saved game classic_flag = 0x%02x\n", _s6.header.classic_flag);
+ if (isScenario)
+ {
+ if (_s6.header.type != S6_TYPE_SCENARIO)
+ {
+ throw Exception("Park is not a scenario.");
+ }
+ }
+ else
+ {
+ if (_s6.header.type != S6_TYPE_SAVEDGAME)
+ {
+ throw Exception("Park is not a saved game.");
+ }
+ }
- log_error("failed to load scenario, invalid checksum");
- throw IOException("Invalid SC6 checksum.");
+ SawyerEncoding::ReadChunkTolerant(&_s6.info, sizeof(_s6.info), stream);
+
+ // Read packed objects
+ // TODO try to contain this more and not store objects until later
+ for (uint16 i = 0; i < _s6.header.num_packed_objects; i++)
+ {
+ // object_load_packed(rw);
+ }
+
+ if (isScenario)
+ {
+ SawyerEncoding::ReadChunkTolerant(&_s6.objects, sizeof(_s6.objects), stream);
+ SawyerEncoding::ReadChunkTolerant(&_s6.elapsed_months, 16, stream);
+ SawyerEncoding::ReadChunkTolerant(&_s6.map_elements, sizeof(_s6.map_elements), stream);
+ SawyerEncoding::ReadChunkTolerant(&_s6.next_free_map_element_pointer_index, 2560076, stream);
+ SawyerEncoding::ReadChunkTolerant(&_s6.guests_in_park, 4, stream);
+ SawyerEncoding::ReadChunkTolerant(&_s6.last_guests_in_park, 8, stream);
+ SawyerEncoding::ReadChunkTolerant(&_s6.park_rating, 2, stream);
+ SawyerEncoding::ReadChunkTolerant(&_s6.active_research_types, 1082, stream);
+ SawyerEncoding::ReadChunkTolerant(&_s6.current_expenditure, 16, stream);
+ SawyerEncoding::ReadChunkTolerant(&_s6.park_value, 4, stream);
+ SawyerEncoding::ReadChunkTolerant(&_s6.completed_company_value, 483816, stream);
+ }
+ else
+ {
+ SawyerEncoding::ReadChunkTolerant(&_s6.objects, sizeof(_s6.objects), stream);
+ SawyerEncoding::ReadChunkTolerant(&_s6.elapsed_months, 16, stream);
+ SawyerEncoding::ReadChunkTolerant(&_s6.map_elements, sizeof(_s6.map_elements), stream);
+ SawyerEncoding::ReadChunkTolerant(&_s6.next_free_map_element_pointer_index, 3048816, stream);
+ }
}
- LoadScenario(rw);
-
- SDL_RWclose(rw);
-
- _s6Path = path;
-}
-
-void S6Importer::LoadSavedGame(SDL_RWops *rw)
-{
- sawyercoding_read_chunk_safe(rw, &_s6.header, sizeof(_s6.header));
- if (_s6.header.type != S6_TYPE_SAVEDGAME)
+ bool GetDetails(scenario_index_entry * dst) override
{
- throw Exception("Data is not a saved game.");
- }
- log_verbose("saved game classic_flag = 0x%02x\n", _s6.header.classic_flag);
-
- // Read packed objects
- // TODO try to contain this more and not store objects until later
- for (uint16 i = 0; i < _s6.header.num_packed_objects; i++)
- {
- object_load_packed(rw);
+ Memory::Set(dst, 0, sizeof(scenario_index_entry));
+ return false;
}
- sawyercoding_read_chunk_safe(rw, &_s6.objects, sizeof(_s6.objects));
- sawyercoding_read_chunk_safe(rw, &_s6.elapsed_months, 16);
- sawyercoding_read_chunk_safe(rw, &_s6.map_elements, sizeof(_s6.map_elements));
- sawyercoding_read_chunk_safe(rw, &_s6.next_free_map_element_pointer_index, 3048816);
-}
-
-void S6Importer::LoadScenario(SDL_RWops *rw)
-{
- sawyercoding_read_chunk_safe(rw, &_s6.header, sizeof(_s6.header));
- if (_s6.header.type != S6_TYPE_SCENARIO)
+ void Import()
{
- throw Exception("Data is not a scenario.");
- }
- log_verbose("scenario classic_flag = 0x%02x\n", _s6.header.classic_flag);
+ Initialise();
- sawyercoding_read_chunk_safe(rw, &_s6.info, sizeof(_s6.info));
+ // _s6.header
+ gS6Info = _s6.info;
- // Read packed objects
- // TODO try to contain this more and not store objects until later
- for (uint16 i = 0; i < _s6.header.num_packed_objects; i++)
- {
- object_load_packed(rw);
+ gDateMonthsElapsed = _s6.elapsed_months;
+ gDateMonthTicks = _s6.current_day;
+ gScenarioTicks = _s6.scenario_ticks;
+ gScenarioSrand0 = _s6.scenario_srand_0;
+ gScenarioSrand1 = _s6.scenario_srand_1;
+
+ memcpy(gMapElements, _s6.map_elements, sizeof(_s6.map_elements));
+
+ gNextFreeMapElementPointerIndex = _s6.next_free_map_element_pointer_index;
+ for (sint32 i = 0; i < MAX_SPRITES; i++)
+ {
+ memcpy(get_sprite(i), &_s6.sprites[i], sizeof(rct_sprite));
+ }
+
+ for (sint32 i = 0; i < NUM_SPRITE_LISTS; i++)
+ {
+ gSpriteListHead[i] = _s6.sprite_lists_head[i];
+ gSpriteListCount[i] = _s6.sprite_lists_count[i];
+ }
+ gParkName = _s6.park_name;
+ // pad_013573D6
+ gParkNameArgs = _s6.park_name_args;
+ gInitialCash = _s6.initial_cash;
+ gBankLoan = _s6.current_loan;
+ gParkFlags = _s6.park_flags;
+ gParkEntranceFee = _s6.park_entrance_fee;
+ // rct1_park_entrance_x
+ // rct1_park_entrance_y
+ // pad_013573EE
+ // rct1_park_entrance_z
+ memcpy(gPeepSpawns, _s6.peep_spawns, sizeof(_s6.peep_spawns));
+ gGuestChangeModifier = _s6.guest_count_change_modifier;
+ gResearchFundingLevel = _s6.current_research_level;
+ // pad_01357400
+ memcpy(gResearchedRideTypes, _s6.researched_ride_types, sizeof(_s6.researched_ride_types));
+ memcpy(gResearchedRideEntries, _s6.researched_ride_entries, sizeof(_s6.researched_ride_entries));
+ memcpy(gResearchedTrackTypesA, _s6.researched_track_types_a, sizeof(_s6.researched_track_types_a));
+ memcpy(gResearchedTrackTypesB, _s6.researched_track_types_b, sizeof(_s6.researched_track_types_b));
+
+ gNumGuestsInPark = _s6.guests_in_park;
+ gNumGuestsHeadingForPark = _s6.guests_heading_for_park;
+
+ memcpy(gExpenditureTable, _s6.expenditure_table, sizeof(_s6.expenditure_table));
+
+ gNumGuestsInParkLastWeek = _s6.last_guests_in_park;
+ // pad_01357BCA
+ gStaffHandymanColour = _s6.handyman_colour;
+ gStaffMechanicColour = _s6.mechanic_colour;
+ gStaffSecurityColour = _s6.security_colour;
+
+ memcpy(gResearchedSceneryItems, _s6.researched_scenery_items, sizeof(_s6.researched_scenery_items));
+
+ gParkRating = _s6.park_rating;
+
+ memcpy(gParkRatingHistory, _s6.park_rating_history, sizeof(_s6.park_rating_history));
+ memcpy(gGuestsInParkHistory, _s6.guests_in_park_history, sizeof(_s6.guests_in_park_history));
+
+ gResearchPriorities = _s6.active_research_types;
+ gResearchProgressStage = _s6.research_progress_stage;
+ gResearchLastItemSubject = _s6.last_researched_item_subject;
+ // pad_01357CF8
+ gResearchNextItem = _s6.next_research_item;
+ gResearchProgress = _s6.research_progress;
+ gResearchNextCategory = _s6.next_research_category;
+ gResearchExpectedDay = _s6.next_research_expected_day;
+ gResearchExpectedMonth = _s6.next_research_expected_month;
+ gGuestInitialHappiness = _s6.guest_initial_happiness;
+ gParkSize = _s6.park_size;
+ _guestGenerationProbability = _s6.guest_generation_probability;
+ gTotalRideValue = _s6.total_ride_value;
+ gMaxBankLoan = _s6.maximum_loan;
+ gGuestInitialCash = _s6.guest_initial_cash;
+ gGuestInitialHunger = _s6.guest_initial_hunger;
+ gGuestInitialThirst = _s6.guest_initial_thirst;
+ gScenarioObjectiveType = _s6.objective_type;
+ gScenarioObjectiveYear = _s6.objective_year;
+ // pad_013580FA
+ gScenarioObjectiveCurrency = _s6.objective_currency;
+ gScenarioObjectiveNumGuests = _s6.objective_guests;
+ memcpy(gMarketingCampaignDaysLeft, _s6.campaign_weeks_left, sizeof(_s6.campaign_weeks_left));
+ memcpy(gMarketingCampaignRideIndex, _s6.campaign_ride_index, sizeof(_s6.campaign_ride_index));
+
+ memcpy(gCashHistory, _s6.balance_history, sizeof(_s6.balance_history));
+
+ gCurrentExpenditure = _s6.current_expenditure;
+ gCurrentProfit = _s6.current_profit;
+ gWeeklyProfitAverageDividend = _s6.weekly_profit_average_dividend;
+ gWeeklyProfitAverageDivisor = _s6.weekly_profit_average_divisor;
+ // pad_0135833A
+
+ memcpy(gWeeklyProfitHistory, _s6.weekly_profit_history, sizeof(_s6.weekly_profit_history));
+
+ gParkValue = _s6.park_value;
+
+ memcpy(gParkValueHistory, _s6.park_value_history, sizeof(_s6.park_value_history));
+
+ gScenarioCompletedCompanyValue = _s6.completed_company_value;
+ gTotalAdmissions = _s6.total_admissions;
+ gTotalIncomeFromAdmissions = _s6.income_from_admissions;
+ gCompanyValue = _s6.company_value;
+ memcpy(gPeepWarningThrottle, _s6.peep_warning_throttle, sizeof(_s6.peep_warning_throttle));
+
+ // Awards
+ for (sint32 i = 0; i < RCT12_MAX_AWARDS; i++)
+ {
+ rct12_award * src = &_s6.awards[i];
+ Award * dst = &gCurrentAwards[i];
+ dst->Time = src->time;
+ dst->Type = src->type;
+ }
+
+ gLandPrice = _s6.land_price;
+ gConstructionRightsPrice = _s6.construction_rights_price;
+ // unk_01358774
+ // pad_01358776
+ // _s6.cd_key
+ _gameVersion = _s6.game_version_number;
+ gScenarioCompanyValueRecord = _s6.completed_company_value_record;
+ // _s6.loan_hash;
+ gRideCount = _s6.ride_count;
+ // pad_013587CA
+ gHistoricalProfit = _s6.historical_profit;
+ // pad_013587D4
+ memcpy(gScenarioCompletedBy, _s6.scenario_completed_name, sizeof(_s6.scenario_completed_name));
+ gCashEncrypted = _s6.cash;
+ // pad_013587FC
+ gParkRatingCasualtyPenalty = _s6.park_rating_casualty_penalty;
+ gMapSizeUnits = _s6.map_size_units;
+ gMapSizeMinus2 = _s6.map_size_minus_2;
+ gMapSize = _s6.map_size;
+ gMapSizeMaxXY = _s6.map_max_xy;
+ gSamePriceThroughoutParkA = _s6.same_price_throughout;
+ _suggestedGuestMaximum = _s6.suggested_max_guests;
+ gScenarioParkRatingWarningDays = _s6.park_rating_warning_days;
+ gLastEntranceStyle = _s6.last_entrance_style;
+ // rct1_water_colour
+ // pad_01358842
+ memcpy(gResearchItems, _s6.research_items, sizeof(_s6.research_items));
+ gMapBaseZ = _s6.map_base_z;
+ memcpy(gScenarioName, _s6.scenario_name, sizeof(_s6.scenario_name));
+ memcpy(gScenarioDetails, _s6.scenario_description, sizeof(_s6.scenario_description));
+ gBankLoanInterestRate = _s6.current_interest_rate;
+ // pad_0135934B
+ gSamePriceThroughoutParkB = _s6.same_price_throughout_extended;
+ memcpy(gParkEntranceX, _s6.park_entrance_x, sizeof(_s6.park_entrance_x));
+ memcpy(gParkEntranceY, _s6.park_entrance_y, sizeof(_s6.park_entrance_y));
+ memcpy(gParkEntranceZ, _s6.park_entrance_z, sizeof(_s6.park_entrance_z));
+ memcpy(gParkEntranceDirection, _s6.park_entrance_direction, sizeof(_s6.park_entrance_direction));
+ scenario_set_filename(_s6.scenario_filename);
+ memcpy(gScenarioExpansionPacks, _s6.saved_expansion_pack_names, sizeof(_s6.saved_expansion_pack_names));
+ memcpy(gBanners, _s6.banners, sizeof(_s6.banners));
+ memcpy(gUserStrings, _s6.custom_strings, sizeof(_s6.custom_strings));
+ gCurrentTicks = _s6.game_ticks_1;
+ memcpy(gRideList, _s6.rides, sizeof(_s6.rides));
+ gSavedAge = _s6.saved_age;
+ gSavedViewX = _s6.saved_view_x;
+ gSavedViewY = _s6.saved_view_y;
+ gSavedViewZoom = _s6.saved_view_zoom;
+ gSavedViewRotation = _s6.saved_view_rotation;
+ memcpy(gAnimatedObjects, _s6.map_animations, sizeof(_s6.map_animations));
+ gNumMapAnimations = _s6.num_map_animations;
+ // pad_0138B582
+
+ gRideRatingsCalcData = _s6.ride_ratings_calc_data;
+ memcpy(gRideMeasurements, _s6.ride_measurements, sizeof(_s6.ride_measurements));
+ gNextGuestNumber = _s6.next_guest_index;
+ gGrassSceneryTileLoopPosition = _s6.grass_and_scenery_tilepos;
+ memcpy(gStaffPatrolAreas, _s6.patrol_areas, sizeof(_s6.patrol_areas));
+ memcpy(gStaffModes, _s6.staff_modes, sizeof(_s6.staff_modes));
+ // unk_13CA73E
+ // pad_13CA73F
+ gUnk13CA740 = _s6.byte_13CA740;
+ gClimate = _s6.climate;
+ // pad_13CA741;
+ // byte_13CA742
+ // pad_013CA747
+ gClimateUpdateTimer = _s6.climate_update_timer;
+ gClimateCurrentWeather = _s6.current_weather;
+ gClimateNextWeather = _s6.next_weather;
+ gClimateCurrentTemperature = _s6.temperature;
+ gClimateNextTemperature = _s6.next_temperature;
+ gClimateCurrentWeatherEffect = _s6.current_weather_effect;
+ gClimateNextWeatherEffect = _s6.next_weather_effect;
+ gClimateCurrentWeatherGloom = _s6.current_weather_gloom;
+ gClimateNextWeatherGloom = _s6.next_weather_gloom;
+ gClimateCurrentRainLevel = _s6.current_rain_level;
+ gClimateNextRainLevel = _s6.next_rain_level;
+
+ // News items
+ for (size_t i = 0; i < RCT12_MAX_NEWS_ITEMS; i++)
+ {
+ const rct12_news_item * src = &_s6.news_items[i];
+ NewsItem * dst = &gNewsItems[i];
+
+ dst->Type = src->Type;
+ dst->Flags = src->Flags;
+ dst->Assoc = src->Assoc;
+ dst->Ticks = src->Ticks;
+ dst->MonthYear = src->MonthYear;
+ dst->Day = src->Day;
+ memcpy(dst->Text, src->Text, sizeof(src->Text));
+ }
+
+ // pad_13CE730
+ // rct1_scenario_flags
+ gWidePathTileLoopX = _s6.wide_path_tile_loop_x;
+ gWidePathTileLoopY = _s6.wide_path_tile_loop_y;
+ // pad_13CE778
+
+ // Fix and set dynamic variables
+ if (!object_load_entries(_s6.objects))
+ {
+ throw ObjectLoadException();
+ }
+ map_strip_ghost_flag_from_elements();
+ map_update_tile_pointers();
+ game_convert_strings_to_utf8();
+ map_count_remaining_land_rights();
}
- sawyercoding_read_chunk_safe(rw, &_s6.objects, sizeof(_s6.objects));
- sawyercoding_read_chunk_safe(rw, &_s6.elapsed_months, 16);
- sawyercoding_read_chunk_safe(rw, &_s6.map_elements, sizeof(_s6.map_elements));
- sawyercoding_read_chunk_safe(rw, &_s6.next_free_map_element_pointer_index, 2560076);
- sawyercoding_read_chunk_safe(rw, &_s6.guests_in_park, 4);
- sawyercoding_read_chunk_safe(rw, &_s6.last_guests_in_park, 8);
- sawyercoding_read_chunk_safe(rw, &_s6.park_rating, 2);
- sawyercoding_read_chunk_safe(rw, &_s6.active_research_types, 1082);
- sawyercoding_read_chunk_safe(rw, &_s6.current_expenditure, 16);
- sawyercoding_read_chunk_safe(rw, &_s6.park_value, 4);
- sawyercoding_read_chunk_safe(rw, &_s6.completed_company_value, 483816);
-}
-
-void S6Importer::Import()
-{
- Initialise();
-
- // _s6.header
- gS6Info = _s6.info;
-
- gDateMonthsElapsed = _s6.elapsed_months;
- gDateMonthTicks = _s6.current_day;
- gScenarioTicks = _s6.scenario_ticks;
- gScenarioSrand0 = _s6.scenario_srand_0;
- gScenarioSrand1 = _s6.scenario_srand_1;
-
- memcpy(gMapElements, _s6.map_elements, sizeof(_s6.map_elements));
-
- gNextFreeMapElementPointerIndex = _s6.next_free_map_element_pointer_index;
- for (sint32 i = 0; i < MAX_SPRITES; i++)
+ void Initialise()
{
- memcpy(get_sprite(i), &_s6.sprites[i], sizeof(rct_sprite));
+ game_init_all(_s6.map_size);
}
-
- for (sint32 i = 0; i < NUM_SPRITE_LISTS; i++)
- {
- gSpriteListHead[i] = _s6.sprite_lists_head[i];
- gSpriteListCount[i] = _s6.sprite_lists_count[i];
- }
- gParkName = _s6.park_name;
- // pad_013573D6
- gParkNameArgs = _s6.park_name_args;
- gInitialCash = _s6.initial_cash;
- gBankLoan = _s6.current_loan;
- gParkFlags = _s6.park_flags;
- gParkEntranceFee = _s6.park_entrance_fee;
- // rct1_park_entrance_x
- // rct1_park_entrance_y
- // pad_013573EE
- // rct1_park_entrance_z
- memcpy(gPeepSpawns, _s6.peep_spawns, sizeof(_s6.peep_spawns));
- gGuestChangeModifier = _s6.guest_count_change_modifier;
- gResearchFundingLevel = _s6.current_research_level;
- // pad_01357400
- memcpy(gResearchedRideTypes, _s6.researched_ride_types, sizeof(_s6.researched_ride_types));
- memcpy(gResearchedRideEntries, _s6.researched_ride_entries, sizeof(_s6.researched_ride_entries));
- memcpy(gResearchedTrackTypesA, _s6.researched_track_types_a, sizeof(_s6.researched_track_types_a));
- memcpy(gResearchedTrackTypesB, _s6.researched_track_types_b, sizeof(_s6.researched_track_types_b));
-
- gNumGuestsInPark = _s6.guests_in_park;
- gNumGuestsHeadingForPark = _s6.guests_heading_for_park;
-
- memcpy(gExpenditureTable, _s6.expenditure_table, sizeof(_s6.expenditure_table));
-
- gNumGuestsInParkLastWeek = _s6.last_guests_in_park;
- // pad_01357BCA
- gStaffHandymanColour = _s6.handyman_colour;
- gStaffMechanicColour = _s6.mechanic_colour;
- gStaffSecurityColour = _s6.security_colour;
-
- memcpy(gResearchedSceneryItems, _s6.researched_scenery_items, sizeof(_s6.researched_scenery_items));
-
- gParkRating = _s6.park_rating;
-
- memcpy(gParkRatingHistory, _s6.park_rating_history, sizeof(_s6.park_rating_history));
- memcpy(gGuestsInParkHistory, _s6.guests_in_park_history, sizeof(_s6.guests_in_park_history));
-
- gResearchPriorities = _s6.active_research_types;
- gResearchProgressStage = _s6.research_progress_stage;
- gResearchLastItemSubject = _s6.last_researched_item_subject;
- // pad_01357CF8
- gResearchNextItem = _s6.next_research_item;
- gResearchProgress = _s6.research_progress;
- gResearchNextCategory = _s6.next_research_category;
- gResearchExpectedDay = _s6.next_research_expected_day;
- gResearchExpectedMonth = _s6.next_research_expected_month;
- gGuestInitialHappiness = _s6.guest_initial_happiness;
- gParkSize = _s6.park_size;
- _guestGenerationProbability = _s6.guest_generation_probability;
- gTotalRideValue = _s6.total_ride_value;
- gMaxBankLoan = _s6.maximum_loan;
- gGuestInitialCash = _s6.guest_initial_cash;
- gGuestInitialHunger = _s6.guest_initial_hunger;
- gGuestInitialThirst = _s6.guest_initial_thirst;
- gScenarioObjectiveType = _s6.objective_type;
- gScenarioObjectiveYear = _s6.objective_year;
- // pad_013580FA
- gScenarioObjectiveCurrency = _s6.objective_currency;
- gScenarioObjectiveNumGuests = _s6.objective_guests;
- memcpy(gMarketingCampaignDaysLeft, _s6.campaign_weeks_left, sizeof(_s6.campaign_weeks_left));
- memcpy(gMarketingCampaignRideIndex, _s6.campaign_ride_index, sizeof(_s6.campaign_ride_index));
-
- memcpy(gCashHistory, _s6.balance_history, sizeof(_s6.balance_history));
-
- gCurrentExpenditure = _s6.current_expenditure;
- gCurrentProfit = _s6.current_profit;
- gWeeklyProfitAverageDividend = _s6.weekly_profit_average_dividend;
- gWeeklyProfitAverageDivisor = _s6.weekly_profit_average_divisor;
- // pad_0135833A
-
- memcpy(gWeeklyProfitHistory, _s6.weekly_profit_history, sizeof(_s6.weekly_profit_history));
-
- gParkValue = _s6.park_value;
-
- memcpy(gParkValueHistory, _s6.park_value_history, sizeof(_s6.park_value_history));
-
- gScenarioCompletedCompanyValue = _s6.completed_company_value;
- gTotalAdmissions = _s6.total_admissions;
- gTotalIncomeFromAdmissions = _s6.income_from_admissions;
- gCompanyValue = _s6.company_value;
- memcpy(gPeepWarningThrottle, _s6.peep_warning_throttle, sizeof(_s6.peep_warning_throttle));
-
- // Awards
- for (sint32 i = 0; i < RCT12_MAX_AWARDS; i++)
- {
- rct12_award * src = &_s6.awards[i];
- Award * dst = &gCurrentAwards[i];
- dst->Time = src->time;
- dst->Type = src->type;
- }
-
- gLandPrice = _s6.land_price;
- gConstructionRightsPrice = _s6.construction_rights_price;
- // unk_01358774
- // pad_01358776
- // _s6.cd_key
- _gameVersion = _s6.game_version_number;
- gScenarioCompanyValueRecord = _s6.completed_company_value_record;
- // _s6.loan_hash;
- gRideCount = _s6.ride_count;
- // pad_013587CA
- gHistoricalProfit = _s6.historical_profit;
- // pad_013587D4
- memcpy(gScenarioCompletedBy, _s6.scenario_completed_name, sizeof(_s6.scenario_completed_name));
- gCashEncrypted = _s6.cash;
- // pad_013587FC
- gParkRatingCasualtyPenalty = _s6.park_rating_casualty_penalty;
- gMapSizeUnits = _s6.map_size_units;
- gMapSizeMinus2 = _s6.map_size_minus_2;
- gMapSize = _s6.map_size;
- gMapSizeMaxXY = _s6.map_max_xy;
- gSamePriceThroughoutParkA = _s6.same_price_throughout;
- _suggestedGuestMaximum = _s6.suggested_max_guests;
- gScenarioParkRatingWarningDays = _s6.park_rating_warning_days;
- gLastEntranceStyle = _s6.last_entrance_style;
- // rct1_water_colour
- // pad_01358842
- memcpy(gResearchItems, _s6.research_items, sizeof(_s6.research_items));
- gMapBaseZ = _s6.map_base_z;
- memcpy(gScenarioName, _s6.scenario_name, sizeof(_s6.scenario_name));
- memcpy(gScenarioDetails, _s6.scenario_description, sizeof(_s6.scenario_description));
- gBankLoanInterestRate = _s6.current_interest_rate;
- // pad_0135934B
- gSamePriceThroughoutParkB = _s6.same_price_throughout_extended;
- memcpy(gParkEntranceX, _s6.park_entrance_x, sizeof(_s6.park_entrance_x));
- memcpy(gParkEntranceY, _s6.park_entrance_y, sizeof(_s6.park_entrance_y));
- memcpy(gParkEntranceZ, _s6.park_entrance_z, sizeof(_s6.park_entrance_z));
- memcpy(gParkEntranceDirection, _s6.park_entrance_direction, sizeof(_s6.park_entrance_direction));
- scenario_set_filename(_s6.scenario_filename);
- memcpy(gScenarioExpansionPacks, _s6.saved_expansion_pack_names, sizeof(_s6.saved_expansion_pack_names));
- memcpy(gBanners, _s6.banners, sizeof(_s6.banners));
- memcpy(gUserStrings, _s6.custom_strings, sizeof(_s6.custom_strings));
- gCurrentTicks = _s6.game_ticks_1;
- memcpy(gRideList, _s6.rides, sizeof(_s6.rides));
- gSavedAge = _s6.saved_age;
- gSavedViewX = _s6.saved_view_x;
- gSavedViewY = _s6.saved_view_y;
- gSavedViewZoom = _s6.saved_view_zoom;
- gSavedViewRotation = _s6.saved_view_rotation;
- memcpy(gAnimatedObjects, _s6.map_animations, sizeof(_s6.map_animations));
- gNumMapAnimations = _s6.num_map_animations;
- // pad_0138B582
-
- gRideRatingsCalcData = _s6.ride_ratings_calc_data;
- memcpy(gRideMeasurements, _s6.ride_measurements, sizeof(_s6.ride_measurements));
- gNextGuestNumber = _s6.next_guest_index;
- gGrassSceneryTileLoopPosition = _s6.grass_and_scenery_tilepos;
- memcpy(gStaffPatrolAreas, _s6.patrol_areas, sizeof(_s6.patrol_areas));
- memcpy(gStaffModes, _s6.staff_modes, sizeof(_s6.staff_modes));
- // unk_13CA73E
- // pad_13CA73F
- gUnk13CA740 = _s6.byte_13CA740;
- gClimate = _s6.climate;
- // pad_13CA741;
- // byte_13CA742
- // pad_013CA747
- gClimateUpdateTimer = _s6.climate_update_timer;
- gClimateCurrentWeather = _s6.current_weather;
- gClimateNextWeather = _s6.next_weather;
- gClimateCurrentTemperature = _s6.temperature;
- gClimateNextTemperature = _s6.next_temperature;
- gClimateCurrentWeatherEffect = _s6.current_weather_effect;
- gClimateNextWeatherEffect = _s6.next_weather_effect;
- gClimateCurrentWeatherGloom = _s6.current_weather_gloom;
- gClimateNextWeatherGloom = _s6.next_weather_gloom;
- gClimateCurrentRainLevel = _s6.current_rain_level;
- gClimateNextRainLevel = _s6.next_rain_level;
-
- // News items
- for (size_t i = 0; i < RCT12_MAX_NEWS_ITEMS; i++)
- {
- const rct12_news_item * src = &_s6.news_items[i];
- NewsItem * dst = &gNewsItems[i];
-
- dst->Type = src->Type;
- dst->Flags = src->Flags;
- dst->Assoc = src->Assoc;
- dst->Ticks = src->Ticks;
- dst->MonthYear = src->MonthYear;
- dst->Day = src->Day;
- memcpy(dst->Text, src->Text, sizeof(src->Text));
- }
-
- // pad_13CE730
- // rct1_scenario_flags
- gWidePathTileLoopX = _s6.wide_path_tile_loop_x;
- gWidePathTileLoopY = _s6.wide_path_tile_loop_y;
- // pad_13CE778
-
- // Fix and set dynamic variables
- if (!object_load_entries(_s6.objects))
- {
- throw ObjectLoadException();
- }
- map_strip_ghost_flag_from_elements();
- map_update_tile_pointers();
- game_convert_strings_to_utf8();
- map_count_remaining_land_rights();
- if (FixIssues)
- {
- game_fix_save_vars();
- }
-}
-
-void S6Importer::Initialise()
-{
- game_init_all(_s6.map_size);
-}
+};
extern "C"
{
@@ -413,13 +425,14 @@ extern "C"
}
bool result = false;
+ auto stream = FileStream(rw, FILE_MODE_OPEN);
auto s6Importer = new S6Importer();
try
{
- s6Importer->FixIssues = true;
- s6Importer->LoadSavedGame(rw);
+ s6Importer->LoadFromStream(&stream, false);
s6Importer->Import();
+ game_fix_save_vars();
sprite_position_tween_reset();
result = true;
}
@@ -443,10 +456,10 @@ extern "C"
auto s6Importer = new S6Importer();
try
{
- s6Importer->FixIssues = true;
s6Importer->LoadSavedGame(path);
s6Importer->Import();
+ game_fix_save_vars();
sprite_position_tween_reset();
result = true;
}
@@ -475,13 +488,14 @@ extern "C"
bool scenario_load_rw(SDL_RWops * rw)
{
bool result = false;
+ auto stream = FileStream(rw, FILE_MODE_OPEN);
auto s6Importer = new S6Importer();
try
{
- s6Importer->FixIssues = true;
- s6Importer->LoadScenario(rw);
+ s6Importer->LoadFromStream(&stream, true);
s6Importer->Import();
+ game_fix_save_vars();
sprite_position_tween_reset();
result = true;
}
@@ -518,10 +532,10 @@ extern "C"
auto s6Importer = new S6Importer();
try
{
- s6Importer->FixIssues = true;
s6Importer->LoadScenario(path);
s6Importer->Import();
+ game_fix_save_vars();
sprite_position_tween_reset();
result = true;
}
@@ -550,10 +564,11 @@ extern "C"
sint32 game_load_network(SDL_RWops* rw)
{
bool result = false;
+ auto stream = FileStream(rw, FILE_MODE_OPEN);
auto s6Importer = new S6Importer();
try
{
- s6Importer->LoadSavedGame(rw);
+ s6Importer->LoadFromStream(&stream, false);
s6Importer->Import();
sprite_position_tween_reset();
diff --git a/src/openrct2/rct2/S6Importer.h b/src/openrct2/rct2/S6Importer.h
deleted file mode 100644
index 29e1f0f423..0000000000
--- a/src/openrct2/rct2/S6Importer.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
-/*****************************************************************************
- * OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
- *
- * OpenRCT2 is the work of many authors, a full list can be found in contributors.md
- * For more information, visit https://github.com/OpenRCT2/OpenRCT2
- *
- * OpenRCT2 is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * A full copy of the GNU General Public License can be found in licence.txt
- *****************************************************************************/
-#pragma endregion
-
-#pragma once
-
-#include "../common.h"
-
-extern "C"
-{
- #include "../scenario/scenario.h"
-}
-
-/**
- * Class to import RollerCoaster Tycoon 2 scenarios (*.SC6) and saved games (*.SV6).
- */
-class S6Importer final
-{
-public:
- bool FixIssues;
-
- S6Importer();
-
- void LoadSavedGame(const utf8 * path);
- void LoadSavedGame(SDL_RWops *rw);
- void LoadScenario(const utf8 * path);
- void LoadScenario(SDL_RWops *rw);
- void Import();
-
-private:
- const utf8 * _s6Path = nullptr;
- rct_s6_data _s6;
- uint8 _gameVersion = 0;
-
- void Initialise();
-};