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(); -};