diff --git a/openrct2.vcxproj.user b/openrct2.vcxproj.user
index fd6680fbf4..6df380b165 100644
--- a/openrct2.vcxproj.user
+++ b/openrct2.vcxproj.user
@@ -14,8 +14,7 @@
$(TargetDir)
WindowsLocalDebugger
$(TargetDir)\openrct2.exe
-
-
+ C:\Users\Ted\Desktop\rct1ll\Scenarios\sc1.sc4
$(TargetDir)
diff --git a/src/rct1.c b/src/rct1.c
index cb2db19731..db837e5aaa 100644
--- a/src/rct1.c
+++ b/src/rct1.c
@@ -1828,3 +1828,322 @@ bool rideTypeShouldLoseSeparateFlag(rct_ride_entry *ride)
}
#pragma endregion
+
+#pragma region RCT1 Scenario / Saved Game Import
+
+#include "audio/audio.h"
+#include "localisation/date.h"
+#include "peep/staff.h"
+
+static void rct1_import_s4_properly(rct1_s4 *s4);
+
+static const uint8 RCT1RideTypeConversionTable[] = {
+ RIDE_TYPE_WOODEN_ROLLER_COASTER,
+ RIDE_TYPE_STAND_UP_ROLLER_COASTER,
+ RIDE_TYPE_SUSPENDED_SWINGING_COASTER,
+ RIDE_TYPE_INVERTED_ROLLER_COASTER,
+ RIDE_TYPE_JUNIOR_ROLLER_COASTER,
+ RIDE_TYPE_MINIATURE_RAILWAY,
+ RIDE_TYPE_MONORAIL,
+ RIDE_TYPE_MINI_SUSPENDED_COASTER,
+ RIDE_TYPE_BOAT_RIDE,
+ RIDE_TYPE_WOODEN_WILD_MOUSE,
+ RIDE_TYPE_STEEPLECHASE,
+ RIDE_TYPE_CAR_RIDE,
+ RIDE_TYPE_LAUNCHED_FREEFALL,
+ RIDE_TYPE_BOBSLEIGH_COASTER,
+ RIDE_TYPE_OBSERVATION_TOWER,
+ RIDE_TYPE_LOOPING_ROLLER_COASTER,
+ RIDE_TYPE_DINGHY_SLIDE,
+ RIDE_TYPE_MINE_TRAIN_COASTER,
+ RIDE_TYPE_CHAIRLIFT,
+ RIDE_TYPE_CORKSCREW_ROLLER_COASTER,
+ RIDE_TYPE_MAZE,
+ RIDE_TYPE_SPIRAL_ROLLER_COASTER,
+ RIDE_TYPE_GO_KARTS,
+ RIDE_TYPE_LOG_FLUME,
+ RIDE_TYPE_RIVER_RAPIDS,
+ RIDE_TYPE_DODGEMS,
+ RIDE_TYPE_PIRATE_SHIP,
+ RIDE_TYPE_SWINGING_INVERTER_SHIP,
+ RIDE_TYPE_FOOD_STALL,
+ RIDE_TYPE_FOOD_STALL,
+ RIDE_TYPE_DRINK_STALL,
+ RIDE_TYPE_FOOD_STALL,
+ RIDE_TYPE_SHOP,
+ RIDE_TYPE_MERRY_GO_ROUND,
+ RIDE_TYPE_SHOP,
+ RIDE_TYPE_INFORMATION_KIOSK,
+ RIDE_TYPE_TOILETS,
+ RIDE_TYPE_FERRIS_WHEEL,
+ RIDE_TYPE_MOTION_SIMULATOR,
+ RIDE_TYPE_3D_CINEMA,
+ RIDE_TYPE_TOP_SPIN,
+ RIDE_TYPE_SPACE_RINGS,
+ RIDE_TYPE_REVERSE_FREEFALL_COASTER,
+ RIDE_TYPE_SHOP,
+ RIDE_TYPE_VERTICAL_DROP_ROLLER_COASTER,
+ RIDE_TYPE_FOOD_STALL,
+ RIDE_TYPE_TWIST,
+ RIDE_TYPE_HAUNTED_HOUSE,
+ RIDE_TYPE_FOOD_STALL,
+ RIDE_TYPE_CIRCUS_SHOW,
+ RIDE_TYPE_GHOST_TRAIN,
+ RIDE_TYPE_TWISTER_ROLLER_COASTER,
+ RIDE_TYPE_WOODEN_ROLLER_COASTER,
+ RIDE_TYPE_SIDE_FRICTION_ROLLER_COASTER,
+ RIDE_TYPE_WILD_MOUSE,
+ RIDE_TYPE_FOOD_STALL,
+ RIDE_TYPE_FOOD_STALL,
+ RIDE_TYPE_SHOP,
+ RIDE_TYPE_FOOD_STALL,
+ RIDE_TYPE_VIRGINIA_REEL,
+ RIDE_TYPE_SPLASH_BOATS,
+ RIDE_TYPE_MINI_HELICOPTERS,
+ RIDE_TYPE_LAY_DOWN_ROLLER_COASTER,
+ RIDE_TYPE_SUSPENDED_MONORAIL,
+ RIDE_TYPE_NULL,
+ RIDE_TYPE_REVERSER_ROLLER_COASTER,
+ RIDE_TYPE_HEARTLINE_TWISTER_COASTER,
+ RIDE_TYPE_MINI_GOLF,
+ RIDE_TYPE_NULL,
+ RIDE_TYPE_ROTO_DROP,
+ RIDE_TYPE_FLYING_SAUCERS,
+ RIDE_TYPE_CROOKED_HOUSE,
+ RIDE_TYPE_MONORAIL_CYCLES,
+ RIDE_TYPE_COMPACT_INVERTED_COASTER,
+ RIDE_TYPE_WATER_COASTER,
+ RIDE_TYPE_AIR_POWERED_VERTICAL_COASTER,
+ RIDE_TYPE_INVERTED_HAIRPIN_COASTER,
+ RIDE_TYPE_BOAT_RIDE,
+ RIDE_TYPE_SHOP,
+ RIDE_TYPE_RIVER_RAFTS,
+ RIDE_TYPE_FOOD_STALL,
+ RIDE_TYPE_ENTERPRISE,
+ RIDE_TYPE_DRINK_STALL,
+ RIDE_TYPE_FOOD_STALL,
+ RIDE_TYPE_DRINK_STALL
+};
+
+// rct2: 0x0097F0BC
+static const uint8 RCT1ColourConversionTable[] = {
+ 0, 1, 2, 4, 5, 6, 7, 9,
+ 11, 12, 13, 14, 15, 16, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 30, 31, 29, 3, 10, 17, 8
+};
+
+bool rct1_load_saved_game(const char *path)
+{
+ rct1_s4 *s4;
+
+ s4 = malloc(sizeof(rct1_s4));
+ if (!rct1_read_sv4(path, s4)) {
+ free(s4);
+ return false;
+ }
+ rct1_import_s4_properly(s4);
+ free(s4);
+ return true;
+}
+
+bool rct1_load_scenario(const char *path)
+{
+ rct1_s4 *s4;
+
+ s4 = malloc(sizeof(rct1_s4));
+ if (!rct1_read_sc4(path, s4)) {
+ free(s4);
+ return false;
+ }
+ rct1_import_s4_properly(s4);
+ free(s4);
+
+ scenario_begin();
+ return true;
+}
+
+static void rct1_import_map_elements(rct1_s4 *s4)
+{
+ memcpy(gMapElements, s4->map_elements, 0xC000 * sizeof(rct_map_element));
+ rct1_clear_extra_tile_entries();
+ // sub_69F143();
+ rct1_fix_z();
+ sub_69F3AB();
+ sub_6A2730();
+ rct1_fix_scenery();
+ rct1_fix_terrain();
+ rct1_fix_entrance_positions();
+
+ rct_map_element *mapElement;
+ for (int y = 0; y < 256; y++) {
+ for (int x = 0; x < 256; x++) {
+ restartTile:
+ mapElement = map_get_first_element_at(x, y);
+ do {
+ switch (map_element_get_type(mapElement)) {
+ case MAP_ELEMENT_TYPE_TRACK:
+ x = x;
+ // map_element_remove(mapElement);
+ // goto restartTile;
+ break;
+ case MAP_ELEMENT_TYPE_ENTRANCE:
+ // if (mapElement->properties.entrance.type != ENTRANCE_TYPE_PARK_ENTRANCE) {
+ // map_element_remove(mapElement);
+ // goto restartTile;
+ // }
+ break;
+ }
+ } while (!map_element_is_last_for_tile(mapElement++));
+ }
+ }
+}
+
+static void rct1_import_ride(rct1_s4 *s4, rct_ride *dst, rct1_ride *src)
+{
+ memset(dst, 0, sizeof(rct_ride));
+
+ dst->type = RCT1RideTypeConversionTable[src->type];
+ dst->subtype = src->type;
+
+ // Ride name
+ dst->name = 0;
+ if (is_user_string_id(src->name)) {
+ const char *rideName = s4->string_table[(src->name - 0x8000) % 1024];
+ 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;
+ }
+
+ //
+ dst->status = RIDE_STATUS_CLOSED;
+
+ // 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];
+ dst->station_length[i] = src->station_length[i];
+ dst->station_depart[i] = src->station_depart[i];
+ dst->entrances[i] = src->entrance[i];
+ dst->exits[i] = src->exit[i];
+ }
+
+ // Operation
+ dst->mode = src->operating_mode;
+ 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->music = src->music;
+
+ // Colours
+ dst->colour_scheme_type = src->colour_scheme;
+ if (s4->game_version == 108166) {
+ dst->track_colour_main[0] = RCT1ColourConversionTable[src->track_primary_colour];
+ dst->track_colour_additional[0] = RCT1ColourConversionTable[src->track_secondary_colour];
+ dst->track_colour_supports[0] = RCT1ColourConversionTable[src->track_support_colour];
+ } else {
+ for (int i = 0; i < 4; i++) {
+ dst->track_colour_main[i] = RCT1ColourConversionTable[src->track_colour_main[i]];
+ dst->track_colour_additional[i] = RCT1ColourConversionTable[src->track_colour_additional[i]];
+ dst->track_colour_supports[i] = RCT1ColourConversionTable[src->track_colour_supports[i]];
+ }
+ }
+
+ // Maintenance
+ 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->price = src->price;
+}
+
+static void rct1_import_s4_properly(rct1_s4 *s4)
+{
+ int mapSize = s4->map_size == 0 ? 128 : s4->map_size;
+
+ pause_sounds();
+ 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();
+ RCT2_GLOBAL(0x0141F570, uint8) = 0;
+ RCT2_GLOBAL(RCT2_ADDRESS_PARK_FLAGS, uint32) |= PARK_FLAGS_SHOW_REAL_GUEST_NAMES;
+ window_new_ride_init_vars();
+ RCT2_GLOBAL(0x0141F571, uint8) = 4;
+ news_item_init_queue();
+
+ rct_string_id stringId;
+
+ rct1_load_default_objects();
+ reset_loaded_objects();
+
+ for (int i = 0; i < MAX_RIDES; i++) {
+ if (s4->rides[i].type != RIDE_TYPE_NULL) {
+ rct1_import_ride(s4, GET_RIDE(i), &s4->rides[i]);
+ }
+ }
+
+ // Map elements
+ rct1_import_map_elements(s4);
+
+ // Finance
+ RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONEY_ENCRYPTED, uint32) = ENCRYPT_MONEY(s4->cash);
+ RCT2_GLOBAL(RCT2_ADDRESS_PARK_ENTRANCE_FEE, money16) = s4->park_entrance_fee;
+
+ // Park name
+ const char *parkName = s4->scenario_name;
+ if (is_user_string_id((rct_string_id)s4->park_name_string_index)) {
+ const char *userString = s4->string_table[(s4->park_name_string_index - 0x8000) % 1024];
+ if (userString[0] != 0) {
+ parkName = userString;
+ }
+ }
+ stringId = user_string_allocate(4, parkName);
+ if (stringId != 0) {
+ RCT2_GLOBAL(RCT2_ADDRESS_PARK_NAME, rct_string_id) = stringId;
+ RCT2_GLOBAL(RCT2_ADDRESS_PARK_NAME_ARGS, uint32) = 0;
+ }
+
+ // Scenario name
+ strcpy((char*)RCT2_ADDRESS_SCENARIO_NAME, s4->scenario_name);
+
+ // Scenario objective
+ RCT2_GLOBAL(RCT2_ADDRESS_OBJECTIVE_TYPE, uint8) = s4->scenario_objective_type;
+ RCT2_GLOBAL(RCT2_ADDRESS_OBJECTIVE_YEAR, uint8) = s4->scenario_objective_years;
+ RCT2_GLOBAL(RCT2_ADDRESS_OBJECTIVE_CURRENCY, uint32) = s4->scenario_objective_currency;
+ RCT2_GLOBAL(RCT2_ADDRESS_OBJECTIVE_NUM_GUESTS, uint16) = s4->scenario_objective_num_guests;
+
+ // Restore view
+ RCT2_GLOBAL(RCT2_ADDRESS_SAVED_VIEW_X, uint16) = s4->view_x;
+ RCT2_GLOBAL(RCT2_ADDRESS_SAVED_VIEW_Y, uint16) = s4->view_y;
+ RCT2_GLOBAL(RCT2_ADDRESS_SAVED_VIEW_ZOOM_AND_ROTATION, uint8) = s4->view_zoom;
+ RCT2_GLOBAL(RCT2_ADDRESS_SAVED_VIEW_ZOOM_AND_ROTATION + 1, uint8) = s4->view_rotation;
+}
+
+#pragma endregion
diff --git a/src/rct1.h b/src/rct1.h
index 0307647a52..b28721fe74 100644
--- a/src/rct1.h
+++ b/src/rct1.h
@@ -511,4 +511,7 @@ void rct1_fix_landscape();
bool vehicleIsHigherInHierarchy(int track_type, char *currentVehicleName, char *comparedVehicleName);
bool rideTypeShouldLoseSeparateFlag(rct_ride_entry *ride);
+bool rct1_load_saved_game(const char *path);
+bool rct1_load_scenario(const char *path);
+
#endif
diff --git a/src/rct2.c b/src/rct2.c
index e862f2d89a..3a03265152 100644
--- a/src/rct2.c
+++ b/src/rct2.c
@@ -365,10 +365,12 @@ bool rct2_open_file(const char *path)
scenario_load_and_play_from_path(scenarioBasic.path);
return true;
} else if (_stricmp(extension, "td6") == 0 || _stricmp(extension, "td4") == 0) {
- return true;
- } else if (!_stricmp(extension, "td6") || !_stricmp(extension, "td4")) {
// TODO track design install
return true;
+ } else if (_stricmp(extension, "sv4") == 0) {
+ rct1_load_saved_game(path);
+ } else if (_stricmp(extension, "sc4") == 0) {
+ rct1_load_scenario(path);
}
return false;