1 /*****************************************************************************
2  * Copyright (c) 2014-2020 OpenRCT2 developers
3  *
4  * For a complete list of all authors, please refer to contributors.md
5  * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6  *
7  * OpenRCT2 is licensed under the GNU General Public License version 3.
8  *****************************************************************************/
9 
10 #include "../Context.h"
11 #include "../Diagnostic.h"
12 #include "../Editor.h"
13 #include "../Game.h"
14 #include "../GameState.h"
15 #include "../OpenRCT2.h"
16 #include "../ParkImporter.h"
17 #include "../config/Config.h"
18 #include "../core/Console.hpp"
19 #include "../core/FileStream.h"
20 #include "../core/IStream.hpp"
21 #include "../core/Numerics.hpp"
22 #include "../core/Path.hpp"
23 #include "../core/Random.hpp"
24 #include "../core/String.hpp"
25 #include "../interface/Viewport.h"
26 #include "../localisation/Date.h"
27 #include "../localisation/Localisation.h"
28 #include "../management/Award.h"
29 #include "../management/Finance.h"
30 #include "../management/Marketing.h"
31 #include "../management/NewsItem.h"
32 #include "../management/Research.h"
33 #include "../network/network.h"
34 #include "../object/ObjectLimits.h"
35 #include "../object/ObjectList.h"
36 #include "../object/ObjectManager.h"
37 #include "../object/ObjectRepository.h"
38 #include "../peep/Guest.h"
39 #include "../peep/RideUseSystem.h"
40 #include "../peep/Staff.h"
41 #include "../rct12/RCT12.h"
42 #include "../rct12/SawyerChunkReader.h"
43 #include "../rct12/SawyerEncoding.h"
44 #include "../rct2/RCT2.h"
45 #include "../ride/Ride.h"
46 #include "../ride/RideData.h"
47 #include "../ride/RideRatings.h"
48 #include "../ride/ShopItem.h"
49 #include "../ride/Station.h"
50 #include "../ride/Track.h"
51 #include "../ride/TrainManager.h"
52 #include "../ride/Vehicle.h"
53 #include "../scenario/Scenario.h"
54 #include "../scenario/ScenarioRepository.h"
55 #include "../util/SawyerCoding.h"
56 #include "../util/Util.h"
57 #include "../world/Balloon.h"
58 #include "../world/Climate.h"
59 #include "../world/Duck.h"
60 #include "../world/EntityList.h"
61 #include "../world/EntityTweener.h"
62 #include "../world/Entrance.h"
63 #include "../world/Fountain.h"
64 #include "../world/Litter.h"
65 #include "../world/MapAnimation.h"
66 #include "../world/MoneyEffect.h"
67 #include "../world/Park.h"
68 #include "../world/Particle.h"
69 #include "../world/Scenery.h"
70 #include "../world/Sprite.h"
71 #include "../world/Surface.h"
72 
73 #include <algorithm>
74 #include <bitset>
75 
76 #define DECRYPT_MONEY(money) (static_cast<money32>(Numerics::rol32((money) ^ 0xF4EC9621, 13)))
77 
78 /**
79  * Class to import RollerCoaster Tycoon 2 scenarios (*.SC6) and saved games (*.SV6).
80  */
81 class S6Importer final : public IParkImporter
82 {
83 private:
84     IObjectRepository& _objectRepository;
85 
86     const utf8* _s6Path = nullptr;
87     rct_s6_data _s6{};
88     uint8_t _gameVersion = 0;
89     bool _isSV7 = false;
90     std::bitset<RCT12_MAX_RIDES_IN_PARK> _isFlatRide{};
91 
92 public:
S6Importer(IObjectRepository & objectRepository)93     S6Importer(IObjectRepository& objectRepository)
94         : _objectRepository(objectRepository)
95     {
96     }
97 
Load(const utf8 * path)98     ParkLoadResult Load(const utf8* path) override
99     {
100         const utf8* extension = Path::GetExtension(path);
101         if (String::Equals(extension, ".sc6", true))
102         {
103             return LoadScenario(path);
104         }
105         if (String::Equals(extension, ".sv6", true))
106         {
107             return LoadSavedGame(path);
108         }
109 
110         throw std::runtime_error("Invalid RCT2 park extension.");
111     }
112 
LoadSavedGame(const utf8 * path,bool skipObjectCheck=false)113     ParkLoadResult LoadSavedGame(const utf8* path, bool skipObjectCheck = false) override
114     {
115         auto fs = OpenRCT2::FileStream(path, OpenRCT2::FILE_MODE_OPEN);
116         auto result = LoadFromStream(&fs, false, skipObjectCheck);
117         _s6Path = path;
118         return result;
119     }
120 
LoadScenario(const utf8 * path,bool skipObjectCheck=false)121     ParkLoadResult LoadScenario(const utf8* path, bool skipObjectCheck = false) override
122     {
123         auto fs = OpenRCT2::FileStream(path, OpenRCT2::FILE_MODE_OPEN);
124         auto result = LoadFromStream(&fs, true, skipObjectCheck);
125         _s6Path = path;
126         return result;
127     }
128 
LoadFromStream(OpenRCT2::IStream * stream,bool isScenario,bool skipObjectCheck=false,const utf8 * path=String::Empty)129     ParkLoadResult LoadFromStream(
130         OpenRCT2::IStream* stream, bool isScenario, [[maybe_unused]] bool skipObjectCheck = false,
131         const utf8* path = String::Empty) override
132     {
133         if (isScenario && !gConfigGeneral.allow_loading_with_incorrect_checksum && !SawyerEncoding::ValidateChecksum(stream))
134         {
135             throw IOException("Invalid checksum.");
136         }
137 
138         auto chunkReader = SawyerChunkReader(stream);
139         chunkReader.ReadChunk(&_s6.header, sizeof(_s6.header));
140 
141         log_verbose("saved game classic_flag = 0x%02x", _s6.header.classic_flag);
142         if (isScenario)
143         {
144             if (_s6.header.type != S6_TYPE_SCENARIO)
145             {
146                 throw std::runtime_error("Park is not a scenario.");
147             }
148             chunkReader.ReadChunk(&_s6.info, sizeof(_s6.info));
149         }
150         else
151         {
152             if (_s6.header.type != S6_TYPE_SAVEDGAME)
153             {
154                 throw std::runtime_error("Park is not a saved game.");
155             }
156         }
157 
158         if (_s6.header.classic_flag == 0xf)
159         {
160             throw UnsupportedRCTCFlagException(_s6.header.classic_flag);
161         }
162 
163         // Read packed objects
164         // TODO try to contain this more and not store objects until later
165         for (uint16_t i = 0; i < _s6.header.num_packed_objects; i++)
166         {
167             _objectRepository.ExportPackedObject(stream);
168         }
169 
170         if (path)
171         {
172             auto extension = path_get_extension(path);
173             _isSV7 = _stricmp(extension, ".sv7") == 0;
174         }
175 
176         chunkReader.ReadChunk(&_s6.Objects, sizeof(_s6.Objects));
177 
178         if (isScenario)
179         {
180             chunkReader.ReadChunk(&_s6.elapsed_months, 16);
181             chunkReader.ReadChunk(&_s6.tile_elements, sizeof(_s6.tile_elements));
182             chunkReader.ReadChunk(&_s6.next_free_tile_element_pointer_index, 2560076);
183             chunkReader.ReadChunk(&_s6.guests_in_park, 4);
184             chunkReader.ReadChunk(&_s6.last_guests_in_park, 8);
185             chunkReader.ReadChunk(&_s6.park_rating, 2);
186             chunkReader.ReadChunk(&_s6.active_research_types, 1082);
187             chunkReader.ReadChunk(&_s6.current_expenditure, 16);
188             chunkReader.ReadChunk(&_s6.park_value, 4);
189             chunkReader.ReadChunk(&_s6.completed_company_value, 483816);
190         }
191         else
192         {
193             chunkReader.ReadChunk(&_s6.elapsed_months, 16);
194             chunkReader.ReadChunk(&_s6.tile_elements, sizeof(_s6.tile_elements));
195             chunkReader.ReadChunk(&_s6.next_free_tile_element_pointer_index, 3048816);
196         }
197 
198         _s6Path = path;
199 
200         return ParkLoadResult(GetRequiredObjects());
201     }
202 
GetDetails(scenario_index_entry * dst)203     bool GetDetails(scenario_index_entry* dst) override
204     {
205         *dst = {};
206         return false;
207     }
208 
Import()209     void Import() override
210     {
211         Initialise();
212 
213         gEditorStep = _s6.info.editor_step;
214         gScenarioCategory = static_cast<SCENARIO_CATEGORY>(_s6.info.category);
215 
216         // Some scenarios have their scenario details in UTF-8, due to earlier bugs in OpenRCT2.
217         auto loadMaybeUTF8 = [](std::string_view str) -> std::string {
218             return !IsLikelyUTF8(str) ? rct2_to_utf8(str, RCT2LanguageId::EnglishUK) : std::string(str);
219         };
220 
221         if (_s6.header.type == S6_TYPE_SCENARIO)
222         {
223             gScenarioName = loadMaybeUTF8(_s6.info.name);
224             gScenarioDetails = loadMaybeUTF8(_s6.info.details);
225         }
226         else
227         {
228             // Saved games do not have an info chunk
229             gScenarioName = loadMaybeUTF8(_s6.scenario_name);
230             gScenarioDetails = loadMaybeUTF8(_s6.scenario_description);
231         }
232 
233         gDateMonthsElapsed = static_cast<int32_t>(_s6.elapsed_months);
234         gDateMonthTicks = _s6.current_day;
235         gCurrentTicks = _s6.game_ticks_1;
236 
237         scenario_rand_seed(_s6.scenario_srand_0, _s6.scenario_srand_1);
238 
239         DetermineFlatRideStatus();
240         ImportTileElements();
241         ImportEntities();
242 
243         gInitialCash = ToMoney64(_s6.initial_cash);
244         gBankLoan = ToMoney64(_s6.current_loan);
245         gParkFlags = _s6.park_flags;
246         gParkEntranceFee = _s6.park_entrance_fee;
247         // rct1_park_entrance_x
248         // rct1_park_entrance_y
249         // pad_013573EE
250         // rct1_park_entrance_z
251 
252         ImportPeepSpawns();
253 
254         gGuestChangeModifier = _s6.guest_count_change_modifier;
255         gResearchFundingLevel = _s6.current_research_level;
256         // pad_01357400
257         ImportResearchedRideTypes();
258         ImportResearchedRideEntries();
259         // _s6.researched_track_types_a
260         // _s6.researched_track_types_b
261 
262         gNumGuestsInPark = _s6.guests_in_park;
263         gNumGuestsHeadingForPark = _s6.guests_heading_for_park;
264 
265         for (size_t i = 0; i < RCT12_EXPENDITURE_TABLE_MONTH_COUNT; i++)
266         {
267             for (size_t j = 0; j < RCT12_EXPENDITURE_TYPE_COUNT; j++)
268             {
269                 gExpenditureTable[i][j] = ToMoney64(_s6.expenditure_table[i][j]);
270             }
271         }
272 
273         gNumGuestsInParkLastWeek = _s6.last_guests_in_park;
274         // pad_01357BCA
275         gStaffHandymanColour = _s6.handyman_colour;
276         gStaffMechanicColour = _s6.mechanic_colour;
277         gStaffSecurityColour = _s6.security_colour;
278 
279         ImportResearchedSceneryItems();
280 
281         gParkRating = _s6.park_rating;
282 
283         auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark();
284         park.ResetHistories();
285         std::copy(std::begin(_s6.park_rating_history), std::end(_s6.park_rating_history), gParkRatingHistory);
286         for (size_t i = 0; i < std::size(_s6.guests_in_park_history); i++)
287         {
288             if (_s6.guests_in_park_history[i] != RCT12ParkHistoryUndefined)
289             {
290                 gGuestsInParkHistory[i] = _s6.guests_in_park_history[i] * RCT12GuestsInParkHistoryFactor;
291             }
292         }
293 
294         gResearchPriorities = _s6.active_research_types;
295         gResearchProgressStage = _s6.research_progress_stage;
296         if (_s6.last_researched_item_subject != RCT12_RESEARCHED_ITEMS_SEPARATOR)
297             gResearchLastItem = ResearchItem(
298                 RCT12ResearchItem{ _s6.last_researched_item_subject, EnumValue(ResearchCategory::Transport) });
299         else
300             gResearchLastItem = std::nullopt;
301         // pad_01357CF8
302         if (_s6.next_research_item != RCT12_RESEARCHED_ITEMS_SEPARATOR)
303             gResearchNextItem = ResearchItem(RCT12ResearchItem{ _s6.next_research_item, _s6.next_research_category });
304         else
305             gResearchNextItem = std::nullopt;
306 
307         gResearchProgress = _s6.research_progress;
308         gResearchExpectedDay = _s6.next_research_expected_day;
309         gResearchExpectedMonth = _s6.next_research_expected_month;
310         gGuestInitialHappiness = _s6.guest_initial_happiness;
311         gParkSize = _s6.park_size;
312         _guestGenerationProbability = _s6.guest_generation_probability;
313         gTotalRideValueForMoney = _s6.total_ride_value_for_money;
314         gMaxBankLoan = ToMoney64(_s6.maximum_loan);
315         gGuestInitialCash = _s6.guest_initial_cash;
316         gGuestInitialHunger = _s6.guest_initial_hunger;
317         gGuestInitialThirst = _s6.guest_initial_thirst;
318         gScenarioObjective.Type = _s6.objective_type;
319         gScenarioObjective.Year = _s6.objective_year;
320         // pad_013580FA
321         gScenarioObjective.Currency = _s6.objective_currency;
322         // In RCT2, the ride string IDs start at index STR_0002 and are directly mappable.
323         // This is not always the case in OpenRCT2, so we use the actual ride ID.
324         if (gScenarioObjective.Type == OBJECTIVE_BUILD_THE_BEST)
325             gScenarioObjective.RideId = _s6.objective_guests - RCT2_RIDE_STRING_START;
326         else
327             gScenarioObjective.NumGuests = _s6.objective_guests;
328         ImportMarketingCampaigns();
329 
330         gCurrentExpenditure = ToMoney64(_s6.current_expenditure);
331         gCurrentProfit = ToMoney64(_s6.current_profit);
332         gWeeklyProfitAverageDividend = ToMoney64(_s6.weekly_profit_average_dividend);
333         gWeeklyProfitAverageDivisor = _s6.weekly_profit_average_divisor;
334         // pad_0135833A
335 
336         gParkValue = ToMoney64(_s6.park_value);
337 
338         for (size_t i = 0; i < RCT12_FINANCE_GRAPH_SIZE; i++)
339         {
340             gCashHistory[i] = ToMoney64(_s6.balance_history[i]);
341             gWeeklyProfitHistory[i] = ToMoney64(_s6.weekly_profit_history[i]);
342             gParkValueHistory[i] = ToMoney64(_s6.park_value_history[i]);
343         }
344 
345         gScenarioCompletedCompanyValue = RCT12CompletedCompanyValueToOpenRCT2(_s6.completed_company_value);
346         gTotalAdmissions = _s6.total_admissions;
347         gTotalIncomeFromAdmissions = ToMoney64(_s6.income_from_admissions);
348         gCompanyValue = ToMoney64(_s6.company_value);
349         std::memcpy(gPeepWarningThrottle, _s6.peep_warning_throttle, sizeof(_s6.peep_warning_throttle));
350 
351         // Awards
352         for (int32_t i = 0; i < RCT12_MAX_AWARDS; i++)
353         {
354             rct12_award* src = &_s6.awards[i];
355             Award* dst = &gCurrentAwards[i];
356             dst->Time = src->time;
357             dst->Type = src->type;
358         }
359 
360         gLandPrice = _s6.land_price;
361         gConstructionRightsPrice = _s6.construction_rights_price;
362         // unk_01358774
363         // pad_01358776
364         // _s6.cd_key
365         _gameVersion = _s6.game_version_number;
366         gScenarioCompanyValueRecord = _s6.completed_company_value_record;
367         // _s6.loan_hash;
368         // pad_013587CA
369         gHistoricalProfit = ToMoney64(_s6.historical_profit);
370         // pad_013587D4
371         gScenarioCompletedBy = std::string_view(_s6.scenario_completed_name, sizeof(_s6.scenario_completed_name));
372         gCash = ToMoney64(DECRYPT_MONEY(_s6.cash));
373         // pad_013587FC
374         gParkRatingCasualtyPenalty = _s6.park_rating_casualty_penalty;
375         gMapSize = _s6.map_size;
376         gSamePriceThroughoutPark = _s6.same_price_throughout
377             | (static_cast<uint64_t>(_s6.same_price_throughout_extended) << 32);
378         _suggestedGuestMaximum = _s6.suggested_max_guests;
379         gScenarioParkRatingWarningDays = _s6.park_rating_warning_days;
380         gLastEntranceStyle = _s6.last_entrance_style;
381         // rct1_water_colour
382         // pad_01358842
383         ImportResearchList();
384         gMapBaseZ = _s6.map_base_z;
385         gBankLoanInterestRate = _s6.current_interest_rate;
386         // pad_0135934B
387         // Preserve compatibility with vanilla RCT2's save format.
388         gParkEntrances.clear();
389         for (uint8_t i = 0; i < RCT12_MAX_PARK_ENTRANCES; i++)
390         {
391             if (_s6.park_entrance_x[i] != LOCATION_NULL)
392             {
393                 CoordsXYZD entrance;
394                 entrance.x = _s6.park_entrance_x[i];
395                 entrance.y = _s6.park_entrance_y[i];
396                 entrance.z = _s6.park_entrance_z[i];
397                 entrance.direction = _s6.park_entrance_direction[i];
398                 gParkEntrances.push_back(entrance);
399             }
400         }
401         if (_s6.header.type == S6_TYPE_SCENARIO)
402         {
403             // _s6.scenario_filename is wrong for some RCT2 expansion scenarios, so we use the real filename
404             String::Set(gScenarioFileName, sizeof(gScenarioFileName), Path::GetFileName(_s6Path));
405         }
406         else
407         {
408             // For savegames the filename can be arbitrary, so we have no choice but to rely on the name provided
409             String::Set(gScenarioFileName, sizeof(gScenarioFileName), _s6.scenario_filename);
410         }
411         std::memcpy(gScenarioExpansionPacks, _s6.saved_expansion_pack_names, sizeof(_s6.saved_expansion_pack_names));
412         gCurrentRealTimeTicks = 0;
413 
414         ImportRides();
415 
416         gSavedAge = _s6.saved_age;
417         gSavedView = ScreenCoordsXY{ _s6.saved_view_x, _s6.saved_view_y };
418         gSavedViewZoom = _s6.saved_view_zoom;
419         gSavedViewRotation = _s6.saved_view_rotation;
420 
421         ImportRideRatingsCalcData();
422         ImportRideMeasurements();
423         gNextGuestNumber = _s6.next_guest_index;
424         gGrassSceneryTileLoopPosition = _s6.grass_and_scenery_tilepos;
425         // unk_13CA73E
426         // pad_13CA73F
427         // unk_13CA740
428         gClimate = ClimateType{ _s6.climate };
429         // pad_13CA741;
430         // byte_13CA742
431         // pad_013CA747
432         gClimateUpdateTimer = _s6.climate_update_timer;
433         gClimateCurrent.Weather = WeatherType{ _s6.current_weather };
434         gClimateNext.Weather = WeatherType{ _s6.next_weather };
435         gClimateCurrent.Temperature = _s6.temperature;
436         gClimateNext.Temperature = _s6.next_temperature;
437         gClimateCurrent.WeatherEffect = WeatherEffectType{ _s6.current_weather_effect };
438         gClimateNext.WeatherEffect = WeatherEffectType{ _s6.next_weather_effect };
439         gClimateCurrent.WeatherGloom = _s6.current_weather_gloom;
440         gClimateNext.WeatherGloom = _s6.next_weather_gloom;
441         gClimateCurrent.Level = static_cast<WeatherLevel>(_s6.current_weather_level);
442         gClimateNext.Level = static_cast<WeatherLevel>(_s6.next_weather_level);
443 
444         // News items
445         News::InitQueue();
446         for (size_t i = 0; i < RCT12_MAX_NEWS_ITEMS; i++)
447         {
448             const rct12_news_item* src = &_s6.news_items[i];
449             News::Item* dst = &gNewsItems[i];
450             if (src->Type < News::ItemTypeCount)
451             {
452                 dst->Type = static_cast<News::ItemType>(src->Type);
453                 dst->Flags = src->Flags;
454                 dst->Assoc = src->Assoc;
455                 dst->Ticks = src->Ticks;
456                 dst->MonthYear = src->MonthYear;
457                 dst->Day = src->Day;
458                 dst->Text = ConvertFormattedStringToOpenRCT2(std::string_view(src->Text, sizeof(src->Text)));
459             }
460             else
461             {
462                 // In case where news item type is broken, consider all remaining news items invalid.
463                 log_error("Invalid news type 0x%x for news item %d, ignoring remaining news items", src->Type, i);
464                 // Still need to set the correct type to properly terminate the queue
465                 dst->Type = News::ItemType::Null;
466                 break;
467             }
468         }
469 
470         // pad_13CE730
471         // rct1_scenario_flags
472         gWidePathTileLoopPosition.x = _s6.wide_path_tile_loop_x;
473         gWidePathTileLoopPosition.y = _s6.wide_path_tile_loop_y;
474         // pad_13CE778
475 
476         // Fix and set dynamic variables
477         map_strip_ghost_flag_from_elements();
478         game_convert_strings_to_utf8();
479         map_count_remaining_land_rights();
480         determine_ride_entrance_and_exit_locations();
481 
482         park.Name = GetUserString(_s6.park_name);
483 
484         FixLandOwnership();
485 
486         research_determine_first_of_type();
487         staff_update_greyed_patrol_areas();
488     }
489 
FixLandOwnership() const490     void FixLandOwnership() const
491     {
492         if (String::Equals(_s6.scenario_filename, "Europe - European Cultural Festival.SC6"))
493         {
494             // This scenario breaks pathfinding. Create passages between the worlds. (List is grouped by neighbouring tiles.)
495             // clang-format off
496             FixLandOwnershipTilesWithOwnership(
497                 {
498                     { 67, 94 }, { 68, 94 }, { 69, 94 },
499                     { 58, 24 }, { 58, 25 }, { 58, 26 }, { 58, 27 }, { 58, 28 }, { 58, 29 }, { 58, 30 }, { 58, 31 }, { 58, 32 },
500                     { 26, 44 }, { 26, 45 },
501                     { 32, 79 }, { 32, 80 }, { 32, 81 },
502                 },
503                 OWNERSHIP_OWNED);
504             // clang-format on
505         }
506         else if (String::Equals(gScenarioFileName, "N America - Extreme Hawaiian Island.SC6"))
507         {
508             FixLandOwnershipTilesWithOwnership(
509                 {
510                     { 132, 124 },
511                     { 133, 124 },
512                     { 133, 125 },
513                     { 133, 126 },
514                     { 119, 35 },
515                     { 132, 62 },
516                     { 133, 67 },
517                     { 136, 71 },
518                     { 87, 33 },
519                     { 87, 34 },
520                     { 90, 36 },
521                     { 91, 36 },
522                 },
523                 OWNERSHIP_OWNED);
524             // We set the doNotDowngrade flag for cases where the player has used a cheat to own all land.
525             FixLandOwnershipTilesWithOwnership(
526                 {
527                     { 49, 99 },
528                     { 50, 99 },
529                     { 88, 110 },
530                 },
531                 OWNERSHIP_AVAILABLE, true);
532         }
533     }
534 
ImportRides()535     void ImportRides()
536     {
537         for (uint8_t index = 0; index < RCT12_MAX_RIDES_IN_PARK; index++)
538         {
539             auto src = &_s6.rides[index];
540             if (src->type != RIDE_TYPE_NULL)
541             {
542                 const auto rideId = static_cast<ride_id_t>(index);
543                 auto dst = GetOrAllocateRide(rideId);
544                 ImportRide(dst, src, rideId);
545             }
546         }
547     }
548 
549     /**
550      * This code is needed to detect hacks where a tracked ride has been made invisible
551      * by setting its ride type to a flat ride.
552      *
553      * The function should classify rides as follows:
554      * 1. If the ride type is tracked and its vehicles also belong on tracks, it should be classified as tracked.
555      * 2. If the ride type is a flat ride, but its vehicles belong on tracks,
556      *     it should be classified as tracked (Crooked House mod).
557      * 3. If the ride type is tracked and its vehicles belong to a flat ride, it should be classified as tracked.
558      * 4. If the ride type is a flat ride and its vehicles also belong to a flat ride, it should be classified as a flat ride.
559      */
DetermineFlatRideStatus()560     void DetermineFlatRideStatus()
561     {
562         for (uint8_t index = 0; index < RCT12_MAX_RIDES_IN_PARK; index++)
563         {
564             auto src = &_s6.rides[index];
565             if (src->type == RIDE_TYPE_NULL)
566                 continue;
567 
568             auto subtype = RCTEntryIndexToOpenRCT2EntryIndex(src->subtype);
569             auto* rideEntry = get_ride_entry(subtype);
570             // If the ride is tracked, we don’t need to check the vehicle any more.
571             if (!GetRideTypeDescriptor(src->type).HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE))
572             {
573                 _isFlatRide[index] = false;
574                 continue;
575             }
576 
577             // We have established the ride type is a flat ride, which means the vehicle now determines whether it is a
578             // true flat ride (scenario 4) or a tracked ride with an invisibility hack (scenario 2).
579             ObjectEntryIndex originalRideType = src->type;
580             if (rideEntry != nullptr)
581             {
582                 originalRideType = ride_entry_get_first_non_null_ride_type(rideEntry);
583             }
584             const auto isFlatRide = GetRideTypeDescriptor(originalRideType).HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE);
585             _isFlatRide.set(static_cast<size_t>(index), isFlatRide);
586         }
587     }
588 
IsFlatRide(const uint8_t rct12RideIndex)589     bool IsFlatRide(const uint8_t rct12RideIndex)
590     {
591         if (rct12RideIndex == RCT12_RIDE_ID_NULL)
592             return false;
593         return _isFlatRide[rct12RideIndex];
594     }
595 
ImportRide(Ride * dst,const rct2_ride * src,const ride_id_t rideIndex)596     void ImportRide(Ride* dst, const rct2_ride* src, const ride_id_t rideIndex)
597     {
598         *dst = {};
599         dst->id = rideIndex;
600 
601         ObjectEntryIndex rideType = src->type;
602         auto subtype = RCTEntryIndexToOpenRCT2EntryIndex(src->subtype);
603         if (RCT2RideTypeNeedsConversion(src->type))
604         {
605             auto* rideEntry = get_ride_entry(subtype);
606             if (rideEntry != nullptr)
607             {
608                 rideType = RCT2RideTypeToOpenRCT2RideType(src->type, rideEntry);
609             }
610         }
611 
612         if (rideType >= RIDE_TYPE_COUNT)
613         {
614             log_error("Invalid ride type for a ride in this save.");
615             throw UnsupportedRideTypeException(rideType);
616         }
617         dst->type = rideType;
618         dst->subtype = subtype;
619         // pad_002;
620         dst->mode = static_cast<RideMode>(src->mode);
621         dst->colour_scheme_type = src->colour_scheme_type;
622 
623         for (uint8_t i = 0; i < RCT2_MAX_CARS_PER_TRAIN; i++)
624         {
625             dst->vehicle_colours[i].Body = src->vehicle_colours[i].body_colour;
626             dst->vehicle_colours[i].Trim = src->vehicle_colours[i].trim_colour;
627         }
628 
629         // pad_046;
630         dst->status = static_cast<RideStatus>(src->status);
631 
632         dst->default_name_number = src->name_arguments_number;
633         if (is_user_string_id(src->name))
634         {
635             dst->custom_name = GetUserString(src->name);
636         }
637         else
638         {
639             dst->default_name_number = src->name_arguments_number;
640         }
641 
642         if (src->overall_view.IsNull())
643         {
644             dst->overall_view.SetNull();
645         }
646         else
647         {
648             auto tileLoc = TileCoordsXY(src->overall_view.x, src->overall_view.y);
649             dst->overall_view = tileLoc.ToCoordsXY();
650         }
651 
652         for (int32_t i = 0; i < RCT12_MAX_STATIONS_PER_RIDE; i++)
653         {
654             if (src->station_starts[i].IsNull())
655             {
656                 dst->stations[i].Start.SetNull();
657             }
658             else
659             {
660                 auto tileStartLoc = TileCoordsXY(src->station_starts[i].x, src->station_starts[i].y);
661                 dst->stations[i].Start = tileStartLoc.ToCoordsXY();
662             }
663             dst->stations[i].Height = src->station_heights[i];
664             dst->stations[i].Length = src->station_length[i];
665             dst->stations[i].Depart = src->station_depart[i];
666             dst->stations[i].TrainAtStation = src->train_at_station[i];
667             // Direction is fixed later.
668 
669             if (src->entrances[i].IsNull())
670                 ride_clear_entrance_location(dst, i);
671             else
672                 ride_set_entrance_location(dst, i, { src->entrances[i].x, src->entrances[i].y, src->station_heights[i], 0 });
673 
674             if (src->exits[i].IsNull())
675                 ride_clear_exit_location(dst, i);
676             else
677                 ride_set_exit_location(dst, i, { src->exits[i].x, src->exits[i].y, src->station_heights[i], 0 });
678 
679             dst->stations[i].LastPeepInQueue = src->last_peep_in_queue[i];
680 
681             dst->stations[i].SegmentLength = src->length[i];
682             dst->stations[i].SegmentTime = src->time[i];
683 
684             dst->stations[i].QueueTime = src->queue_time[i];
685 
686             dst->stations[i].QueueLength = src->queue_length[i];
687         }
688         // All other values take 0 as their default. Since they're already memset to that, no need to do it again.
689         for (int32_t i = RCT12_MAX_STATIONS_PER_RIDE; i < MAX_STATIONS; i++)
690         {
691             dst->stations[i].Start.SetNull();
692             dst->stations[i].TrainAtStation = RideStation::NO_TRAIN;
693             ride_clear_entrance_location(dst, i);
694             ride_clear_exit_location(dst, i);
695             dst->stations[i].LastPeepInQueue = SPRITE_INDEX_NULL;
696         }
697 
698         for (int32_t i = 0; i <= RCT2_MAX_VEHICLES_PER_RIDE; i++)
699         {
700             dst->vehicles[i] = src->vehicles[i];
701         }
702         for (int32_t i = RCT2_MAX_VEHICLES_PER_RIDE; i <= MAX_VEHICLES_PER_RIDE; i++)
703         {
704             dst->vehicles[i] = SPRITE_INDEX_NULL;
705         }
706 
707         dst->depart_flags = src->depart_flags;
708 
709         dst->num_stations = src->num_stations;
710         dst->num_vehicles = src->num_vehicles;
711         dst->num_cars_per_train = src->num_cars_per_train;
712         dst->proposed_num_vehicles = src->proposed_num_vehicles;
713         dst->proposed_num_cars_per_train = src->proposed_num_cars_per_train;
714         dst->max_trains = src->max_trains;
715         dst->MinCarsPerTrain = src->GetMinCarsPerTrain();
716         dst->MaxCarsPerTrain = src->GetMaxCarsPerTrain();
717         dst->min_waiting_time = src->min_waiting_time;
718         dst->max_waiting_time = src->max_waiting_time;
719 
720         // Includes time_limit, num_laps, launch_speed, speed, rotations
721         dst->operation_option = src->operation_option;
722 
723         dst->boat_hire_return_direction = src->boat_hire_return_direction;
724         dst->boat_hire_return_position = { src->boat_hire_return_position.x, src->boat_hire_return_position.y };
725 
726         dst->special_track_elements = src->special_track_elements;
727         // pad_0D6[2];
728 
729         dst->max_speed = src->max_speed;
730         dst->average_speed = src->average_speed;
731         dst->current_test_segment = src->current_test_segment;
732         dst->average_speed_test_timeout = src->average_speed_test_timeout;
733         // pad_0E2[0x2];
734 
735         dst->max_positive_vertical_g = src->max_positive_vertical_g;
736         dst->max_negative_vertical_g = src->max_negative_vertical_g;
737         dst->max_lateral_g = src->max_lateral_g;
738         dst->previous_vertical_g = src->previous_vertical_g;
739         dst->previous_lateral_g = src->previous_lateral_g;
740         // pad_106[0x2];
741         dst->testing_flags = src->testing_flags;
742 
743         if (src->cur_test_track_location.IsNull())
744         {
745             dst->CurTestTrackLocation.SetNull();
746         }
747         else
748         {
749             dst->CurTestTrackLocation = { src->cur_test_track_location.x, src->cur_test_track_location.y,
750                                           src->cur_test_track_z };
751         }
752 
753         dst->turn_count_default = src->turn_count_default;
754         dst->turn_count_banked = src->turn_count_banked;
755         dst->turn_count_sloped = src->turn_count_sloped;
756         if (dst->type == RIDE_TYPE_MINI_GOLF)
757             dst->holes = src->inversions & 0x1F;
758         else
759             dst->inversions = src->inversions & 0x1F;
760         dst->sheltered_eighths = src->inversions >> 5;
761         dst->drops = src->drops;
762         dst->start_drop_height = src->start_drop_height;
763         dst->highest_drop_height = src->highest_drop_height;
764         dst->sheltered_length = src->sheltered_length;
765         dst->var_11C = src->var_11C;
766         dst->num_sheltered_sections = src->num_sheltered_sections;
767 
768         dst->cur_num_customers = src->cur_num_customers;
769         dst->num_customers_timeout = src->num_customers_timeout;
770 
771         for (uint8_t i = 0; i < RCT2_CUSTOMER_HISTORY_SIZE; i++)
772         {
773             dst->num_customers[i] = src->num_customers[i];
774         }
775 
776         dst->price[0] = src->price;
777 
778         for (uint8_t i = 0; i < 2; i++)
779         {
780             dst->ChairliftBullwheelLocation[i] = { src->chairlift_bullwheel_location[i].x,
781                                                    src->chairlift_bullwheel_location[i].y, src->chairlift_bullwheel_z[i] };
782         }
783 
784         dst->ratings = src->ratings;
785         dst->value = src->value;
786 
787         dst->chairlift_bullwheel_rotation = src->chairlift_bullwheel_rotation;
788 
789         dst->satisfaction = src->satisfaction;
790         dst->satisfaction_time_out = src->satisfaction_time_out;
791         dst->satisfaction_next = src->satisfaction_next;
792 
793         dst->window_invalidate_flags = src->window_invalidate_flags;
794         // pad_14E[0x02];
795 
796         dst->total_customers = src->total_customers;
797         dst->total_profit = ToMoney64(src->total_profit);
798         dst->popularity = src->popularity;
799         dst->popularity_time_out = src->popularity_time_out;
800         dst->popularity_next = src->popularity_next;
801 
802         ImportNumRiders(dst, rideIndex);
803 
804         dst->music_tune_id = src->music_tune_id;
805         dst->slide_in_use = src->slide_in_use;
806         // Includes maze_tiles
807         dst->slide_peep = src->slide_peep;
808         // pad_160[0xE];
809         dst->slide_peep_t_shirt_colour = src->slide_peep_t_shirt_colour;
810         // pad_16F[0x7];
811         dst->spiral_slide_progress = src->spiral_slide_progress;
812         // pad_177[0x9];
813         dst->build_date = static_cast<int32_t>(src->build_date);
814         dst->upkeep_cost = src->upkeep_cost;
815         dst->race_winner = src->race_winner;
816         // pad_186[0x02];
817         dst->music_position = src->music_position;
818 
819         dst->breakdown_reason_pending = src->breakdown_reason_pending;
820         dst->mechanic_status = src->mechanic_status;
821         dst->mechanic = src->mechanic;
822         dst->inspection_station = src->inspection_station;
823         dst->broken_vehicle = src->broken_vehicle;
824         dst->broken_car = src->broken_car;
825         dst->breakdown_reason = src->breakdown_reason;
826 
827         dst->price[1] = src->price_secondary;
828 
829         dst->reliability = src->reliability;
830         dst->unreliability_factor = src->unreliability_factor;
831         dst->downtime = src->downtime;
832         dst->inspection_interval = src->inspection_interval;
833         dst->last_inspection = src->last_inspection;
834 
835         for (uint8_t i = 0; i < RCT2_DOWNTIME_HISTORY_SIZE; i++)
836         {
837             dst->downtime_history[i] = src->downtime_history[i];
838         }
839 
840         dst->no_primary_items_sold = src->no_primary_items_sold;
841         dst->no_secondary_items_sold = src->no_secondary_items_sold;
842 
843         dst->breakdown_sound_modifier = src->breakdown_sound_modifier;
844         dst->not_fixed_timeout = src->not_fixed_timeout;
845         dst->last_crash_type = src->last_crash_type;
846         dst->connected_message_throttle = src->connected_message_throttle;
847 
848         dst->income_per_hour = ToMoney64(src->income_per_hour);
849         dst->profit = ToMoney64(src->profit);
850 
851         for (uint8_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++)
852         {
853             dst->track_colour[i].main = src->track_colour_main[i];
854             dst->track_colour[i].additional = src->track_colour_additional[i];
855             dst->track_colour[i].supports = src->track_colour_supports[i];
856         }
857         // This stall was not colourable in RCT2.
858         if (dst->type == RIDE_TYPE_FOOD_STALL)
859         {
860             auto object = object_entry_get_object(ObjectType::Ride, dst->subtype);
861             if (object != nullptr && object->GetIdentifier() == "rct2.icecr1")
862             {
863                 dst->track_colour[0].main = COLOUR_LIGHT_BLUE;
864             }
865         }
866 
867         auto musicStyle = OBJECT_ENTRY_INDEX_NULL;
868         if (GetRideTypeDescriptor(dst->type).HasFlag(RIDE_TYPE_FLAG_ALLOW_MUSIC))
869         {
870             musicStyle = src->music;
871         }
872         dst->music = musicStyle;
873 
874         auto entranceStyle = src->entrance_style;
875         // In SV7, "plain" entrances are invisible.
876         if (_isSV7 && entranceStyle == RCT12_STATION_STYLE_PLAIN)
877         {
878             entranceStyle = RCT12_STATION_STYLE_INVISIBLE;
879         }
880         dst->entrance_style = entranceStyle;
881         dst->vehicle_change_timeout = src->vehicle_change_timeout;
882         dst->num_block_brakes = src->num_block_brakes;
883         dst->lift_hill_speed = src->lift_hill_speed;
884         dst->guests_favourite = src->guests_favourite;
885         dst->lifecycle_flags = src->lifecycle_flags;
886 
887         for (uint8_t i = 0; i < RCT2_MAX_CARS_PER_TRAIN; i++)
888         {
889             dst->vehicle_colours[i].Ternary = src->vehicle_colours_extended[i];
890         }
891 
892         dst->total_air_time = src->total_air_time;
893         dst->current_test_station = src->current_test_station;
894         dst->num_circuits = src->num_circuits;
895         dst->CableLiftLoc = { src->cable_lift_x, src->cable_lift_y, src->cable_lift_z * COORDS_Z_STEP };
896         // pad_1FD;
897         dst->cable_lift = src->cable_lift;
898 
899         // pad_208[0x58];
900     }
901 
ImportRideRatingsCalcData()902     void ImportRideRatingsCalcData()
903     {
904         const auto& src = _s6.ride_ratings_calc_data;
905         auto& dst = gRideRatingUpdateState;
906         dst = {};
907         dst.Proximity = { src.proximity_x, src.proximity_y, src.proximity_z };
908         dst.ProximityStart = { src.proximity_start_x, src.proximity_start_y, src.proximity_start_z };
909         dst.CurrentRide = RCT12RideIdToOpenRCT2RideId(src.current_ride);
910         dst.State = src.state;
911         if (src.current_ride < RCT12_MAX_RIDES_IN_PARK && _s6.rides[src.current_ride].type < std::size(RideTypeDescriptors))
912             dst.ProximityTrackType = RCT2TrackTypeToOpenRCT2(
913                 src.proximity_track_type, _s6.rides[src.current_ride].type, IsFlatRide(src.current_ride));
914         else
915             dst.ProximityTrackType = 0xFF;
916         dst.ProximityBaseHeight = src.proximity_base_height;
917         dst.ProximityTotal = src.proximity_total;
918         for (size_t i = 0; i < std::size(src.proximity_scores); i++)
919         {
920             dst.ProximityScores[i] = src.proximity_scores[i];
921         }
922         dst.AmountOfBrakes = src.num_brakes;
923         dst.AmountOfReversers = src.num_reversers;
924         dst.StationFlags = src.station_flags;
925     }
926 
ImportRideMeasurements()927     void ImportRideMeasurements()
928     {
929         for (const auto& src : _s6.ride_measurements)
930         {
931             if (src.ride_index != RCT12_RIDE_ID_NULL)
932             {
933                 const auto rideId = static_cast<ride_id_t>(src.ride_index);
934                 auto ride = get_ride(rideId);
935                 if (ride != nullptr)
936                 {
937                     ride->measurement = std::make_unique<RideMeasurement>();
938                     ImportRideMeasurement(*ride->measurement, src);
939                 }
940             }
941         }
942     }
943 
ImportRideMeasurement(RideMeasurement & dst,const RCT12RideMeasurement & src)944     void ImportRideMeasurement(RideMeasurement& dst, const RCT12RideMeasurement& src)
945     {
946         dst.flags = src.flags;
947         dst.last_use_tick = src.last_use_tick;
948         dst.num_items = src.num_items;
949         dst.current_item = src.current_item;
950         dst.vehicle_index = src.vehicle_index;
951         dst.current_station = src.current_station;
952         for (size_t i = 0; i < std::size(src.velocity); i++)
953         {
954             dst.velocity[i] = src.velocity[i];
955             dst.altitude[i] = src.altitude[i];
956             dst.vertical[i] = src.vertical[i];
957             dst.lateral[i] = src.lateral[i];
958         }
959     }
960 
ImportResearchedRideTypes()961     void ImportResearchedRideTypes()
962     {
963         set_every_ride_type_not_invented();
964 
965         for (int32_t rideType = 0; rideType < RCT2_RIDE_TYPE_COUNT; rideType++)
966         {
967             int32_t quadIndex = rideType >> 5;
968             int32_t bitIndex = rideType & 0x1F;
969             bool invented = (_s6.researched_ride_types[quadIndex] & (1UL << bitIndex));
970 
971             if (invented)
972                 ride_type_set_invented(rideType);
973         }
974     }
975 
ImportResearchedRideEntries()976     void ImportResearchedRideEntries()
977     {
978         set_every_ride_entry_not_invented();
979 
980         for (int32_t rideEntryIndex = 0; rideEntryIndex < MAX_RIDE_OBJECTS; rideEntryIndex++)
981         {
982             int32_t quadIndex = rideEntryIndex >> 5;
983             int32_t bitIndex = rideEntryIndex & 0x1F;
984             bool invented = (_s6.researched_ride_entries[quadIndex] & (1UL << bitIndex));
985 
986             if (invented)
987                 ride_entry_set_invented(rideEntryIndex);
988         }
989     }
990 
ImportResearchedSceneryItems()991     void ImportResearchedSceneryItems()
992     {
993         set_all_scenery_items_not_invented();
994 
995         for (uint16_t sceneryEntryIndex = 0; sceneryEntryIndex < RCT2_MAX_RESEARCHED_SCENERY_ITEMS; sceneryEntryIndex++)
996         {
997             int32_t quadIndex = sceneryEntryIndex >> 5;
998             int32_t bitIndex = sceneryEntryIndex & 0x1F;
999             bool invented = (_s6.researched_scenery_items[quadIndex] & (1UL << bitIndex));
1000 
1001             if (invented)
1002             {
1003                 ScenerySelection scenerySelection = { static_cast<uint8_t>((sceneryEntryIndex >> 8) & 0xFF),
1004                                                       static_cast<uint16_t>(sceneryEntryIndex & 0xFF) };
1005 
1006                 // SV6 has room for 8 types of scenery, and sometimes scenery of non-existing types 5 and 6 is marked as
1007                 // "invented".
1008                 if (scenerySelection.SceneryType < SCENERY_TYPE_COUNT)
1009                 {
1010                     scenery_set_invented(scenerySelection);
1011                 }
1012             }
1013         }
1014     }
1015 
ImportResearchList()1016     void ImportResearchList()
1017     {
1018         bool invented = true;
1019         for (const auto& researchItem : _s6.research_items)
1020         {
1021             if (researchItem.IsInventedEndMarker())
1022             {
1023                 invented = false;
1024                 continue;
1025             }
1026             if (researchItem.IsUninventedEndMarker() || researchItem.IsRandomEndMarker())
1027             {
1028                 break;
1029             }
1030 
1031             if (invented)
1032                 gResearchItemsInvented.emplace_back(researchItem);
1033             else
1034                 gResearchItemsUninvented.emplace_back(researchItem);
1035         }
1036     }
1037 
ImportBanner(Banner * dst,const RCT12Banner * src)1038     void ImportBanner(Banner* dst, const RCT12Banner* src)
1039     {
1040         *dst = {};
1041         dst->type = RCTEntryIndexToOpenRCT2EntryIndex(src->type);
1042         dst->flags = src->flags;
1043 
1044         if (!(src->flags & BANNER_FLAG_LINKED_TO_RIDE) && is_user_string_id(src->string_idx))
1045         {
1046             dst->text = GetUserString(src->string_idx);
1047         }
1048 
1049         if (src->flags & BANNER_FLAG_LINKED_TO_RIDE)
1050         {
1051             dst->ride_index = RCT12RideIdToOpenRCT2RideId(src->ride_index);
1052         }
1053         else
1054         {
1055             dst->colour = src->colour;
1056         }
1057 
1058         dst->text_colour = src->text_colour;
1059         dst->position.x = src->x;
1060         dst->position.y = src->y;
1061     }
1062 
Initialise()1063     void Initialise()
1064     {
1065         OpenRCT2::GetContext()->GetGameState()->InitAll(_s6.map_size);
1066     }
1067 
1068     /**
1069      * Imports guest entry points.
1070      * Includes fixes for incorrectly set guest entry points in some scenarios.
1071      */
ImportPeepSpawns()1072     void ImportPeepSpawns()
1073     {
1074         // Many WW and TT have scenario_filename fields containing an incorrect filename. Check for both this filename
1075         // and the corrected filename.
1076 
1077         // In this park, peep_spawns[0] is incorrect, and peep_spawns[1] is correct.
1078         if (String::Equals(_s6.scenario_filename, "WW South America - Rio Carnival.SC6")
1079             || String::Equals(_s6.scenario_filename, "South America - Rio Carnival.SC6"))
1080         {
1081             _s6.peep_spawns[0] = { 2160, 3167, 6, 1 };
1082             _s6.peep_spawns[1].x = RCT12_PEEP_SPAWN_UNDEFINED;
1083         }
1084         // In this park, peep_spawns[0] is correct. Just clear the other.
1085         else if (
1086             String::Equals(_s6.scenario_filename, "Great Wall of China Tourism Enhancement.SC6")
1087             || String::Equals(_s6.scenario_filename, "Asia - Great Wall of China Tourism Enhancement.SC6"))
1088         {
1089             _s6.peep_spawns[1].x = RCT12_PEEP_SPAWN_UNDEFINED;
1090         }
1091         // Amity Airfield has peeps entering from the corner of the tile, instead of the middle.
1092         else if (String::Equals(_s6.scenario_filename, "Amity Airfield.SC6"))
1093         {
1094             _s6.peep_spawns[0].y = 1296;
1095         }
1096         // #9926: Africa - Oasis has peeps spawning on the edge underground near the entrance
1097         else if (String::Equals(_s6.scenario_filename, "Africa - Oasis.SC6"))
1098         {
1099             _s6.peep_spawns[0].y = 2128;
1100             _s6.peep_spawns[0].z = 7;
1101         }
1102 
1103         gPeepSpawns.clear();
1104         for (size_t i = 0; i < RCT12_MAX_PEEP_SPAWNS; i++)
1105         {
1106             if (_s6.peep_spawns[i].x != RCT12_PEEP_SPAWN_UNDEFINED)
1107             {
1108                 PeepSpawn spawn = { _s6.peep_spawns[i].x, _s6.peep_spawns[i].y, _s6.peep_spawns[i].z * 16,
1109                                     _s6.peep_spawns[i].direction };
1110                 gPeepSpawns.push_back(spawn);
1111             }
1112         }
1113     }
1114 
ImportNumRiders(Ride * dst,const ride_id_t rideIndex)1115     void ImportNumRiders(Ride* dst, const ride_id_t rideIndex)
1116     {
1117         // The number of riders might have overflown or underflown. Re-calculate the value.
1118         uint16_t numRiders = 0;
1119         for (const auto& sprite : _s6.sprites)
1120         {
1121             if (sprite.unknown.sprite_identifier == RCT12SpriteIdentifier::Peep)
1122             {
1123                 if (sprite.peep.current_ride == static_cast<RCT12RideId>(rideIndex)
1124                     && (static_cast<PeepState>(sprite.peep.state) == PeepState::OnRide
1125                         || static_cast<PeepState>(sprite.peep.state) == PeepState::EnteringRide))
1126                 {
1127                     numRiders++;
1128                 }
1129             }
1130         }
1131         dst->num_riders = numRiders;
1132     }
1133 
ImportTileElements()1134     void ImportTileElements()
1135     {
1136         // Build tile pointer cache (needed to get the first element at a certain location)
1137         auto tilePointerIndex = TilePointerIndex<RCT12TileElement>(RCT2_MAXIMUM_MAP_SIZE_TECHNICAL, _s6.tile_elements);
1138 
1139         std::vector<TileElement> tileElements;
1140         for (TileCoordsXY coords = { 0, 0 }; coords.y < MAXIMUM_MAP_SIZE_TECHNICAL; coords.y++)
1141         {
1142             for (coords.x = 0; coords.x < MAXIMUM_MAP_SIZE_TECHNICAL; coords.x++)
1143             {
1144                 if (coords.x >= RCT2_MAXIMUM_MAP_SIZE_TECHNICAL || coords.y >= RCT2_MAXIMUM_MAP_SIZE_TECHNICAL)
1145                 {
1146                     auto& dstElement = tileElements.emplace_back();
1147                     dstElement.ClearAs(TILE_ELEMENT_TYPE_SURFACE);
1148                     dstElement.SetLastForTile(true);
1149                     continue;
1150                 }
1151 
1152                 RCT12TileElement* srcElement = tilePointerIndex.GetFirstElementAt(coords);
1153                 // This might happen with damaged parks. Make sure there is *something* to avoid crashes.
1154                 if (srcElement == nullptr)
1155                 {
1156                     auto& dstElement = tileElements.emplace_back();
1157                     dstElement.ClearAs(TILE_ELEMENT_TYPE_SURFACE);
1158                     dstElement.SetLastForTile(true);
1159                     continue;
1160                 }
1161 
1162                 do
1163                 {
1164                     auto& dstElement = tileElements.emplace_back();
1165                     if (srcElement->base_height == RCT12_MAX_ELEMENT_HEIGHT)
1166                     {
1167                         std::memcpy(&dstElement, srcElement, sizeof(*srcElement));
1168                     }
1169                     else
1170                     {
1171                         auto tileElementType = static_cast<RCT12TileElementType>(srcElement->GetType());
1172                         // Todo: replace with setting invisibility bit
1173                         if (tileElementType == RCT12TileElementType::Corrupt
1174                             || tileElementType == RCT12TileElementType::EightCarsCorrupt14
1175                             || tileElementType == RCT12TileElementType::EightCarsCorrupt15)
1176                             std::memcpy(&dstElement, srcElement, sizeof(*srcElement));
1177                         else
1178                             ImportTileElement(&dstElement, srcElement);
1179                     }
1180                 } while (!(srcElement++)->IsLastForTile());
1181 
1182                 // Set last element flag in case the original last element was never added
1183                 if (tileElements.size() > 0)
1184                 {
1185                     tileElements.back().SetLastForTile(true);
1186                 }
1187             }
1188         }
1189         SetTileElements(std::move(tileElements));
1190     }
1191 
ImportTileElement(TileElement * dst,const RCT12TileElement * src)1192     void ImportTileElement(TileElement* dst, const RCT12TileElement* src)
1193     {
1194         // Todo: allow for changing definition of OpenRCT2 tile element types - replace with a map
1195         uint8_t tileElementType = src->GetType();
1196         dst->ClearAs(tileElementType);
1197         dst->SetDirection(src->GetDirection());
1198         dst->SetBaseZ(src->base_height * COORDS_Z_STEP);
1199         dst->SetClearanceZ(src->clearance_height * COORDS_Z_STEP);
1200 
1201         // All saved in "flags"
1202         dst->SetOccupiedQuadrants(src->GetOccupiedQuadrants());
1203         dst->SetGhost(src->IsGhost());
1204         dst->SetLastForTile(src->IsLastForTile());
1205 
1206         switch (tileElementType)
1207         {
1208             case TILE_ELEMENT_TYPE_SURFACE:
1209             {
1210                 auto dst2 = dst->AsSurface();
1211                 auto src2 = src->AsSurface();
1212 
1213                 dst2->SetSlope(src2->GetSlope());
1214                 dst2->SetSurfaceStyle(src2->GetSurfaceStyle());
1215                 dst2->SetEdgeStyle(src2->GetEdgeStyle());
1216                 dst2->SetGrassLength(src2->GetGrassLength());
1217                 dst2->SetOwnership(src2->GetOwnership());
1218                 dst2->SetParkFences(src2->GetParkFences());
1219                 dst2->SetWaterHeight(src2->GetWaterHeight());
1220                 dst2->SetHasTrackThatNeedsWater(src2->HasTrackThatNeedsWater());
1221 
1222                 break;
1223             }
1224             case TILE_ELEMENT_TYPE_PATH:
1225             {
1226                 auto dst2 = dst->AsPath();
1227                 auto src2 = src->AsPath();
1228 
1229                 dst2->SetLegacyPathEntryIndex(src2->GetEntryIndex());
1230                 dst2->SetQueueBannerDirection(src2->GetQueueBannerDirection());
1231                 dst2->SetSloped(src2->IsSloped());
1232                 dst2->SetSlopeDirection(src2->GetSlopeDirection());
1233                 dst2->SetRideIndex(RCT12RideIdToOpenRCT2RideId(src2->GetRideIndex()));
1234                 dst2->SetStationIndex(src2->GetStationIndex());
1235                 dst2->SetWide(src2->IsWide());
1236                 dst2->SetIsQueue(src2->IsQueue());
1237                 dst2->SetHasQueueBanner(src2->HasQueueBanner());
1238                 dst2->SetEdges(src2->GetEdges());
1239                 dst2->SetCorners(src2->GetCorners());
1240                 dst2->SetAddition(src2->GetAddition());
1241                 dst2->SetAdditionIsGhost(src2->AdditionIsGhost());
1242                 dst2->SetAdditionStatus(src2->GetAdditionStatus());
1243                 dst2->SetIsBroken(src2->IsBroken());
1244                 dst2->SetIsBlockedByVehicle(src2->IsBlockedByVehicle());
1245 
1246                 break;
1247             }
1248             case TILE_ELEMENT_TYPE_TRACK:
1249             {
1250                 auto dst2 = dst->AsTrack();
1251                 auto src2 = src->AsTrack();
1252 
1253                 auto rideType = _s6.rides[src2->GetRideIndex()].type;
1254                 track_type_t trackType = static_cast<track_type_t>(src2->GetTrackType());
1255 
1256                 dst2->SetTrackType(RCT2TrackTypeToOpenRCT2(trackType, rideType, IsFlatRide(src2->GetRideIndex())));
1257                 dst2->SetRideType(rideType);
1258                 dst2->SetSequenceIndex(src2->GetSequenceIndex());
1259                 dst2->SetRideIndex(RCT12RideIdToOpenRCT2RideId(src2->GetRideIndex()));
1260                 dst2->SetColourScheme(src2->GetColourScheme());
1261                 dst2->SetHasChain(src2->HasChain());
1262                 dst2->SetHasCableLift(src2->HasCableLift());
1263                 dst2->SetInverted(src2->IsInverted());
1264                 dst2->SetStationIndex(src2->GetStationIndex());
1265                 dst2->SetHasGreenLight(src2->HasGreenLight());
1266                 dst2->SetBlockBrakeClosed(src2->BlockBrakeClosed());
1267                 dst2->SetIsIndestructible(src2->IsIndestructible());
1268                 // Skipping IsHighlighted()
1269 
1270                 if (TrackTypeHasSpeedSetting(trackType))
1271                 {
1272                     dst2->SetBrakeBoosterSpeed(src2->GetBrakeBoosterSpeed());
1273                 }
1274                 else if (trackType == TrackElemType::OnRidePhoto)
1275                 {
1276                     dst2->SetPhotoTimeout(src2->GetPhotoTimeout());
1277                 }
1278 
1279                 // This has to be done last, since the maze entry shares fields with the colour and sequence fields.
1280                 if (rideType == RIDE_TYPE_MAZE)
1281                 {
1282                     dst2->SetMazeEntry(src2->GetMazeEntry());
1283                 }
1284                 else if (rideType == RIDE_TYPE_GHOST_TRAIN)
1285                 {
1286                     dst2->SetDoorAState(src2->GetDoorAState());
1287                     dst2->SetDoorBState(src2->GetDoorBState());
1288                 }
1289                 else
1290                 {
1291                     dst2->SetSeatRotation(src2->GetSeatRotation());
1292                 }
1293 
1294                 break;
1295             }
1296             case TILE_ELEMENT_TYPE_SMALL_SCENERY:
1297             {
1298                 auto dst2 = dst->AsSmallScenery();
1299                 auto src2 = src->AsSmallScenery();
1300 
1301                 dst2->SetEntryIndex(src2->GetEntryIndex());
1302                 dst2->SetAge(src2->GetAge());
1303                 dst2->SetSceneryQuadrant(src2->GetSceneryQuadrant());
1304                 dst2->SetPrimaryColour(src2->GetPrimaryColour());
1305                 dst2->SetSecondaryColour(src2->GetSecondaryColour());
1306                 if (src2->NeedsSupports())
1307                     dst2->SetNeedsSupports();
1308 
1309                 break;
1310             }
1311             case TILE_ELEMENT_TYPE_ENTRANCE:
1312             {
1313                 auto dst2 = dst->AsEntrance();
1314                 auto src2 = src->AsEntrance();
1315 
1316                 dst2->SetEntranceType(src2->GetEntranceType());
1317                 dst2->SetRideIndex(RCT12RideIdToOpenRCT2RideId(src2->GetRideIndex()));
1318                 dst2->SetStationIndex(src2->GetStationIndex());
1319                 dst2->SetSequenceIndex(src2->GetSequenceIndex());
1320                 dst2->SetLegacyPathEntryIndex(src2->GetPathType());
1321 
1322                 break;
1323             }
1324             case TILE_ELEMENT_TYPE_WALL:
1325             {
1326                 auto dst2 = dst->AsWall();
1327                 auto src2 = src->AsWall();
1328 
1329                 dst2->SetEntryIndex(src2->GetEntryIndex());
1330                 dst2->SetSlope(src2->GetSlope());
1331                 dst2->SetPrimaryColour(src2->GetPrimaryColour());
1332                 dst2->SetSecondaryColour(src2->GetSecondaryColour());
1333                 dst2->SetTertiaryColour(src2->GetTertiaryColour());
1334                 dst2->SetAnimationFrame(src2->GetAnimationFrame());
1335                 dst2->SetAcrossTrack(src2->IsAcrossTrack());
1336                 dst2->SetAnimationIsBackwards(src2->AnimationIsBackwards());
1337 
1338                 // Import banner information
1339                 dst2->SetBannerIndex(BANNER_INDEX_NULL);
1340                 auto entry = dst2->GetEntry();
1341                 if (entry != nullptr && entry->scrolling_mode != SCROLLING_MODE_NONE)
1342                 {
1343                     auto bannerIndex = src2->GetBannerIndex();
1344                     if (bannerIndex < std::size(_s6.banners))
1345                     {
1346                         auto srcBanner = &_s6.banners[bannerIndex];
1347                         auto dstBanner = GetOrCreateBanner(bannerIndex);
1348                         if (dstBanner == nullptr)
1349                         {
1350                             dst2->SetBannerIndex(BANNER_INDEX_NULL);
1351                         }
1352                         else
1353                         {
1354                             ImportBanner(dstBanner, srcBanner);
1355                             dst2->SetBannerIndex(src2->GetBannerIndex());
1356                         }
1357                     }
1358                 }
1359                 break;
1360             }
1361             case TILE_ELEMENT_TYPE_LARGE_SCENERY:
1362             {
1363                 auto dst2 = dst->AsLargeScenery();
1364                 auto src2 = src->AsLargeScenery();
1365 
1366                 dst2->SetEntryIndex(src2->GetEntryIndex());
1367                 dst2->SetSequenceIndex(src2->GetSequenceIndex());
1368                 dst2->SetPrimaryColour(src2->GetPrimaryColour());
1369                 dst2->SetSecondaryColour(src2->GetSecondaryColour());
1370 
1371                 // Import banner information
1372                 dst2->SetBannerIndex(BANNER_INDEX_NULL);
1373                 auto entry = dst2->GetEntry();
1374                 if (entry != nullptr && entry->scrolling_mode != SCROLLING_MODE_NONE)
1375                 {
1376                     auto bannerIndex = src2->GetBannerIndex();
1377                     if (bannerIndex < std::size(_s6.banners))
1378                     {
1379                         auto srcBanner = &_s6.banners[bannerIndex];
1380                         auto dstBanner = GetOrCreateBanner(bannerIndex);
1381                         if (dstBanner == nullptr)
1382                         {
1383                             dst2->SetBannerIndex(BANNER_INDEX_NULL);
1384                         }
1385                         else
1386                         {
1387                             ImportBanner(dstBanner, srcBanner);
1388                             dst2->SetBannerIndex(src2->GetBannerIndex());
1389                         }
1390                     }
1391                 }
1392                 break;
1393             }
1394             case TILE_ELEMENT_TYPE_BANNER:
1395             {
1396                 auto dst2 = dst->AsBanner();
1397                 auto src2 = src->AsBanner();
1398 
1399                 dst2->SetPosition(src2->GetPosition());
1400                 dst2->SetAllowedEdges(src2->GetAllowedEdges());
1401 
1402                 auto bannerIndex = src2->GetIndex();
1403                 if (bannerIndex < std::size(_s6.banners))
1404                 {
1405                     auto srcBanner = &_s6.banners[bannerIndex];
1406                     auto dstBanner = GetOrCreateBanner(bannerIndex);
1407                     if (dstBanner == nullptr)
1408                     {
1409                         dst2->SetIndex(BANNER_INDEX_NULL);
1410                     }
1411                     else
1412                     {
1413                         ImportBanner(dstBanner, srcBanner);
1414                         dst2->SetIndex(bannerIndex);
1415                     }
1416                 }
1417                 else
1418                 {
1419                     dst2->SetIndex(BANNER_INDEX_NULL);
1420                 }
1421                 break;
1422             }
1423             default:
1424                 assert(false);
1425         }
1426     }
1427 
ImportMarketingCampaigns()1428     void ImportMarketingCampaigns()
1429     {
1430         for (size_t i = 0; i < ADVERTISING_CAMPAIGN_COUNT; i++)
1431         {
1432             if (_s6.campaign_weeks_left[i] & CAMPAIGN_ACTIVE_FLAG)
1433             {
1434                 MarketingCampaign campaign{};
1435                 campaign.Type = static_cast<uint8_t>(i);
1436                 campaign.WeeksLeft = _s6.campaign_weeks_left[i] & ~(CAMPAIGN_ACTIVE_FLAG | CAMPAIGN_FIRST_WEEK_FLAG);
1437                 if ((_s6.campaign_weeks_left[i] & CAMPAIGN_FIRST_WEEK_FLAG) != 0)
1438                 {
1439                     campaign.Flags |= MarketingCampaignFlags::FIRST_WEEK;
1440                 }
1441                 if (campaign.Type == ADVERTISING_CAMPAIGN_RIDE_FREE || campaign.Type == ADVERTISING_CAMPAIGN_RIDE)
1442                 {
1443                     campaign.RideId = RCT12RideIdToOpenRCT2RideId(_s6.campaign_ride_index[i]);
1444                 }
1445                 else if (campaign.Type == ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE)
1446                 {
1447                     campaign.ShopItemType = ShopItem(_s6.campaign_ride_index[i]);
1448                 }
1449                 gMarketingCampaigns.push_back(campaign);
1450             }
1451         }
1452     }
1453 
ImportStaffPatrolArea(Staff * staffmember,uint8_t staffId)1454     void ImportStaffPatrolArea(Staff* staffmember, uint8_t staffId)
1455     {
1456         // First check staff mode as vanilla did not clean up patrol areas when switching from patrol to walk
1457         // without doing this we could accidentally add a patrol when it didn't exist.
1458         if (_s6.staff_modes[staffId] != RCT2StaffMode::Patrol)
1459         {
1460             return;
1461         }
1462         int32_t peepOffset = staffId * RCT12_PATROL_AREA_SIZE;
1463         for (int32_t i = 0; i < RCT12_PATROL_AREA_SIZE; i++)
1464         {
1465             if (_s6.patrol_areas[peepOffset + i] == 0)
1466             {
1467                 // No patrol for this area
1468                 continue;
1469             }
1470 
1471             // Loop over the bits of the uint32_t
1472             for (int32_t j = 0; j < 32; j++)
1473             {
1474                 int8_t bit = (_s6.patrol_areas[peepOffset + i] >> j) & 1;
1475                 if (bit == 0)
1476                 {
1477                     // No patrol for this area
1478                     continue;
1479                 }
1480                 // val contains the 6 highest bits of both the x and y coordinates
1481                 int32_t val = j | (i << 5);
1482                 int32_t x = val & 0x03F;
1483                 x <<= 7;
1484                 int32_t y = val & 0xFC0;
1485                 y <<= 1;
1486                 staffmember->SetPatrolArea({ x, y }, true);
1487             }
1488         }
1489     }
1490 
ImportEntities()1491     void ImportEntities()
1492     {
1493         for (int32_t i = 0; i < RCT2_MAX_SPRITES; i++)
1494         {
1495             ImportEntity(_s6.sprites[i].unknown);
1496         }
1497     }
1498 
1499     template<typename OpenRCT2_T> void ImportEntity(const RCT12SpriteBase& src);
1500 
ImportEntityPeep(Peep * dst,const RCT2SpritePeep * src)1501     void ImportEntityPeep(Peep* dst, const RCT2SpritePeep* src)
1502     {
1503         const auto isNullLocation = [](const rct12_xyzd8& pos) {
1504             return pos.x == 0xFF && pos.y == 0xFF && pos.z == 0xFF && pos.direction == INVALID_DIRECTION;
1505         };
1506 
1507         ImportEntityCommonProperties(static_cast<EntityBase*>(dst), src);
1508         if (is_user_string_id(src->name_string_idx))
1509         {
1510             dst->SetName(GetUserString(src->name_string_idx));
1511         }
1512         dst->NextLoc = { src->next_x, src->next_y, src->next_z * COORDS_Z_STEP };
1513         dst->NextFlags = src->next_flags;
1514         dst->State = static_cast<PeepState>(src->state);
1515         dst->SubState = src->sub_state;
1516         dst->SpriteType = static_cast<PeepSpriteType>(src->sprite_type);
1517         dst->TshirtColour = src->tshirt_colour;
1518         dst->TrousersColour = src->trousers_colour;
1519         dst->DestinationX = src->destination_x;
1520         dst->DestinationY = src->destination_y;
1521         dst->DestinationTolerance = src->destination_tolerance;
1522         dst->Var37 = src->var_37;
1523         dst->Energy = src->energy;
1524         dst->EnergyTarget = src->energy_target;
1525         dst->Mass = src->mass;
1526         dst->WindowInvalidateFlags = src->window_invalidate_flags;
1527         dst->CurrentRide = RCT12RideIdToOpenRCT2RideId(src->current_ride);
1528         dst->CurrentRideStation = src->current_ride_station;
1529         dst->CurrentTrain = src->current_train;
1530         dst->TimeToSitdown = src->time_to_sitdown;
1531         dst->SpecialSprite = src->special_sprite;
1532         dst->ActionSpriteType = static_cast<PeepActionSpriteType>(src->action_sprite_type);
1533         dst->NextActionSpriteType = static_cast<PeepActionSpriteType>(src->next_action_sprite_type);
1534         dst->ActionSpriteImageOffset = src->action_sprite_image_offset;
1535         dst->Action = static_cast<PeepActionType>(src->action);
1536         dst->ActionFrame = src->action_frame;
1537         dst->StepProgress = src->step_progress;
1538         dst->PeepDirection = src->direction;
1539         dst->InteractionRideIndex = RCT12RideIdToOpenRCT2RideId(src->interaction_ride_index);
1540         dst->Id = src->id;
1541         dst->PathCheckOptimisation = src->path_check_optimisation;
1542         dst->PeepFlags = src->peep_flags;
1543         if (isNullLocation(src->pathfind_goal))
1544         {
1545             dst->PathfindGoal.SetNull();
1546             dst->PathfindGoal.direction = INVALID_DIRECTION;
1547         }
1548         else
1549         {
1550             dst->PathfindGoal = { src->pathfind_goal.x, src->pathfind_goal.y, src->pathfind_goal.z,
1551                                   src->pathfind_goal.direction };
1552         }
1553         for (size_t i = 0; i < std::size(src->pathfind_history); i++)
1554         {
1555             if (isNullLocation(src->pathfind_history[i]))
1556             {
1557                 dst->PathfindHistory[i].SetNull();
1558                 dst->PathfindHistory[i].direction = INVALID_DIRECTION;
1559             }
1560             else
1561             {
1562                 dst->PathfindHistory[i] = { src->pathfind_history[i].x, src->pathfind_history[i].y, src->pathfind_history[i].z,
1563                                             src->pathfind_history[i].direction };
1564             }
1565         }
1566         dst->WalkingFrameNum = src->no_action_frame_num;
1567     }
1568 
GetEntityTypeFromRCT2Sprite(const RCT12SpriteBase * src)1569     constexpr EntityType GetEntityTypeFromRCT2Sprite(const RCT12SpriteBase* src)
1570     {
1571         EntityType output = EntityType::Null;
1572         switch (src->sprite_identifier)
1573         {
1574             case RCT12SpriteIdentifier::Vehicle:
1575                 output = EntityType::Vehicle;
1576                 break;
1577             case RCT12SpriteIdentifier::Peep:
1578                 if (RCT12PeepType(static_cast<const RCT2SpritePeep*>(src)->peep_type) == RCT12PeepType::Guest)
1579                 {
1580                     output = EntityType::Guest;
1581                 }
1582                 else
1583                 {
1584                     output = EntityType::Staff;
1585                 }
1586                 break;
1587             case RCT12SpriteIdentifier::Misc:
1588 
1589                 switch (RCT12MiscEntityType(src->type))
1590                 {
1591                     case RCT12MiscEntityType::SteamParticle:
1592                         output = EntityType::SteamParticle;
1593                         break;
1594                     case RCT12MiscEntityType::MoneyEffect:
1595                         output = EntityType::MoneyEffect;
1596                         break;
1597                     case RCT12MiscEntityType::CrashedVehicleParticle:
1598                         output = EntityType::CrashedVehicleParticle;
1599                         break;
1600                     case RCT12MiscEntityType::ExplosionCloud:
1601                         output = EntityType::ExplosionCloud;
1602                         break;
1603                     case RCT12MiscEntityType::CrashSplash:
1604                         output = EntityType::CrashSplash;
1605                         break;
1606                     case RCT12MiscEntityType::ExplosionFlare:
1607                         output = EntityType::ExplosionFlare;
1608                         break;
1609                     case RCT12MiscEntityType::JumpingFountainWater:
1610                     case RCT12MiscEntityType::JumpingFountainSnow:
1611                         output = EntityType::JumpingFountain;
1612                         break;
1613                     case RCT12MiscEntityType::Balloon:
1614                         output = EntityType::Balloon;
1615                         break;
1616                     case RCT12MiscEntityType::Duck:
1617                         output = EntityType::Duck;
1618                         break;
1619                     default:
1620                         break;
1621                 }
1622                 break;
1623             case RCT12SpriteIdentifier::Litter:
1624                 output = EntityType::Litter;
1625                 break;
1626             default:
1627                 break;
1628         }
1629         return output;
1630     }
1631 
ImportEntityCommonProperties(EntityBase * dst,const RCT12SpriteBase * src)1632     void ImportEntityCommonProperties(EntityBase* dst, const RCT12SpriteBase* src)
1633     {
1634         dst->Type = GetEntityTypeFromRCT2Sprite(src);
1635         dst->sprite_height_negative = src->sprite_height_negative;
1636         dst->sprite_index = src->sprite_index;
1637         dst->x = src->x;
1638         dst->y = src->y;
1639         dst->z = src->z;
1640         dst->sprite_width = src->sprite_width;
1641         dst->sprite_height_positive = src->sprite_height_positive;
1642         dst->SpriteRect = ScreenRect(src->sprite_left, src->sprite_top, src->sprite_right, src->sprite_bottom);
1643         dst->sprite_direction = src->sprite_direction;
1644     }
1645 
1646     void ImportEntity(const RCT12SpriteBase& src);
1647 
GetUserString(rct_string_id stringId)1648     std::string GetUserString(rct_string_id stringId)
1649     {
1650         const auto originalString = _s6.custom_strings[(stringId - USER_STRING_START) % 1024];
1651         auto originalStringView = std::string_view(
1652             originalString, GetRCT2StringBufferLen(originalString, USER_STRING_MAX_LENGTH));
1653         auto asUtf8 = rct2_to_utf8(originalStringView, RCT2LanguageId::EnglishUK);
1654         auto justText = RCT12RemoveFormattingUTF8(asUtf8);
1655         return justText.data();
1656     }
1657 
1658     template<size_t TInternalLimit, typename T>
AddRequiredObjects(std::vector<rct_object_entry> & required,const T & list)1659     static void AddRequiredObjects(std::vector<rct_object_entry>& required, const T& list)
1660     {
1661         rct_object_entry nullEntry = {};
1662         std::memset(&nullEntry, 0xFF, sizeof(nullEntry));
1663 
1664         for (const auto& entry : list)
1665         {
1666             required.push_back(entry);
1667         }
1668 
1669         // NOTE: The segment of this object type needs to be filled to the internal limit
1670         // the object manager currently expects this.
1671         for (size_t i = std::size(list); i < TInternalLimit; i++)
1672         {
1673             required.push_back(nullEntry);
1674         }
1675     }
1676 
GetRequiredObjects()1677     ObjectList GetRequiredObjects()
1678     {
1679         std::vector<rct_object_entry> result;
1680 
1681         AddRequiredObjects<MAX_RIDE_OBJECTS>(result, _s6.RideObjects);
1682         AddRequiredObjects<MAX_SMALL_SCENERY_OBJECTS>(result, _s6.SceneryObjects);
1683         AddRequiredObjects<MAX_LARGE_SCENERY_OBJECTS>(result, _s6.LargeSceneryObjects);
1684         AddRequiredObjects<MAX_WALL_SCENERY_OBJECTS>(result, _s6.WallSceneryObjects);
1685         AddRequiredObjects<MAX_BANNER_OBJECTS>(result, _s6.BannerObjects);
1686         AddRequiredObjects<MAX_PATH_OBJECTS>(result, _s6.PathObjects);
1687         AddRequiredObjects<MAX_PATH_ADDITION_OBJECTS>(result, _s6.PathAdditionObjects);
1688         AddRequiredObjects<MAX_SCENERY_GROUP_OBJECTS>(result, _s6.SceneryGroupObjects);
1689         AddRequiredObjects<MAX_PARK_ENTRANCE_OBJECTS>(result, _s6.ParkEntranceObjects);
1690         AddRequiredObjects<MAX_WATER_OBJECTS>(result, _s6.WaterObjects);
1691         AddRequiredObjects<MAX_SCENARIO_TEXT_OBJECTS>(result, _s6.ScenarioTextObjects);
1692 
1693         ObjectList objectList;
1694         for (size_t i = 0; i < result.size(); i++)
1695         {
1696             ObjectType objectType;
1697             ObjectEntryIndex entryIndex;
1698             get_type_entry_index(i, &objectType, &entryIndex);
1699 
1700             auto desc = ObjectEntryDescriptor(result[i]);
1701             if (desc.HasValue())
1702             {
1703                 assert(desc.GetType() == objectType);
1704                 objectList.SetObject(entryIndex, desc);
1705             }
1706         }
1707 
1708         return objectList;
1709     }
1710 };
1711 
ImportEntity(const RCT12SpriteBase & baseSrc)1712 template<> void S6Importer::ImportEntity<Vehicle>(const RCT12SpriteBase& baseSrc)
1713 {
1714     auto dst = CreateEntityAt<Vehicle>(baseSrc.sprite_index);
1715     auto src = static_cast<const RCT2SpriteVehicle*>(&baseSrc);
1716     const auto& ride = _s6.rides[src->ride];
1717 
1718     ImportEntityCommonProperties(dst, src);
1719     dst->SubType = Vehicle::Type(src->type);
1720     dst->Pitch = src->Pitch;
1721     dst->bank_rotation = src->bank_rotation;
1722     dst->remaining_distance = src->remaining_distance;
1723     dst->velocity = src->velocity;
1724     dst->acceleration = src->acceleration;
1725     dst->ride = static_cast<ride_id_t>(src->ride);
1726     dst->vehicle_type = src->vehicle_type;
1727     dst->colours = src->colours;
1728     dst->track_progress = src->track_progress;
1729     dst->TrackLocation = { src->track_x, src->track_y, src->track_z };
1730     if (src->boat_location.IsNull() || static_cast<RideMode>(ride.mode) != RideMode::BoatHire
1731         || src->status != static_cast<uint8_t>(Vehicle::Status::TravellingBoat))
1732     {
1733         dst->BoatLocation.SetNull();
1734         dst->SetTrackDirection(src->GetTrackDirection());
1735         dst->SetTrackType(src->GetTrackType());
1736         // RotationControlToggle and Booster are saved as the same track piece ID
1737         // Which one the vehicle is using must be determined
1738         if (IsFlatRide(src->ride))
1739         {
1740             dst->SetTrackType(RCT12FlatTrackTypeToOpenRCT2(src->GetTrackType()));
1741         }
1742         else if (src->GetTrackType() == TrackElemType::RotationControlToggleAlias)
1743         {
1744             // Merging hacks mean the track type that's appropriate for the ride type is not necessarily the track type the
1745             // ride is on. It's possible to create unwanted behavior if a user layers spinning control track on top of
1746             // booster track but this is unlikely since only two rides have spinning control track - by default they load as
1747             // booster
1748             TileElement* tileElement2 = map_get_track_element_at_of_type_seq(
1749                 dst->TrackLocation, TrackElemType::RotationControlToggle, 0);
1750 
1751             if (tileElement2 != nullptr)
1752                 dst->SetTrackType(TrackElemType::RotationControlToggle);
1753         }
1754     }
1755     else
1756     {
1757         dst->BoatLocation = TileCoordsXY{ src->boat_location.x, src->boat_location.y }.ToCoordsXY();
1758         dst->SetTrackDirection(0);
1759         dst->SetTrackType(0);
1760     }
1761 
1762     dst->next_vehicle_on_train = src->next_vehicle_on_train;
1763     dst->prev_vehicle_on_ride = src->prev_vehicle_on_ride;
1764     dst->next_vehicle_on_ride = src->next_vehicle_on_ride;
1765     dst->var_44 = src->var_44;
1766     dst->mass = src->mass;
1767     dst->update_flags = src->update_flags;
1768     dst->SwingSprite = src->SwingSprite;
1769     dst->current_station = src->current_station;
1770     dst->current_time = src->current_time;
1771     dst->crash_z = src->crash_z;
1772 
1773     Vehicle::Status statusSrc = Vehicle::Status::MovingToEndOfStation;
1774     if (src->status <= static_cast<uint8_t>(Vehicle::Status::StoppedByBlockBrakes))
1775     {
1776         statusSrc = static_cast<Vehicle::Status>(src->status);
1777     }
1778 
1779     dst->status = statusSrc;
1780     dst->sub_state = src->sub_state;
1781     for (size_t i = 0; i < std::size(src->peep); i++)
1782     {
1783         dst->peep[i] = src->peep[i];
1784         dst->peep_tshirt_colours[i] = src->peep_tshirt_colours[i];
1785     }
1786     dst->num_seats = src->num_seats;
1787     dst->num_peeps = src->num_peeps;
1788     dst->next_free_seat = src->next_free_seat;
1789     dst->restraints_position = src->restraints_position;
1790     dst->crash_x = src->crash_x;
1791     dst->sound2_flags = src->sound2_flags;
1792     dst->spin_sprite = src->spin_sprite;
1793     dst->sound1_id = static_cast<OpenRCT2::Audio::SoundId>(src->sound1_id);
1794     dst->sound1_volume = src->sound1_volume;
1795     dst->sound2_id = static_cast<OpenRCT2::Audio::SoundId>(src->sound2_id);
1796     dst->sound2_volume = src->sound2_volume;
1797     dst->sound_vector_factor = src->sound_vector_factor;
1798     dst->time_waiting = src->time_waiting;
1799     dst->speed = src->speed;
1800     dst->powered_acceleration = src->powered_acceleration;
1801     dst->dodgems_collision_direction = src->dodgems_collision_direction;
1802     dst->animation_frame = src->animation_frame;
1803     dst->animationState = src->animationState;
1804     dst->scream_sound_id = static_cast<OpenRCT2::Audio::SoundId>(src->scream_sound_id);
1805     dst->TrackSubposition = VehicleTrackSubposition{ src->TrackSubposition };
1806     dst->var_CE = src->var_CE;
1807     dst->var_CF = src->var_CF;
1808     dst->lost_time_out = src->lost_time_out;
1809     dst->vertical_drop_countdown = src->vertical_drop_countdown;
1810     dst->var_D3 = src->var_D3;
1811     dst->mini_golf_current_animation = MiniGolfAnimation(src->mini_golf_current_animation);
1812     dst->mini_golf_flags = src->mini_golf_flags;
1813     dst->ride_subtype = RCTEntryIndexToOpenRCT2EntryIndex(src->ride_subtype);
1814     dst->colours_extended = src->colours_extended;
1815     dst->seat_rotation = src->seat_rotation;
1816     dst->target_seat_rotation = src->target_seat_rotation;
1817     dst->IsCrashedVehicle = src->flags & RCT12_SPRITE_FLAGS_IS_CRASHED_VEHICLE_SPRITE;
1818 }
1819 
AdjustScenarioToCurrentTicks(const rct_s6_data & s6,uint32_t tick)1820 static uint32_t AdjustScenarioToCurrentTicks(const rct_s6_data& s6, uint32_t tick)
1821 {
1822     // Previously gScenarioTicks was used as a time point, now it's gCurrentTicks.
1823     // gCurrentTicks and gScenarioTicks are now exported as the same, older saves that have a different
1824     // scenario tick must account for the difference between the two.
1825     uint32_t ticksElapsed = s6.scenario_ticks - tick;
1826     return s6.game_ticks_1 - ticksElapsed;
1827 }
1828 
ImportEntity(const RCT12SpriteBase & baseSrc)1829 template<> void S6Importer::ImportEntity<Guest>(const RCT12SpriteBase& baseSrc)
1830 {
1831     auto dst = CreateEntityAt<Guest>(baseSrc.sprite_index);
1832     auto src = static_cast<const RCT2SpritePeep*>(&baseSrc);
1833     ImportEntityPeep(dst, src);
1834 
1835     dst->OutsideOfPark = static_cast<bool>(src->outside_of_park);
1836     dst->GuestNumRides = src->no_of_rides;
1837     dst->Happiness = src->happiness;
1838     dst->HappinessTarget = src->happiness_target;
1839     dst->Nausea = src->nausea;
1840     dst->NauseaTarget = src->nausea_target;
1841     dst->Hunger = src->hunger;
1842     dst->Thirst = src->thirst;
1843     dst->Toilet = src->toilet;
1844     dst->TimeToConsume = src->time_to_consume;
1845     dst->Intensity = static_cast<IntensityRange>(src->intensity);
1846     dst->NauseaTolerance = static_cast<PeepNauseaTolerance>(src->nausea_tolerance);
1847     dst->PaidOnDrink = src->paid_on_drink;
1848 
1849     OpenRCT2::RideUse::GetHistory().Set(dst->sprite_index, RCT12GetRidesBeenOn(src));
1850     OpenRCT2::RideUse::GetTypeHistory().Set(dst->sprite_index, RCT12GetRideTypesBeenOn(src));
1851 
1852     dst->SetItemFlags(src->GetItemFlags());
1853     dst->Photo1RideRef = RCT12RideIdToOpenRCT2RideId(src->photo1_ride_ref);
1854     dst->Photo2RideRef = RCT12RideIdToOpenRCT2RideId(src->photo2_ride_ref);
1855     dst->Photo3RideRef = RCT12RideIdToOpenRCT2RideId(src->photo3_ride_ref);
1856     dst->Photo4RideRef = RCT12RideIdToOpenRCT2RideId(src->photo4_ride_ref);
1857     dst->GuestNextInQueue = src->next_in_queue;
1858     dst->TimeInQueue = src->time_in_queue;
1859     dst->CashInPocket = src->cash_in_pocket;
1860     dst->CashSpent = src->cash_spent;
1861     dst->ParkEntryTime = AdjustScenarioToCurrentTicks(_s6, src->park_entry_time);
1862     dst->RejoinQueueTimeout = src->rejoin_queue_timeout;
1863     dst->PreviousRide = RCT12RideIdToOpenRCT2RideId(src->previous_ride);
1864     dst->PreviousRideTimeOut = src->previous_ride_time_out;
1865     for (size_t i = 0; i < std::size(src->thoughts); i++)
1866     {
1867         auto srcThought = &src->thoughts[i];
1868         auto dstThought = &dst->Thoughts[i];
1869         dstThought->type = static_cast<PeepThoughtType>(srcThought->type);
1870         if (srcThought->item == RCT12PeepThoughtItemNone)
1871             dstThought->item = PeepThoughtItemNone;
1872         else
1873             dstThought->item = srcThought->item;
1874         dstThought->freshness = srcThought->freshness;
1875         dstThought->fresh_timeout = srcThought->fresh_timeout;
1876     }
1877     dst->GuestHeadingToRideId = RCT12RideIdToOpenRCT2RideId(src->guest_heading_to_ride_id);
1878     dst->GuestIsLostCountdown = src->peep_is_lost_countdown;
1879     dst->LitterCount = src->litter_count;
1880     dst->GuestTimeOnRide = src->time_on_ride;
1881     dst->DisgustingCount = src->disgusting_count;
1882     dst->PaidToEnter = src->paid_to_enter;
1883     dst->PaidOnRides = src->paid_on_rides;
1884     dst->PaidOnFood = src->paid_on_food;
1885     dst->PaidOnSouvenirs = src->paid_on_souvenirs;
1886     dst->AmountOfFood = src->no_of_food;
1887     dst->AmountOfDrinks = src->no_of_drinks;
1888     dst->AmountOfSouvenirs = src->no_of_souvenirs;
1889     dst->VandalismSeen = src->vandalism_seen;
1890     dst->VoucherType = src->voucher_type;
1891     dst->VoucherRideId = RCT12RideIdToOpenRCT2RideId(src->voucher_arguments);
1892     dst->SurroundingsThoughtTimeout = src->surroundings_thought_timeout;
1893     dst->Angriness = src->angriness;
1894     dst->TimeLost = src->time_lost;
1895     dst->DaysInQueue = src->days_in_queue;
1896     dst->BalloonColour = src->balloon_colour;
1897     dst->UmbrellaColour = src->umbrella_colour;
1898     dst->HatColour = src->hat_colour;
1899     dst->FavouriteRide = RCT12RideIdToOpenRCT2RideId(src->favourite_ride);
1900     dst->FavouriteRideRating = src->favourite_ride_rating;
1901 }
1902 
ImportEntity(const RCT12SpriteBase & baseSrc)1903 template<> void S6Importer::ImportEntity<Staff>(const RCT12SpriteBase& baseSrc)
1904 {
1905     auto dst = CreateEntityAt<Staff>(baseSrc.sprite_index);
1906     auto src = static_cast<const RCT2SpritePeep*>(&baseSrc);
1907     ImportEntityPeep(dst, src);
1908 
1909     dst->AssignedStaffType = StaffType(src->staff_type);
1910     dst->MechanicTimeSinceCall = src->mechanic_time_since_call;
1911 
1912     dst->HireDate = src->park_entry_time;
1913     dst->StaffOrders = src->staff_orders;
1914     dst->StaffMowingTimeout = src->staff_mowing_timeout;
1915     dst->StaffLawnsMown = src->paid_to_enter;
1916     dst->StaffGardensWatered = src->paid_on_rides;
1917     dst->StaffLitterSwept = src->paid_on_food;
1918     dst->StaffBinsEmptied = src->paid_on_souvenirs;
1919 
1920     ImportStaffPatrolArea(dst, src->staff_id);
1921 }
1922 
ImportEntity(const RCT12SpriteBase & baseSrc)1923 template<> void S6Importer::ImportEntity<SteamParticle>(const RCT12SpriteBase& baseSrc)
1924 {
1925     auto dst = CreateEntityAt<SteamParticle>(baseSrc.sprite_index);
1926     auto src = static_cast<const RCT12SpriteSteamParticle*>(&baseSrc);
1927     ImportEntityCommonProperties(dst, src);
1928     dst->time_to_move = src->time_to_move;
1929     dst->frame = src->frame;
1930 }
1931 
ImportEntity(const RCT12SpriteBase & baseSrc)1932 template<> void S6Importer::ImportEntity<MoneyEffect>(const RCT12SpriteBase& baseSrc)
1933 {
1934     auto dst = CreateEntityAt<MoneyEffect>(baseSrc.sprite_index);
1935     auto src = static_cast<const RCT12SpriteMoneyEffect*>(&baseSrc);
1936     ImportEntityCommonProperties(dst, src);
1937     dst->MoveDelay = src->move_delay;
1938     dst->NumMovements = src->num_movements;
1939     dst->Vertical = src->vertical;
1940     dst->Value = src->value;
1941     dst->OffsetX = src->offset_x;
1942     dst->Wiggle = src->wiggle;
1943 }
1944 
ImportEntity(const RCT12SpriteBase & baseSrc)1945 template<> void S6Importer::ImportEntity<VehicleCrashParticle>(const RCT12SpriteBase& baseSrc)
1946 {
1947     auto dst = CreateEntityAt<VehicleCrashParticle>(baseSrc.sprite_index);
1948     auto src = static_cast<const RCT12SpriteCrashedVehicleParticle*>(&baseSrc);
1949     ImportEntityCommonProperties(dst, src);
1950     dst->frame = src->frame;
1951     dst->time_to_live = src->time_to_live;
1952     dst->frame = src->frame;
1953     dst->colour[0] = src->colour[0];
1954     dst->colour[1] = src->colour[1];
1955     dst->crashed_sprite_base = src->crashed_sprite_base;
1956     dst->velocity_x = src->velocity_x;
1957     dst->velocity_y = src->velocity_y;
1958     dst->velocity_z = src->velocity_z;
1959     dst->acceleration_x = src->acceleration_x;
1960     dst->acceleration_y = src->acceleration_y;
1961     dst->acceleration_z = src->acceleration_z;
1962 }
1963 
ImportEntity(const RCT12SpriteBase & baseSrc)1964 template<> void S6Importer::ImportEntity<ExplosionCloud>(const RCT12SpriteBase& baseSrc)
1965 {
1966     auto dst = CreateEntityAt<ExplosionCloud>(baseSrc.sprite_index);
1967     auto src = static_cast<const RCT12SpriteParticle*>(&baseSrc);
1968     ImportEntityCommonProperties(dst, src);
1969     dst->frame = src->frame;
1970 }
1971 
ImportEntity(const RCT12SpriteBase & baseSrc)1972 template<> void S6Importer::ImportEntity<ExplosionFlare>(const RCT12SpriteBase& baseSrc)
1973 {
1974     auto dst = CreateEntityAt<ExplosionFlare>(baseSrc.sprite_index);
1975     auto src = static_cast<const RCT12SpriteParticle*>(&baseSrc);
1976     ImportEntityCommonProperties(dst, src);
1977     dst->frame = src->frame;
1978 }
1979 
ImportEntity(const RCT12SpriteBase & baseSrc)1980 template<> void S6Importer::ImportEntity<CrashSplashParticle>(const RCT12SpriteBase& baseSrc)
1981 {
1982     auto dst = CreateEntityAt<CrashSplashParticle>(baseSrc.sprite_index);
1983     auto src = static_cast<const RCT12SpriteParticle*>(&baseSrc);
1984     ImportEntityCommonProperties(dst, src);
1985     dst->frame = src->frame;
1986 }
1987 
ImportEntity(const RCT12SpriteBase & baseSrc)1988 template<> void S6Importer::ImportEntity<JumpingFountain>(const RCT12SpriteBase& baseSrc)
1989 {
1990     auto dst = CreateEntityAt<JumpingFountain>(baseSrc.sprite_index);
1991     auto src = static_cast<const RCT12SpriteJumpingFountain*>(&baseSrc);
1992     ImportEntityCommonProperties(dst, src);
1993     dst->NumTicksAlive = src->num_ticks_alive;
1994     dst->frame = src->frame;
1995     dst->FountainFlags = src->fountain_flags;
1996     dst->TargetX = src->target_x;
1997     dst->TargetY = src->target_y;
1998     dst->Iteration = src->iteration;
1999     dst->FountainType = RCT12MiscEntityType(src->type) == RCT12MiscEntityType::JumpingFountainSnow ? JumpingFountainType::Snow
2000                                                                                                    : JumpingFountainType::Water;
2001 }
2002 
ImportEntity(const RCT12SpriteBase & baseSrc)2003 template<> void S6Importer::ImportEntity<Balloon>(const RCT12SpriteBase& baseSrc)
2004 {
2005     auto dst = CreateEntityAt<Balloon>(baseSrc.sprite_index);
2006     auto src = static_cast<const RCT12SpriteBalloon*>(&baseSrc);
2007     ImportEntityCommonProperties(dst, src);
2008     dst->popped = src->popped;
2009     dst->time_to_move = src->time_to_move;
2010     dst->frame = src->frame;
2011     dst->colour = src->colour;
2012 }
2013 
ImportEntity(const RCT12SpriteBase & baseSrc)2014 template<> void S6Importer::ImportEntity<Duck>(const RCT12SpriteBase& baseSrc)
2015 {
2016     auto dst = CreateEntityAt<Duck>(baseSrc.sprite_index);
2017     auto src = static_cast<const RCT12SpriteDuck*>(&baseSrc);
2018     ImportEntityCommonProperties(dst, src);
2019     dst->frame = src->frame;
2020     dst->target_x = src->target_x;
2021     dst->target_y = src->target_y;
2022     dst->state = static_cast<Duck::DuckState>(src->state);
2023 }
2024 
ImportEntity(const RCT12SpriteBase & baseSrc)2025 template<> void S6Importer::ImportEntity<Litter>(const RCT12SpriteBase& baseSrc)
2026 {
2027     auto dst = CreateEntityAt<Litter>(baseSrc.sprite_index);
2028     auto src = static_cast<const RCT12SpriteLitter*>(&baseSrc);
2029     ImportEntityCommonProperties(dst, src);
2030     dst->SubType = Litter::Type(src->type);
2031     dst->creationTick = AdjustScenarioToCurrentTicks(_s6, src->creationTick);
2032 }
2033 
ImportEntity(const RCT12SpriteBase & src)2034 void S6Importer::ImportEntity(const RCT12SpriteBase& src)
2035 {
2036     switch (GetEntityTypeFromRCT2Sprite(&src))
2037     {
2038         case EntityType::Vehicle:
2039             ImportEntity<Vehicle>(src);
2040             break;
2041         case EntityType::Guest:
2042             ImportEntity<Guest>(src);
2043             break;
2044         case EntityType::Staff:
2045             ImportEntity<Staff>(src);
2046             break;
2047         case EntityType::SteamParticle:
2048             ImportEntity<SteamParticle>(src);
2049             break;
2050         case EntityType::MoneyEffect:
2051             ImportEntity<MoneyEffect>(src);
2052             break;
2053         case EntityType::CrashedVehicleParticle:
2054             ImportEntity<VehicleCrashParticle>(src);
2055             break;
2056         case EntityType::ExplosionCloud:
2057             ImportEntity<ExplosionCloud>(src);
2058             break;
2059         case EntityType::ExplosionFlare:
2060             ImportEntity<ExplosionFlare>(src);
2061             break;
2062         case EntityType::CrashSplash:
2063             ImportEntity<CrashSplashParticle>(src);
2064             break;
2065         case EntityType::JumpingFountain:
2066             ImportEntity<JumpingFountain>(src);
2067             break;
2068         case EntityType::Balloon:
2069             ImportEntity<Balloon>(src);
2070             break;
2071         case EntityType::Duck:
2072             ImportEntity<Duck>(src);
2073             break;
2074         case EntityType::Litter:
2075             ImportEntity<Litter>(src);
2076             break;
2077         default:
2078             // Null elements do not need imported
2079             break;
2080     }
2081 }
2082 
CreateS6(IObjectRepository & objectRepository)2083 std::unique_ptr<IParkImporter> ParkImporter::CreateS6(IObjectRepository& objectRepository)
2084 {
2085     return std::make_unique<S6Importer>(objectRepository);
2086 }
2087 
show_error(uint8_t errorType,rct_string_id errorStringId)2088 static void show_error(uint8_t errorType, rct_string_id errorStringId)
2089 {
2090     if (errorType == ERROR_TYPE_GENERIC)
2091     {
2092         context_show_error(errorStringId, STR_NONE, {});
2093     }
2094     context_show_error(STR_UNABLE_TO_LOAD_FILE, errorStringId, {});
2095 }
2096 
load_from_sv6(const char * path)2097 void load_from_sv6(const char* path)
2098 {
2099     auto context = OpenRCT2::GetContext();
2100     auto s6Importer = std::make_unique<S6Importer>(context->GetObjectRepository());
2101     try
2102     {
2103         auto& objectMgr = context->GetObjectManager();
2104         auto result = s6Importer->LoadSavedGame(path);
2105         objectMgr.LoadObjects(result.RequiredObjects);
2106         s6Importer->Import();
2107         game_fix_save_vars();
2108         AutoCreateMapAnimations();
2109         EntityTweener::Get().Reset();
2110         gScreenAge = 0;
2111         gLastAutoSaveUpdate = AUTOSAVE_PAUSE;
2112     }
2113     catch (const ObjectLoadException&)
2114     {
2115         show_error(ERROR_TYPE_FILE_LOAD, STR_FILE_CONTAINS_INVALID_DATA);
2116     }
2117     catch (const IOException& loadError)
2118     {
2119         log_error("Error loading: %s", loadError.what());
2120         show_error(ERROR_TYPE_FILE_LOAD, STR_GAME_SAVE_FAILED);
2121     }
2122     catch (const UnsupportedRideTypeException&)
2123     {
2124         show_error(ERROR_TYPE_FILE_LOAD, STR_FILE_CONTAINS_UNSUPPORTED_RIDE_TYPES);
2125     }
2126     catch (const std::exception&)
2127     {
2128         show_error(ERROR_TYPE_FILE_LOAD, STR_FILE_CONTAINS_INVALID_DATA);
2129     }
2130 }
2131 
2132 /**
2133  *
2134  *  rct2: 0x00676053
2135  * scenario (ebx)
2136  */
load_from_sc6(const char * path)2137 void load_from_sc6(const char* path)
2138 {
2139     auto context = OpenRCT2::GetContext();
2140     auto& objManager = context->GetObjectManager();
2141     auto s6Importer = std::make_unique<S6Importer>(context->GetObjectRepository());
2142     try
2143     {
2144         auto result = s6Importer->LoadScenario(path);
2145         objManager.LoadObjects(result.RequiredObjects);
2146         s6Importer->Import();
2147         game_fix_save_vars();
2148         AutoCreateMapAnimations();
2149         EntityTweener::Get().Reset();
2150         return;
2151     }
2152     catch (const ObjectLoadException& loadError)
2153     {
2154         log_error("Error loading: %s", loadError.what());
2155         show_error(ERROR_TYPE_FILE_LOAD, STR_GAME_SAVE_FAILED);
2156     }
2157     catch (const IOException& loadError)
2158     {
2159         log_error("Error loading: %s", loadError.what());
2160         show_error(ERROR_TYPE_FILE_LOAD, STR_GAME_SAVE_FAILED);
2161     }
2162     catch (const std::exception&)
2163     {
2164         show_error(ERROR_TYPE_FILE_LOAD, STR_FILE_CONTAINS_INVALID_DATA);
2165     }
2166     gScreenAge = 0;
2167     gLastAutoSaveUpdate = AUTOSAVE_PAUSE;
2168 }
2169