1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-15 11:03:00 +01:00
Files
OpenRCT2/src/rct1/S4Importer.cpp
2016-07-08 22:58:38 +01:00

1611 lines
51 KiB
C++

#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#include "S4Importer.h"
#include "../core/Exception.hpp"
#include "../core/Guard.hpp"
#include "../core/List.hpp"
#include "../core/Path.hpp"
#include "../core/String.hpp"
#include "../core/Util.hpp"
#include "../object/ObjectManager.h"
#include "Tables.h"
extern "C"
{
#include "../audio/audio.h"
#include "../cheats.h"
#include "../editor.h"
#include "../game.h"
#include "../interface/window.h"
#include "../localisation/date.h"
#include "../localisation/localisation.h"
#include "../management/finance.h"
#include "../management/marketing.h"
#include "../object.h"
#include "../peep/staff.h"
#include "../rct1.h"
#include "../util/sawyercoding.h"
#include "../util/util.h"
#include "../world/climate.h"
#include "../world/footpath.h"
#include "../world/map_animation.h"
#include "../world/park.h"
#include "../world/scenery.h"
}
static bool ObjectNameComparer(const char * a, const char * b)
{
return String::Equals(a, b, true);
}
void S4Importer::LoadSavedGame(const utf8 * path)
{
if (!rct1_read_sv4(path, &_s4)) {
throw Exception("Unable to load SV4.");
}
_s4Path = path;
}
void S4Importer::LoadScenario(const utf8 * path)
{
if (!rct1_read_sc4(path, &_s4)) {
throw Exception("Unable to load SC4.");
}
_s4Path = path;
}
void S4Importer::Import()
{
Initialise();
CreateAvailableObjectMappings();
LoadObjects();
ImportRides();
ImportRideMeasurements();
ImportMapElements();
ImportMapAnimations();
ImportPeepSpawns();
ImportFinance();
ImportResearch();
ImportParkName();
ImportParkFlags();
ImportClimate();
ImportScenarioNameDetails();
ImportScenarioObjective();
ImportSavedView();
game_convert_strings_to_utf8();
}
void S4Importer::Initialise()
{
_gameVersion = sawyercoding_detect_rct1_version(_s4.game_version) & FILE_VERSION_MASK;
Memory::Set(_rideTypeToRideEntryMap, 255, sizeof(_rideTypeToRideEntryMap));
Memory::Set(_vehicleTypeToRideEntryMap, 255, sizeof(_vehicleTypeToRideEntryMap));
Memory::Set(_smallSceneryTypeToEntryMap, 255, sizeof(_smallSceneryTypeToEntryMap));
Memory::Set(_largeSceneryTypeToEntryMap, 255, sizeof(_largeSceneryTypeToEntryMap));
Memory::Set(_wallTypeToEntryMap, 255, sizeof(_wallTypeToEntryMap));
Memory::Set(_pathTypeToEntryMap, 255, sizeof(_pathTypeToEntryMap));
Memory::Set(_pathAdditionTypeToEntryMap, 255, sizeof(_pathAdditionTypeToEntryMap));
Memory::Set(_sceneryThemeTypeToEntryMap, 255, sizeof(_sceneryThemeTypeToEntryMap));
uint16 mapSize = _s4.map_size == 0 ? 128 : _s4.map_size;
// Do map initialisation, same kind of stuff done when loading scenario editor
audio_pause_sounds();
audio_unpause_sounds();
object_unload_all();
map_init(mapSize);
banner_init();
reset_park_entrances();
user_string_clear_all();
reset_sprite_list();
ride_init_all();
window_guest_list_init_vars_a();
staff_reset_modes();
park_init();
finance_init();
date_reset();
window_guest_list_init_vars_b();
window_staff_list_init_vars();
gS6Info->editor_step = EDITOR_STEP_OBJECT_SELECTION;
gParkFlags |= PARK_FLAGS_SHOW_REAL_GUEST_NAMES;
window_new_ride_init_vars();
RCT2_GLOBAL(0x0141F571, uint8) = 4;
news_item_init_queue();
}
void S4Importer::CreateAvailableObjectMappings()
{
AddDefaultEntries();
AddAvailableEntriesFromResearchList();
AddAvailableEntriesFromMap();
AddAvailableEntriesFromRides();
AddAvailableEntriesFromSceneryGroups();
}
void S4Importer::AddDefaultEntries()
{
// Add default scenery groups
_sceneryGroupEntries.AddRange({
"SCGTREES",
"SCGSHRUB",
"SCGGARDN",
"SCGFENCE",
"SCGWALLS",
"SCGPATHX",
});
// Add default footpaths
_pathEntries.AddRange({
"TARMAC ",
"TARMACG ",
"TARMACB ",
"PATHCRZY",
"PATHSPCE",
"PATHDIRT",
"PATHASH ",
"ROAD ",
});
}
void S4Importer::AddAvailableEntriesFromResearchList()
{
size_t researchListCount;
const rct1_research_item * researchList = GetResearchList(&researchListCount);
for (size_t i = 0; i < researchListCount; i++)
{
const rct1_research_item * researchItem = &researchList[i];
if (researchItem->item == RCT1_RESEARCH_END_RESEARCHABLE ||
researchItem->item == RCT1_RESEARCH_END)
{
break;
}
if (researchItem->item == RCT1_RESEARCH_END_AVAILABLE)
{
continue;
}
switch (researchItem->category) {
case RCT1_RESEARCH_CATEGORY_THEME:
AddEntriesForSceneryTheme(researchItem->item);
break;
case RCT1_RESEARCH_CATEGORY_RIDE:
{
uint8 rideType = researchItem->item;
// Add all vehicles for this ride type
uint32 numVehicles = 0;
for (size_t j = 0; j < researchListCount; j++)
{
const rct1_research_item *researchItem2 = &researchList[j];
if (researchItem2->item == RCT1_RESEARCH_END_RESEARCHABLE ||
researchItem2->item == RCT1_RESEARCH_END_AVAILABLE)
{
break;
}
if (researchItem2->category == RCT1_RESEARCH_CATEGORY_VEHICLE &&
researchItem2->related_ride == rideType)
{
AddEntryForVehicleType(rideType, researchItem2->item);
numVehicles++;
}
}
// If no vehicles found so just add the default for this ride
if (numVehicles == 0)
{
AddEntryForRideType(rideType);
}
break;
}
}
}
}
void S4Importer::AddAvailableEntriesFromMap()
{
size_t maxTiles = 128 * 128;
size_t tileIndex = 0;
rct_map_element * mapElement = _s4.map_elements;
while (tileIndex < maxTiles)
{
switch (map_element_get_type(mapElement)) {
case MAP_ELEMENT_TYPE_PATH:
{
uint8 pathColour = mapElement->type & 3;
uint8 pathType = (mapElement->properties.path.type & 0xF0) >> 4;
pathType = (pathType << 2) | pathColour;
uint8 pathAdditionsType = mapElement->properties.path.additions & 0x0F;
AddEntryForPath(pathType);
AddEntryForPathAddition(pathAdditionsType);
break;
}
case MAP_ELEMENT_TYPE_SCENERY:
AddEntryForSmallScenery(mapElement->properties.scenery.type);
break;
case MAP_ELEMENT_TYPE_SCENERY_MULTIPLE:
AddEntryForLargeScenery(mapElement->properties.scenerymultiple.type & MAP_ELEMENT_LARGE_TYPE_MASK);
break;
case MAP_ELEMENT_TYPE_FENCE:
{
uint8 var_05 = mapElement->properties.fence.item[0];
uint16 var_06 = mapElement->properties.fence.item[1] |
(mapElement->properties.fence.item[2] << 8);
for (int edge = 0; edge < 4; edge++)
{
int typeA = (var_05 >> (edge * 2)) & 3;
int typeB = (var_06 >> (edge * 4)) & 0x0F;
if (typeB != 0x0F)
{
uint8 type = typeA | (typeB << 2);
AddEntryForWall(type);
}
}
break;
}
}
if (map_element_is_last_for_tile(mapElement++))
{
tileIndex++;
}
}
}
void S4Importer::AddAvailableEntriesFromRides()
{
for (size_t i = 0; i < Util::CountOf(_s4.rides); i++)
{
rct1_ride * ride = &_s4.rides[i];
if (ride->type != RCT1_RIDE_TYPE_NULL && RCT1::RideTypeUsesVehicles(ride->type))
{
AddEntryForVehicleType(ride->type, ride->vehicle_type);
}
}
}
void S4Importer::AddAvailableEntriesFromSceneryGroups()
{
for (int sceneryTheme = 0; sceneryTheme <= RCT1_SCENERY_THEME_PAGODA; sceneryTheme++)
{
if (sceneryTheme != 0 &&
_sceneryThemeTypeToEntryMap[sceneryTheme] == 255) continue;
List<const char *> objects = RCT1::GetSceneryObjects(sceneryTheme);
for (const char * objectName : objects)
{
rct_object_entry * foundEntry = object_list_find_by_name(objectName);
if (foundEntry != nullptr)
{
uint8 objectType = foundEntry->flags & 0x0F;
switch (objectType) {
case OBJECT_TYPE_SMALL_SCENERY:
case OBJECT_TYPE_LARGE_SCENERY:
case OBJECT_TYPE_WALLS:
case OBJECT_TYPE_PATHS:
case OBJECT_TYPE_PATH_BITS:
{
List<const char *> * entries = GetEntryList(objectType);
// Ran out of available entries
if (entries->GetCount() >= (size_t)object_entry_group_counts[objectType])
{
break;
}
if (!entries->Contains(objectName, ObjectNameComparer))
{
entries->Add(objectName);
}
break;
}
}
}
}
}
}
void S4Importer::AddEntryForRideType(uint8 rideType)
{
assert(rideType < Util::CountOf(_rideTypeToRideEntryMap));
if (_rideTypeToRideEntryMap[rideType] == 255)
{
const char * entryName = RCT1::GetRideTypeObject(rideType);
_rideTypeToRideEntryMap[rideType] = (uint8)_rideEntries.GetCount();
_rideEntries.Add(entryName);
}
}
void S4Importer::AddEntryForVehicleType(uint8 rideType, uint8 vehicleType)
{
assert(vehicleType < Util::CountOf(_vehicleTypeToRideEntryMap));
if (_vehicleTypeToRideEntryMap[vehicleType] == 255)
{
const char * entryName = RCT1::GetVehicleObject(vehicleType);
uint8 rideEntryIndex = (uint8)_rideEntries.GetCount();
_vehicleTypeToRideEntryMap[vehicleType] = rideEntryIndex;
_rideEntries.Add(entryName);
// Just overwrite this with the vehicle entry for now...
_rideTypeToRideEntryMap[rideType] = rideEntryIndex;
}
}
void S4Importer::AddEntryForSmallScenery(uint8 smallSceneryType)
{
assert(smallSceneryType < Util::CountOf(_smallSceneryTypeToEntryMap));
if (_smallSceneryTypeToEntryMap[smallSceneryType] == 255)
{
const char * entryName = RCT1::GetSmallSceneryObject(smallSceneryType);
_smallSceneryTypeToEntryMap[smallSceneryType] = (uint8)_smallSceneryEntries.GetCount();
_smallSceneryEntries.Add(entryName);
}
}
void S4Importer::AddEntryForLargeScenery(uint8 largeSceneryType)
{
assert(largeSceneryType < Util::CountOf(_largeSceneryTypeToEntryMap));
if (_largeSceneryTypeToEntryMap[largeSceneryType] == 255)
{
const char * entryName = RCT1::GetLargeSceneryObject(largeSceneryType);
_largeSceneryTypeToEntryMap[largeSceneryType] = (uint8)_largeSceneryEntries.GetCount();
_largeSceneryEntries.Add(entryName);
}
}
void S4Importer::AddEntryForWall(uint8 wallType)
{
assert(wallType < Util::CountOf(_wallTypeToEntryMap));
if (_wallTypeToEntryMap[wallType] == 255)
{
const char * entryName = RCT1::GetWallObject(wallType);
_wallTypeToEntryMap[wallType] = (uint8)_wallEntries.GetCount();
_wallEntries.Add(entryName);
}
}
void S4Importer::AddEntryForPath(uint8 pathType)
{
assert(pathType < Util::CountOf(_pathTypeToEntryMap));
if (_pathTypeToEntryMap[pathType] == 255)
{
const char * entryName = RCT1::GetPathObject(pathType);
size_t index = _pathEntries.IndexOf(entryName, ObjectNameComparer);
if (index != SIZE_MAX)
{
_pathTypeToEntryMap[pathType] = (uint8)index;
}
else
{
_pathTypeToEntryMap[pathType] = (uint8)_pathEntries.GetCount();
_pathEntries.Add(entryName);
}
}
}
void S4Importer::AddEntryForPathAddition(uint8 pathAdditionType)
{
if (pathAdditionType == RCT1_PATH_ADDITION_NONE) return;
if (_pathAdditionTypeToEntryMap[pathAdditionType] == 255)
{
uint8 normalisedPathAdditionType = RCT1::NormalisePathAddition(pathAdditionType);
if (_pathAdditionTypeToEntryMap[normalisedPathAdditionType] == 255)
{
const char * entryName = RCT1::GetPathAddtionObject(normalisedPathAdditionType);
_pathAdditionTypeToEntryMap[normalisedPathAdditionType] = (uint8)_pathAdditionEntries.GetCount();
_pathAdditionEntries.Add(entryName);
}
_pathAdditionTypeToEntryMap[pathAdditionType] = _pathAdditionTypeToEntryMap[normalisedPathAdditionType];
}
}
void S4Importer::AddEntriesForSceneryTheme(uint8 sceneryThemeType)
{
if (sceneryThemeType == RCT1_SCENERY_THEME_GENERAL ||
sceneryThemeType == RCT1_SCENERY_THEME_JUMPING_FOUNTAINS ||
sceneryThemeType == RCT1_SCENERY_THEME_GARDEN_CLOCK)
{
_sceneryThemeTypeToEntryMap[sceneryThemeType] = 254;
}
else
{
const char * entryName = RCT1::GetSceneryGroupObject(sceneryThemeType);
_sceneryThemeTypeToEntryMap[sceneryThemeType] = (uint8)_sceneryGroupEntries.GetCount();
_sceneryGroupEntries.Add(entryName);
}
}
void S4Importer::ImportRides()
{
for (int i = 0; i < MAX_RIDES; i++)
{
if (_s4.rides[i].type != RIDE_TYPE_NULL)
{
ImportRide(get_ride(i), &_s4.rides[i]);
}
}
}
void S4Importer::ImportRide(rct_ride * dst, rct1_ride * src)
{
memset(dst, 0, sizeof(rct_ride));
dst->type = RCT1::GetRideType(src->type);
if (RCT1::RideTypeUsesVehicles(src->type))
{
dst->subtype = _vehicleTypeToRideEntryMap[src->vehicle_type];
}
else
{
dst->subtype = _rideTypeToRideEntryMap[src->type];
}
rct_ride_entry * rideEntry = get_ride_entry(dst->subtype);
// Ride name
dst->name = 0;
if (is_user_string_id(src->name))
{
const char * rideName = GetUserString(src->name);
if (rideName[0] != 0)
{
rct_string_id rideNameStringId = user_string_allocate(4, rideName);
if (rideNameStringId != 0)
{
dst->name = rideNameStringId;
}
}
}
if (dst->name == 0)
{
dst->name = 1;
uint16 * args = (uint16*)&dst->name_arguments;
args[0] = 2 + dst->type;
args[1] = src->name_argument_number;
}
// We can't convert vehicles yet so just close the ride
dst->status = RIDE_STATUS_CLOSED;
// Flags
if (src->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO) dst->lifecycle_flags |= RIDE_LIFECYCLE_ON_RIDE_PHOTO;
if (src->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) dst->lifecycle_flags |= RIDE_LIFECYCLE_MUSIC;
if (src->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE) dst->lifecycle_flags |= RIDE_LIFECYCLE_INDESTRUCTIBLE;
if (src->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK) dst->lifecycle_flags |= RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK;
// Station
dst->overall_view = src->overall_view;
for (int i = 0; i < 4; i++)
{
dst->station_starts[i] = src->station_starts[i];
dst->station_heights[i] = src->station_height[i] / 2;
dst->station_length[i] = src->station_length[i];
dst->station_depart[i] = src->station_light[i];
// Use src->station_depart[i] when we import with guests and vehicles intact
dst->train_at_station[i] = 0xFF;
dst->entrances[i] = src->entrance[i];
dst->exits[i] = src->exit[i];
dst->queue_time[i] = src->queue_time[i];
dst->last_peep_in_queue[i] = 0xFFFF;
}
dst->num_stations = src->num_stations;
for (int i = 0; i < 32; i++)
{
dst->vehicles[i] = SPRITE_INDEX_NULL;
}
dst->num_vehicles = src->num_trains;
dst->num_cars_per_train = src->num_cars_per_train + rideEntry->zero_cars;
dst->proposed_num_vehicles = src->num_trains;
dst->max_trains = 32;
dst->proposed_num_cars_per_train = src->num_cars_per_train + rideEntry->zero_cars;
// Operation
dst->depart_flags = src->depart_flags;
dst->min_waiting_time = src->min_waiting_time;
dst->max_waiting_time = src->max_waiting_time;
dst->operation_option = src->operation_option;
dst->num_circuits = 1;
dst->min_max_cars_per_train = (rideEntry->min_cars_in_train << 4) | rideEntry->max_cars_in_train;
// RCT1 used 5mph / 8 km/h for every lift hill
dst->lift_hill_speed = 5;
if (_gameVersion == FILE_VERSION_RCT1)
{
// Original RCT had no music settings, take default style
dst->music = RCT2_ADDRESS(0x0097D4F4, uint8)[dst->type * 8];
}
else
{
dst->music = src->music;
}
if (src->operating_mode == RCT1_RIDE_MODE_POWERED_LAUNCH)
{
// Launched rides never passed through the station in RCT1.
dst->mode = RIDE_MODE_POWERED_LAUNCH;
}
else
{
dst->mode = src->operating_mode;
}
// Colours
dst->colour_scheme_type = src->colour_scheme;
if (_gameVersion == FILE_VERSION_RCT1)
{
dst->track_colour_main[0] = RCT1::GetColour(src->track_primary_colour);
dst->track_colour_additional[0] = RCT1::GetColour(src->track_secondary_colour);
dst->track_colour_supports[0] = RCT1::GetColour(src->track_support_colour);
}
else
{
for (int i = 0; i < 4; i++)
{
dst->track_colour_main[i] = RCT1::GetColour(src->track_colour_main[i]);
dst->track_colour_additional[i] = RCT1::GetColour(src->track_colour_additional[i]);
dst->track_colour_supports[i] = RCT1::GetColour(src->track_colour_supports[i]);
}
// Entrance styles were introduced with AA. They correspond directly with those in RCT2.
dst->entrance_style = src->entrance_style;
}
if (_gameVersion < FILE_VERSION_RCT1_LL && dst->type == RIDE_TYPE_MERRY_GO_ROUND)
{
// The merry-go-round in pre-LL versions was always yellow with red
dst->vehicle_colours[0].body_colour = COLOUR_YELLOW;
dst->vehicle_colours[0].trim_colour = COLOUR_BRIGHT_RED;
}
else
{
for (int i = 0; i < 12; i++)
{
dst->vehicle_colours[i].body_colour = RCT1::GetColour(src->vehicle_colours[i].body);
dst->vehicle_colours[i].trim_colour = RCT1::GetColour(src->vehicle_colours[i].trim);
}
}
// Fix other Z
// dst->start_drop_height /= 2;
// dst->highest_drop_height = 1;
// if (dst->cur_test_track_z != 255)
// {
// dst->cur_test_track_z /= 2;
// }
// dst->chairlift_bullwheel_z[0] /= 2;
// dst->chairlift_bullwheel_z[1] /= 2;
// Maintenance
dst->build_date = src->build_date;
dst->inspection_interval = src->inspection_interval;
dst->last_inspection = src->last_inspection;
dst->reliability = src->reliability;
dst->unreliability_factor = src->unreliability_factor;
dst->breakdown_reason = src->breakdown_reason;
// Finance
dst->upkeep_cost = src->upkeep_cost;
dst->price = src->price;
dst->income_per_hour = src->income_per_hour;
dst->value = src->value;
dst->satisfaction = 255;
dst->satisfaction_time_out = 0;
dst->satisfaction_next = 0;
dst->popularity = src->popularity;
dst->popularity_next = src->popularity_next;
dst->popularity_time_out = src->popularity_time_out;
dst->music_tune_id = 255;
dst->measurement_index = 255;
dst->excitement = (ride_rating)-1;
}
void S4Importer::ImportRideMeasurements()
{
for (int i = 0; i < MAX_RIDE_MEASUREMENTS; i++)
{
rct_ride_measurement * dst = get_ride_measurement(i);
rct_ride_measurement * src = &_s4.ride_measurements[i];
ImportRideMeasurement(dst, src);
}
}
void S4Importer::ImportRideMeasurement(rct_ride_measurement * dst, rct_ride_measurement * src)
{
// Not yet supported
// *dst = *src;
// for (int i = 0; i < RIDE_MEASUREMENT_MAX_ITEMS; i++)
// {
// dst->altitude[i] /= 2;
// }
}
void S4Importer::ImportPeepSpawns()
{
for (int i = 0; i < 2; i++)
{
gPeepSpawns[i] = _s4.peep_spawn[i];
}
}
void S4Importer::ImportMapAnimations()
{
// This is sketchy, ideally we should try to re-create them
rct_map_animation * s4Animations = (rct_map_animation*)_s4.map_animations;
for (int i = 0; i < 1000; i++)
{
gAnimatedObjects[i] = s4Animations[i];
gAnimatedObjects[i].baseZ /= 2;
}
gNumMapAnimations = _s4.num_map_animations;
}
void S4Importer::ImportFinance()
{
gParkEntranceFee = _s4.park_entrance_fee;
gLandPrice = _s4.land_price;
gConstructionRightsPrice = _s4.construction_rights_price;
gCashEncrypted = ENCRYPT_MONEY(_s4.cash);
gBankLoan = _s4.loan;
gMaxBankLoan = _s4.max_loan;
gInitialCash = _s4.cash;
finance_update_loan_hash();
gCompanyValue = _s4.company_value;
gParkValue = _s4.park_value;
gCurrentProfit = _s4.profit;
for (int i = 0; i < 128; i++)
{
gCashHistory[i] = _s4.cash_history[i];
gParkValueHistory[i] = _s4.park_value_history[i];
gWeeklyProfitHistory[i] = _s4.weekly_profit_history[i];
}
for (int i = 0; i < 14 * 16; i++)
{
gExpenditureTable[i] = _s4.expenditure[i];
}
gCurrentExpenditure = _s4.total_expenditure;
gTotalAdmissions = _s4.num_admissions;
gTotalIncomeFromAdmissions = _s4.admission_total_income;
// TODO marketing campaigns not working
for (int i = 0; i < 6; i++)
{
gMarketingCampaignDaysLeft[i] = _s4.marketing_status[i];
gMarketingCampaignRideIndex[i] = _s4.marketing_assoc[i];
}
}
void S4Importer::LoadObjects()
{
LoadObjects(OBJECT_TYPE_RIDE, _rideEntries);
LoadObjects(OBJECT_TYPE_SMALL_SCENERY, _smallSceneryEntries);
LoadObjects(OBJECT_TYPE_LARGE_SCENERY, _largeSceneryEntries);
LoadObjects(OBJECT_TYPE_WALLS, _wallEntries);
LoadObjects(OBJECT_TYPE_PATHS, _pathEntries);
LoadObjects(OBJECT_TYPE_PATH_BITS, _pathAdditionEntries);
LoadObjects(OBJECT_TYPE_SCENERY_SETS, _sceneryGroupEntries);
LoadObjects(OBJECT_TYPE_BANNERS, List<const char *>({
"BN1 ",
"BN2 ",
"BN3 ",
"BN4 ",
"BN5 ",
"BN6 ",
"BN7 ",
"BN8 ",
"BN9 "
}));
LoadObjects(OBJECT_TYPE_PARK_ENTRANCE, List<const char *>({ "PKENT1 " }));
LoadObjects(OBJECT_TYPE_WATER, List<const char *>({ "WTRCYAN " }));
reset_loaded_objects();
}
void S4Importer::LoadObjects(uint8 objectType, List<const char *> entries)
{
IObjectManager * objectManager = GetObjectManager();
uint32 entryIndex = 0;
for (const char * objectName : entries)
{
rct_object_entry entry;
entry.flags = 0x00008000 + objectType;
Memory::Copy(entry.name, objectName, 8);
entry.checksum = 0;
Object * object = objectManager->LoadObject(&entry);
if (object == nullptr)
{
log_error("Failed to load %s.", objectName);
throw Exception("Failed to load object.");
}
entryIndex++;
}
}
void S4Importer::ImportMapElements()
{
memcpy(gMapElements, _s4.map_elements, 0xC000 * sizeof(rct_map_element));
ClearExtraTileEntries();
FixColours();
FixZ();
FixPaths();
FixWalls();
FixBanners();
FixTerrain();
FixEntrancePositions();
FixMapElementEntryTypes();
}
void S4Importer::ImportResearch()
{
// All available objects must be loaded before this method is called as it
// requires them to correctly insert objects into the research list
research_reset_items();
size_t researchListCount;
const rct1_research_item * researchList = GetResearchList(&researchListCount);
// Initialise the "seen" tables
Memory::Set(_researchRideEntryUsed, 0, sizeof(_researchRideEntryUsed));
Memory::Set(_researchRideTypeUsed, 0, sizeof(_researchRideTypeUsed));
// The first six scenery groups are always available
for (int i = 0; i < 6; i++)
{
research_insert_scenery_group_entry(i, true);
}
bool researched = true;
for (size_t i = 0; i < researchListCount; i++)
{
const rct1_research_item * researchItem = &researchList[i];
if (researchItem->item == RCT1_RESEARCH_END_AVAILABLE)
{
researched = false;
}
else if (researchItem->item == RCT1_RESEARCH_END_RESEARCHABLE ||
researchItem->item == RCT1_RESEARCH_END)
{
break;
}
switch (researchItem->category) {
case RCT1_RESEARCH_CATEGORY_THEME:
{
uint8 rct1SceneryTheme = researchItem->item;
if (rct1SceneryTheme != RCT1_SCENERY_THEME_GENERAL &&
rct1SceneryTheme != RCT1_SCENERY_THEME_JUMPING_FOUNTAINS &&
rct1SceneryTheme != RCT1_SCENERY_THEME_GARDEN_CLOCK)
{
uint8 sceneryGroupEntryIndex = _sceneryThemeTypeToEntryMap[rct1SceneryTheme];
research_insert_scenery_group_entry(sceneryGroupEntryIndex, researched);
}
break;
}
case RCT1_RESEARCH_CATEGORY_RIDE:
{
uint8 rct1RideType = researchItem->item;
// Add all vehicles for this ride type that are researched or before this research item
uint32 numVehicles = 0;
for (size_t j = 0; j < researchListCount; j++)
{
const rct1_research_item *researchItem2 = &researchList[j];
if (researchItem2->item == RCT1_RESEARCH_END_RESEARCHABLE ||
researchItem2->item == RCT1_RESEARCH_END_AVAILABLE)
{
break;
}
if (researchItem2->category == RCT1_RESEARCH_CATEGORY_VEHICLE &&
researchItem2->related_ride == rct1RideType)
{
// Only add the vehicles that were listed before this ride, otherwise we might
// change the research order
if (j < i)
{
InsertResearchVehicle(researchItem2, researched);
}
numVehicles++;
}
}
if (numVehicles == 0)
{
// No vehicles found so just add the default for this ride
uint8 rideEntryIndex = _rideTypeToRideEntryMap[rct1RideType];
Guard::Assert(rideEntryIndex != 255, "rideEntryIndex was 255");
if (!_researchRideEntryUsed[rideEntryIndex])
{
_researchRideEntryUsed[rideEntryIndex] = true;
research_insert_ride_entry(rideEntryIndex, researched);
}
}
break;
}
case RCT1_RESEARCH_CATEGORY_VEHICLE:
// Only add vehicle if the related ride has been seen, this to make sure that vehicles
// are researched only after the ride has been researched
if (_researchRideTypeUsed[researchItem->related_ride])
{
InsertResearchVehicle(researchItem, researched);
}
break;
case RCT1_RESEARCH_CATEGORY_SPECIAL:
// Not supported
break;
}
}
research_remove_non_separate_vehicle_types();
// Fixes availability of rides
sub_684AC3();
// Research funding / priority
uint8 activeResearchTypes = 0;
if (_s4.research_priority & RCT1_RESEARCH_EXPENDITURE_ROLLERCOASTERS)
{
activeResearchTypes |= (1 << RESEARCH_CATEGORY_ROLLERCOASTER);
}
if (_s4.research_priority & RCT1_RESEARCH_EXPENDITURE_THRILL_RIDES)
{
activeResearchTypes |= (1 << RESEARCH_CATEGORY_THRILL);
activeResearchTypes |= (1 << RESEARCH_CATEGORY_WATER);
}
if (_s4.research_priority & RCT1_RESEARCH_EXPENDITURE_GENTLE_TRANSPORT_RIDES)
{
activeResearchTypes |= (1 << RESEARCH_CATEGORY_GENTLE);
activeResearchTypes |= (1 << RESEARCH_CATEGORY_TRANSPORT);
}
if (_s4.research_priority & RCT1_RESEARCH_EXPENDITURE_SHOPS)
{
activeResearchTypes |= (1 << RESEARCH_CATEGORY_SHOP);
}
if (_s4.research_priority & RCT1_RESEARCH_EXPENDITURE_SCENERY_THEMEING)
{
activeResearchTypes |= (1 << RESEARCH_CATEGORY_SCENERYSET);
}
gResearchPriorities = activeResearchTypes;
gResearchFundingLevel = _s4.research_level;
// Research history
gResearchProgress = _s4.research_progress;
// gResearchProgressStage =
gResearchNextItem = _s4.next_research_item;
gResearchNextCategory = _s4.next_research_category;
// gResearchExpectedDay =
// gResearchExpectedMonth =
}
void S4Importer::InsertResearchVehicle(const rct1_research_item * researchItem, bool researched)
{
uint8 vehicle = researchItem->item;
uint8 rideEntryIndex = _vehicleTypeToRideEntryMap[vehicle];
if (!_researchRideEntryUsed[rideEntryIndex])
{
_researchRideEntryUsed[rideEntryIndex] = true;
research_insert_ride_entry(rideEntryIndex, researched);
}
}
void S4Importer::ImportParkName()
{
const char * parkName = _s4.scenario_name;
if (is_user_string_id((rct_string_id)_s4.park_name_string_index))
{
const char * userString = GetUserString(_s4.park_name_string_index);
if (userString[0] != '\0')
{
parkName = userString;
}
}
rct_string_id stringId = user_string_allocate(4, parkName);
if (stringId != 0)
{
gParkName = stringId;
gParkNameArgs = 0;
}
}
void S4Importer::ImportParkFlags()
{
// Date and srand
gCurrentTicks = _s4.ticks;
gScenarioSrand0 = _s4.random_a;
gScenarioSrand1 = _s4.random_b;
gDateMonthsElapsed = _s4.month;
gDateMonthTicks = _s4.day;
// Park rating
gParkRating = _s4.park_rating;
for (int i = 0; i < 32; i++)
{
gParkRatingHistory[i] = _s4.park_rating_history[i];
}
// Awards
for (int i = 0; i < 4; i++)
{
gCurrentAwards[i] = _s4.awards[i];
}
// Number of guests history
for (int i = 0; i < 32; i++)
{
gGuestsInParkHistory[i] = _s4.guests_in_park_history[i];
}
// News items
rct_news_item *newsItems = RCT2_ADDRESS(RCT2_ADDRESS_NEWS_ITEM_LIST, rct_news_item);
for (int i = 0; i < 61; i++)
{
newsItems[i] = _s4.messages[i];
}
// Initial guest status
gGuestInitialCash = _s4.guest_initial_cash;
gGuestInitialHunger = _s4.guest_initial_hunger;
gGuestInitialThirst = _s4.guest_initial_thirst;
// Staff colours
gStaffHandymanColour = RCT1::GetColour(_s4.handman_colour);
gStaffMechanicColour = RCT1::GetColour(_s4.mechanic_colour);
gStaffSecurityColour = RCT1::GetColour(_s4.security_guard_colour);
// Flags
gParkFlags = _s4.park_flags;
gParkFlags &= ~PARK_FLAGS_ANTI_CHEAT_DEPRECATED;
if (!(_s4.park_flags & RCT1_PARK_FLAGS_PARK_ENTRY_LOCKED_AT_FREE))
{
gCheatsUnlockAllPrices = true;
}
// RCT2 uses two flags for no money (for cheat detection). RCT1 used only one.
// Copy its value to make no money scenarios such as Arid Heights work properly.
if (_s4.park_flags & RCT1_PARK_FLAGS_NO_MONEY)
{
gParkFlags |= PARK_FLAGS_NO_MONEY_SCENARIO;
}
}
void S4Importer::ImportClimate()
{
gClimate = _s4.climate;
gClimateUpdateTimer = _s4.climate_timer;
gClimateCurrentTemperature = _s4.temperature;
gClimateCurrentWeather = _s4.weather;
gClimateCurrentWeatherEffect = 0;
gClimateCurrentWeatherGloom = _s4.weather_gloom;
gClimateCurrentRainLevel = _s4.rain;
gClimateNextTemperature = _s4.target_temperature;
gClimateNextWeather = _s4.target_weather;
gClimateNextWeatherEffect = 0;
gClimateNextWeatherGloom = _s4.target_weather_gloom;
gClimateNextRainLevel = _s4.target_rain;
}
void S4Importer::ImportScenarioNameDetails()
{
rct_s6_info * s6Info = gS6Info;
String::Set(s6Info->name, sizeof(s6Info->name), _s4.scenario_name);
String::Set(s6Info->details, sizeof(s6Info->details), "");
int scNumber = GetSCNumber();
if (scNumber != -1)
{
source_desc sourceDesc;
if (scenario_get_source_desc_by_id(scNumber, &sourceDesc))
{
rct_string_id localisedStringIds[3];
if (language_get_localised_scenario_strings(sourceDesc.title, localisedStringIds))
{
if (localisedStringIds[0] != STR_NONE)
{
String::Set(s6Info->name, sizeof(s6Info->name), language_get_string(localisedStringIds[0]));
}
if (localisedStringIds[2] != STR_NONE)
{
String::Set(s6Info->details, sizeof(s6Info->details), language_get_string(localisedStringIds[2]));
}
}
}
}
}
void S4Importer::ImportScenarioObjective()
{
gScenarioObjectiveType = _s4.scenario_objective_type;
gScenarioObjectiveYear = _s4.scenario_objective_years;
gScenarioObjectiveCurrency = _s4.scenario_objective_currency;
gScenarioObjectiveNumGuests = _s4.scenario_objective_num_guests;
}
void S4Importer::ImportSavedView()
{
gSavedViewX = _s4.view_x;
gSavedViewY = _s4.view_y;
gSavedViewZoom = _s4.view_zoom;
gSavedViewRotation = _s4.view_rotation;
}
void S4Importer::ClearExtraTileEntries()
{
// Reset the map tile pointers
for (int i = 0; i < 0x10000; i++)
{
gMapElementTilePointers[i] = (rct_map_element *)-1;
}
// Get the first free map element
rct_map_element * nextFreeMapElement = gMapElements;
for (int i = 0; i < 128 * 128; i++)
{
do { } while (!map_element_is_last_for_tile(nextFreeMapElement++));
}
rct_map_element * mapElement = gMapElements;
rct_map_element * * tilePointer = gMapElementTilePointers;
// 128 rows of map data from RCT1 map
for (int x = 0; x < 128; x++)
{
// Assign the first half of this row
for (int y = 0; y < 128; y++)
{
*tilePointer++ = mapElement;
do { } while (!map_element_is_last_for_tile(mapElement++));
}
// Fill the rest of the row with blank tiles
for (int y = 0; y < 128; y++)
{
nextFreeMapElement->type = MAP_ELEMENT_TYPE_SURFACE;
nextFreeMapElement->flags = MAP_ELEMENT_FLAG_LAST_TILE;
nextFreeMapElement->base_height = 2;
nextFreeMapElement->clearance_height = 0;
nextFreeMapElement->properties.surface.slope = 0;
nextFreeMapElement->properties.surface.terrain = 0;
nextFreeMapElement->properties.surface.grass_length = GRASS_LENGTH_CLEAR_0;
nextFreeMapElement->properties.surface.ownership = 0;
*tilePointer++ = nextFreeMapElement++;
}
}
// 128 extra rows left to fill with blank tiles
for (int y = 0; y < 128 * 256; y++)
{
nextFreeMapElement->type = MAP_ELEMENT_TYPE_SURFACE;
nextFreeMapElement->flags = MAP_ELEMENT_FLAG_LAST_TILE;
nextFreeMapElement->base_height = 2;
nextFreeMapElement->clearance_height = 0;
nextFreeMapElement->properties.surface.slope = 0;
nextFreeMapElement->properties.surface.terrain = 0;
nextFreeMapElement->properties.surface.grass_length = GRASS_LENGTH_CLEAR_0;
nextFreeMapElement->properties.surface.ownership = 0;
*tilePointer++ = nextFreeMapElement++;
}
gNextFreeMapElement = nextFreeMapElement;
}
void S4Importer::FixColours()
{
colour_t colour;
// The following code would be worth doing if we were able to import sprites
// for (int i = 0; i < MAX_SPRITES; i++)
// {
// rct_unk_sprite * sprite = &(g_sprite_list[i].unknown);
// switch (sprite->sprite_identifier) {
// case SPRITE_IDENTIFIER_PEEP:
// {
// rct_peep * peep = (rct_peep*)sprite;
// peep->tshirt_colour = RCT1ColourConversionTable[peep->tshirt_colour];
// peep->trousers_colour = RCT1ColourConversionTable[peep->trousers_colour];
// peep->balloon_colour = RCT1ColourConversionTable[peep->balloon_colour];
// peep->umbrella_colour = RCT1ColourConversionTable[peep->umbrella_colour];
// peep->hat_colour = RCT1ColourConversionTable[peep->hat_colour];
// break;
// }
// case SPRITE_IDENTIFIER_MISC:
// {
// rct_balloon * balloon = (rct_balloon*)sprite;
// balloon->colour = RCT1ColourConversionTable[balloon->colour];
// balloon->var_2D = RCT1ColourConversionTable[balloon->var_2D];
// break;
// }
// }
// }
rct_map_element * mapElement = gMapElements;
while (mapElement < gNextFreeMapElement)
{
if (mapElement->base_height != 255)
{
switch (map_element_get_type(mapElement)) {
case MAP_ELEMENT_TYPE_SCENERY:
colour = RCT1::GetColour(mapElement->properties.scenery.colour_1 & 0x1F);
mapElement->properties.scenery.colour_1 &= 0xE0;
mapElement->properties.scenery.colour_1 |= colour;
// Copied from [rct2: 0x006A2956]
switch (mapElement->properties.scenery.type) {
case 157: // TGE1 (Geometric Sculpture)
case 162: // TGE2 (Geometric Sculpture)
case 168: // TGE3 (Geometric Sculpture)
case 170: // TGE4 (Geometric Sculpture)
case 171: // TGE5 (Geometric Sculpture)
mapElement->properties.scenery.colour_2 = COLOUR_WHITE;
break;
}
break;
case MAP_ELEMENT_TYPE_FENCE:
colour = ((mapElement->type & 0xC0) >> 3) |
((mapElement->properties.fence.type & 0xE0) >> 5);
colour = RCT1::GetColour(colour);
mapElement->type &= 0x3F;
mapElement->properties.fence.type &= 0x1F;
mapElement->type |= (colour & 0x18) << 3;
mapElement->properties.fence.type |= (colour & 7) << 5;
break;
case MAP_ELEMENT_TYPE_SCENERY_MULTIPLE:
colour = RCT1::GetColour(mapElement->properties.scenerymultiple.colour[0] & 0x1F);
mapElement->properties.scenerymultiple.colour[0] &= 0xE0;
mapElement->properties.scenerymultiple.colour[0] |= colour;
colour = RCT1::GetColour(mapElement->properties.scenerymultiple.colour[1] & 0x1F);
mapElement->properties.scenerymultiple.colour[1] &= 0xE0;
mapElement->properties.scenerymultiple.colour[1] |= colour;
break;
}
}
mapElement++;
}
}
void S4Importer::FixZ()
{
// The following code would be useful if we imported sprites
// for (int i = 0; i < MAX_SPRITES; i++)
// {
// rct_unk_sprite * sprite = &(g_sprite_list[i].unknown);
// if (sprite->sprite_identifier == SPRITE_IDENTIFIER_PEEP) {
// rct_peep * peep = (rct_peep*)sprite;
// peep->next_z /= 2;
// RCT2_GLOBAL((int)peep + 0xCE, uint8) /= 2;
// }
// }
rct_map_element * mapElement = gMapElements;
while (mapElement < gNextFreeMapElement)
{
if (mapElement->base_height != 255)
{
mapElement->base_height /= 2;
mapElement->clearance_height /= 2;
}
mapElement++;
}
RCT2_GLOBAL(0x01359208, uint16) = 7;
}
void S4Importer::FixPaths()
{
rct_map_element * mapElement = gMapElements;
while (mapElement < gNextFreeMapElement)
{
switch (map_element_get_type(mapElement)) {
case MAP_ELEMENT_TYPE_PATH:
{
// Type
uint8 pathColour = mapElement->type & 3;
uint8 pathType = (mapElement->properties.path.type & 0xF0) >> 4;
pathType = (pathType << 2) | pathColour;
uint8 entryIndex = _pathTypeToEntryMap[pathType];
mapElement->type &= 0xFC;
mapElement->flags &= ~0x60;
mapElement->flags &= ~MAP_ELEMENT_FLAG_BROKEN;
mapElement->properties.path.type &= 0x0F;
footpath_scenery_set_is_ghost(mapElement, false);
if (RCT1::PathIsQueue(pathType))
{
mapElement->type |= 1;
}
mapElement->properties.path.type |= entryIndex << 4;
// Additions
uint8 additionType = footpath_element_get_path_scenery(mapElement);
if (additionType != RCT1_PATH_ADDITION_NONE)
{
uint8 normalisedType = RCT1::NormalisePathAddition(additionType);
uint8 entryIndex = _pathAdditionTypeToEntryMap[normalisedType];
if (additionType != normalisedType)
{
mapElement->flags |= MAP_ELEMENT_FLAG_BROKEN;
}
footpath_element_set_path_scenery(mapElement, entryIndex + 1);
}
break;
}
case MAP_ELEMENT_TYPE_ENTRANCE:
if (mapElement->properties.entrance.type == ENTRANCE_TYPE_PARK_ENTRANCE)
{
uint8 pathType = mapElement->properties.entrance.path_type;
if (pathType == 0)
{
pathType = RCT1_FOOTPATH_TYPE_TARMAC_GRAY;
}
uint8 entryIndex = _pathTypeToEntryMap[pathType];
mapElement->properties.entrance.path_type = entryIndex & 0x7F;
}
break;
}
mapElement++;
}
}
void S4Importer::FixWalls()
{
for (int x = 0; x < 128; x++)
{
for (int y = 0; y < 128; y++)
{
rct_map_element * mapElement = map_get_first_element_at(x, y);
do
{
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_FENCE)
{
rct_map_element originalMapElement = *mapElement;
map_element_remove(mapElement);
uint8 var_05 = originalMapElement.properties.fence.item[0];
uint16 var_06 = originalMapElement.properties.fence.item[1] |
(originalMapElement.properties.fence.item[2] << 8);
for (int edge = 0; edge < 4; edge++)
{
int typeA = (var_05 >> (edge * 2)) & 3;
int typeB = (var_06 >> (edge * 4)) & 0x0F;
if (typeB != 0x0F)
{
int type = typeA | (typeB << 2);
int colourA = ((originalMapElement.type & 0xC0) >> 3) |
(originalMapElement.properties.fence.type >> 5);
int colourB = 0;
int colourC = 0;
ConvertWall(&type, &colourA, &colourB, &colourC);
type = _wallTypeToEntryMap[type];
map_place_fence(type, x * 32, y * 32, 0, edge, colourA, colourB, colourC, 169);
}
}
break;
}
}
while (!map_element_is_last_for_tile(mapElement++));
}
}
}
void S4Importer::ConvertWall(int * type, int * colourA, int * colourB, int * colourC)
{
switch (*type) {
case 12: // creepy gate
*colourA = 24;
break;
case 26: // white wooden fence
*type = 12;
*colourA = 2;
break;
case 27: // red wooden fence
*type = 12;
*colourA = 25;
break;
case 50: // plate glass
*colourA = 24;
break;
case 13:
*colourB = *colourA;
*colourA = 24;
break;
case 11: // tall castle wall with grey gate
case 22: // brick wall with gate
*colourB = 2;
break;
case 35: // wood post fence
case 42: // tall grey castle wall
case 43: // wooden fence with snow
case 44:
case 45:
case 46:
*colourA = 1;
break;
}
}
void S4Importer::FixBanners()
{
for (int x = 0; x < 128; x++)
{
for (int y = 0; y < 128; y++)
{
rct_map_element * mapElement = map_get_first_element_at(x, y);
do
{
if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_BANNER)
{
uint8 index = mapElement->properties.banner.index;
rct_banner * src = &_s4.banners[index];
rct_banner * dst = &gBanners[index];
ImportBanner(dst, src);
}
}
while (!map_element_is_last_for_tile(mapElement++));
}
}
}
void S4Importer::ImportBanner(rct_banner * dst, rct_banner * src)
{
*dst = *src;
dst->colour = RCT1::GetColour(src->colour);
dst->string_idx = 778;
if (is_user_string_id(src->string_idx))
{
const char * bannerText = GetUserString(src->string_idx);
if (!String::IsNullOrEmpty(bannerText))
{
rct_string_id bannerTextStringId = user_string_allocate(128, bannerText);
if (bannerTextStringId != 0)
{
dst->string_idx = bannerTextStringId;
}
}
}
}
void S4Importer::FixTerrain()
{
map_element_iterator it;
map_element_iterator_begin(&it);
while (map_element_iterator_next(&it))
{
rct_map_element * element = it.element;
if (map_element_get_type(element) == MAP_ELEMENT_TYPE_SURFACE)
{
map_element_set_terrain(element, RCT1::GetTerrain(map_element_get_terrain(element)));
map_element_set_terrain_edge(element, RCT1::GetTerrainEdge(map_element_get_terrain_edge(element)));
}
}
}
void S4Importer::FixEntrancePositions()
{
for (int i = 0; i < 4; i++)
{
gParkEntranceX[i] = (sint16)0x8000;
}
uint8 entranceIndex = 0;
map_element_iterator it;
map_element_iterator_begin(&it);
while (map_element_iterator_next(&it) && entranceIndex < 4)
{
rct_map_element * element = it.element;
if (map_element_get_type(element) != MAP_ELEMENT_TYPE_ENTRANCE) continue;
if (element->properties.entrance.type != ENTRANCE_TYPE_PARK_ENTRANCE) continue;
if ((element->properties.entrance.index & 0x0F) != 0) continue;
gParkEntranceX[entranceIndex] = it.x * 32;
gParkEntranceY[entranceIndex] = it.y * 32;
gParkEntranceZ[entranceIndex] = element->base_height * 8;
gParkEntranceDirection[entranceIndex] = element->type & 3;
entranceIndex++;
}
}
void S4Importer::FixMapElementEntryTypes()
{
map_element_iterator it;
map_element_iterator_begin(&it);
while (map_element_iterator_next(&it))
{
rct_map_element * mapElement = it.element;
switch (map_element_get_type(mapElement)) {
case MAP_ELEMENT_TYPE_SCENERY:
mapElement->properties.scenery.type = _smallSceneryTypeToEntryMap[mapElement->properties.scenery.type];
break;
case MAP_ELEMENT_TYPE_SCENERY_MULTIPLE:
{
uint8 type = mapElement->properties.scenerymultiple.type & MAP_ELEMENT_LARGE_TYPE_MASK;
mapElement->properties.scenerymultiple.type &= ~MAP_ELEMENT_LARGE_TYPE_MASK;
mapElement->properties.scenerymultiple.type |= _largeSceneryTypeToEntryMap[type];
break;
}
}
}
}
List<const char *> * S4Importer::GetEntryList(uint8 objectType)
{
switch (objectType) {
case OBJECT_TYPE_RIDE: return &_rideEntries;
case OBJECT_TYPE_SMALL_SCENERY: return &_smallSceneryEntries;
case OBJECT_TYPE_LARGE_SCENERY: return &_largeSceneryEntries;
case OBJECT_TYPE_WALLS: return &_wallEntries;
case OBJECT_TYPE_PATHS: return &_pathEntries;
case OBJECT_TYPE_PATH_BITS: return &_pathAdditionEntries;
case OBJECT_TYPE_SCENERY_SETS: return &_sceneryGroupEntries;
}
return nullptr;
}
const rct1_research_item * S4Importer::GetResearchList(size_t * count)
{
// Loopy Landscapes stores research items in a different place
if (_gameVersion == FILE_VERSION_RCT1_LL)
{
*count = Util::CountOf(_s4.research_items_LL);
return _s4.research_items_LL;
}
else
{
*count = Util::CountOf(_s4.research_items);
return _s4.research_items;
}
}
int S4Importer::GetSCNumber()
{
const utf8 * fileName = Path::GetFileName(_s4Path);
if (tolower(fileName[0]) == 's' && tolower(fileName[1]) == 'c') {
constexpr size_t maxDigits = 7;
utf8 digitBuffer[maxDigits + 1];
utf8 * dst = digitBuffer;
const utf8 * src = fileName + 2;
for (size_t i = 0; i < maxDigits && *src != '.'; i++)
{
*dst++ = *src++;
}
*dst++ = 0;
if (digitBuffer[0] == '0' && digitBuffer[1] == '\0')
{
return 0;
}
else
{
int digits = atoi(digitBuffer);
return digits == 0 ? -1 : digits;
}
}
else
{
return -1;
}
}
const char * S4Importer::GetUserString(rct_string_id stringId)
{
return _s4.string_table[(stringId - 0x8000) % 1024];
}
/////////////////////////////////////////
// C -> C++ transfer
/////////////////////////////////////////
extern "C"
{
bool rct1_load_saved_game(const utf8 * path)
{
bool result;
auto s4Importer = new S4Importer();
try
{
s4Importer->LoadSavedGame(path);
s4Importer->Import();
result = true;
} catch (Exception ex)
{
result = false;
}
delete s4Importer;
return result;
}
bool rct1_load_scenario(const utf8 * path)
{
bool result;
auto s4Importer = new S4Importer();
try
{
s4Importer->LoadScenario(path);
s4Importer->Import();
result = true;
} catch (Exception ex)
{
result = false;
}
delete s4Importer;
return result;
}
colour_t rct1_get_colour(colour_t colour)
{
return RCT1::GetColour(colour);
}
/**
* This function keeps a list of the preferred vehicle for every generic track
* type, out of the available vehicle types in the current game. It determines
* which picture is shown on the new ride tab and which train type is selected
* by default.
*/
int vehicle_preference_compare(uint8 rideType, const char * a, const char * b)
{
List<const char *> rideEntryOrder = RCT1::GetPreferedRideEntryOrder(rideType);
for (const char * object : rideEntryOrder)
{
if (String::Equals(object, a, true))
{
return -1;
}
if (String::Equals(object, b, true))
{
return 1;
}
}
return 0;
}
}