1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-15 19:13:07 +01:00
Files
OpenRCT2/src/rct2.c
Ted John 0fbdcbe62d Unload objects before g1 and g2
This fixes a crash that happens when exiting the game where g1 elements has been disposed before the object images get disposed. Only apparent in NO_RCT2 or x64 builds where g1 elements is a dynamically allocated.
2016-08-08 01:30:55 +01:00

518 lines
12 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
#pragma warning(disable : 4996) // GetVersionExA deprecated
#include <time.h>
#include "addresses.h"
#include "audio/audio.h"
#include "audio/mixer.h"
#include "config.h"
#include "drawing/drawing.h"
#include "editor.h"
#include "game.h"
#include "input.h"
#include "interface/chat.h"
#include "interface/console.h"
#include "interface/viewport.h"
#include "intro.h"
#include "localisation/date.h"
#include "localisation/localisation.h"
#include "management/news_item.h"
#include "network/network.h"
#include "network/twitch.h"
#include "object.h"
#include "object/ObjectManager.h"
#include "openrct2.h"
#include "peep/staff.h"
#include "platform/platform.h"
#include "rct1.h"
#include "ride/ride.h"
#include "ride/track.h"
#include "ride/track_design.h"
#include "scenario.h"
#include "title.h"
#include "util/util.h"
#include "world/map.h"
#include "world/park.h"
#include "world/climate.h"
#include "world/scenery.h"
#include "world/sprite.h"
// rct2: 0x0097F67C
const char * const RCT2FilePaths[PATH_ID_END] = {
"Data\\g1.dat",
"Data\\plugin.dat",
"Data\\css1.dat",
"Data\\css2.dat",
"Data\\css4.dat",
"Data\\css5.dat",
"Data\\css6.dat",
"Data\\css7.dat",
"Data\\css8.dat",
"Data\\css9.dat",
"Data\\css11.dat",
"Data\\css12.dat",
"Data\\css13.dat",
"Data\\css14.dat",
"Data\\css15.dat",
"Data\\css3.dat",
"Data\\css17.dat",
"Data\\css18.dat",
"Data\\css19.dat",
"Data\\css20.dat",
"Data\\css21.dat",
"Data\\css22.dat",
"Saved Games\\scores.dat",
"Data\\css23.dat",
"Data\\css24.dat",
"Data\\css25.dat",
"Data\\css26.dat",
"Data\\css27.dat",
"Data\\css28.dat",
"Data\\css29.dat",
"Data\\css30.dat",
"Data\\css31.dat",
"Data\\css32.dat",
"Data\\css33.dat",
"Data\\css34.dat",
"Data\\css35.dat",
"Data\\css36.dat",
"Data\\css37.dat",
"Data\\css38.dat",
"Data\\CUSTOM1.WAV",
"Data\\CUSTOM2.WAV",
"Data\\css39.dat",
"Data\\css40.dat",
"Data\\css41.dat",
"Scenarios\\Six Flags Magic Mountain.SC6",
"Data\\css42.dat",
"Data\\css43.dat",
"Data\\css44.dat",
"Data\\css45.dat",
"Data\\css46.dat",
"Data\\css50.dat"
};
uint32 gCurrentDrawCount = 0;
uint8 gScreenFlags;
uint32 gScreenAge;
uint8 gSavePromptMode;
sint32 gScreenWidth;
sint32 gScreenHeight;
char gRCT2AddressSavedGamesPath[MAX_PATH];
char gRCT2AddressSavedGamesPath2[MAX_PATH];
char gRCT2AddressScenariosPath[MAX_PATH];
char gRCT2AddressLandscapesPath[MAX_PATH];
char gRCT2AddressObjectDataPath[MAX_PATH];
char gRCT2AddressTracksPath[MAX_PATH];
typedef struct tm tm_t;
void print_launch_information();
int rct2_init_directories();
int rct2_startup_checks();
static void rct2_draw_fps(rct_drawpixelinfo *dpi);
void rct2_quit()
{
gSavePromptMode = PM_QUIT;
window_save_prompt_open();
}
void rct2_dispose()
{
object_manager_unload_all_objects();
gfx_unload_g2();
gfx_unload_g1();
}
int rct2_init()
{
log_verbose("initialising game");
gScenarioTicks = 0;
util_srand((unsigned int)time(0));
if (!rct2_init_directories())
return 0;
if (!rct2_startup_checks())
return 0;
config_reset_shortcut_keys();
config_shortcut_keys_load();
gInputPlaceObjectModifier = PLACE_OBJECT_MODIFIER_NONE;
// config_load();
gfx_load_g1();
gfx_load_g2();
object_list_load();
scenario_load_list();
track_design_index_create();
font_sprite_initialise_characters();
if (!gOpenRCT2Headless) {
platform_init();
audio_init_ride_sounds_and_info();
}
viewport_init_all();
news_item_init_queue();
reset_park_entrances();
user_string_clear_all();
reset_sprite_list();
ride_init_all();
window_guest_list_init_vars_a();
staff_reset_modes();
map_init(150);
park_init();
if (!gOpenRCT2Headless)
window_title_menu_open();
date_reset();
climate_reset(CLIMATE_COOL_AND_WET);
scenery_set_default_placement_configuration();
window_new_ride_init_vars();
window_guest_list_init_vars_b();
window_staff_list_init_vars();
if (!gOpenRCT2Headless) {
title_load();
}
log_verbose("initialising game finished");
return 1;
}
/**
*
* rct2: 0x00683499
*/
int rct2_init_directories()
{
// windows_get_registry_install_info((rct2_install_info*)0x009AA10C, "RollerCoaster Tycoon 2 Setup", "MS Sans Serif", 0);
// check install directory
if (!platform_original_game_data_exists(gConfigGeneral.game_path)) {
log_verbose("install directory does not exist or invalid directory selected, %s", gConfigGeneral.game_path);
if (!config_find_or_browse_install_directory()) {
utf8 path[MAX_PATH];
config_get_default_path(path);
log_fatal("Invalid RCT2 installation path. Please correct \"game_path\" in %s.", path);
return 0;
}
}
char separator[] = {platform_get_path_separator(), 0};
char gRCT2AddressAppPath[MAX_PATH] = { 0 };
strcpy(gRCT2AddressAppPath, gConfigGeneral.game_path);
strcat(gRCT2AddressAppPath, separator);
strcpy(gRCT2AddressSavedGamesPath, gRCT2AddressAppPath);
strcat(gRCT2AddressSavedGamesPath, "Saved Games");
strcat(gRCT2AddressSavedGamesPath, separator);
strcpy(gRCT2AddressScenariosPath, gRCT2AddressAppPath);
strcat(gRCT2AddressScenariosPath, "Scenarios");
strcat(gRCT2AddressScenariosPath, separator);
strcat(gRCT2AddressScenariosPath, "*.SC6");
strcpy(gRCT2AddressLandscapesPath, gRCT2AddressAppPath);
strcat(gRCT2AddressLandscapesPath, "Landscapes");
strcat(gRCT2AddressLandscapesPath, separator);
strcat(gRCT2AddressLandscapesPath, "*.SC6");
strcpy(gRCT2AddressObjectDataPath, gRCT2AddressAppPath);
strcat(gRCT2AddressObjectDataPath, "ObjData");
strcat(gRCT2AddressObjectDataPath, separator);
strcat(gRCT2AddressObjectDataPath, "*.DAT");
strcpy(gRCT2AddressTracksPath, gRCT2AddressAppPath);
strcat(gRCT2AddressTracksPath, "Tracks");
strcat(gRCT2AddressTracksPath, separator);
strcat(gRCT2AddressTracksPath, "*.TD?");
strcpy(gRCT2AddressSavedGamesPath2, gRCT2AddressSavedGamesPath);
return 1;
}
void substitute_path(char *dest, const char *path, const char *filename)
{
while (*path != '*') {
*dest++ = *path++;
}
strcpy(dest, filename);
}
/**
*
* rct2: 0x00674B42
*/
int rct2_startup_checks()
{
if (!check_file_paths())
return 0;
return 1;
}
void rct2_draw(rct_drawpixelinfo *dpi)
{
if (gIntroState != INTRO_STATE_NONE) {
return;
}
// redraw_rain();
// window_update_all();
// gfx_invalidate_pickedup_peep();
// gfx_draw_pickedup_peep();
// update_rain_animation();
update_palette_effects();
chat_draw(dpi);
console_draw(dpi);
if (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) {
DrawOpenRCT2(dpi, 0, gScreenHeight - 20);
}
if (gConfigGeneral.show_fps) {
rct2_draw_fps(dpi);
}
gCurrentDrawCount++;
}
static time_t _lastSecond;
static int _currentFPS;
static int _frames;
static void rct2_measure_fps()
{
_frames++;
time_t currentTime = time(NULL);
if (currentTime != _lastSecond) {
_currentFPS = _frames;
_frames = 0;
}
_lastSecond = currentTime;
}
static void rct2_draw_fps(rct_drawpixelinfo *dpi)
{
int x = gScreenWidth / 2;
int y = 2;
// Measure FPS
rct2_measure_fps();
// Format string
utf8 buffer[64];
utf8 *ch = buffer;
ch = utf8_write_codepoint(ch, FORMAT_MEDIUMFONT);
ch = utf8_write_codepoint(ch, FORMAT_OUTLINE);
ch = utf8_write_codepoint(ch, FORMAT_WHITE);
sprintf(ch, "%d", _currentFPS);
// Draw Text
int stringWidth = gfx_get_string_width(buffer);
x = x - (stringWidth / 2);
gfx_draw_string(dpi, buffer, 0, x, y);
// Make area dirty so the text doesn't get drawn over the last
gfx_set_dirty_blocks(x - 16, y - 4, gLastDrawStringX + 16, 16);
}
bool rct2_open_file(const char *path)
{
char *extension = strrchr(path, '.');
if (extension == NULL) {
return false;
}
extension++;
if (_stricmp(extension, "sv6") == 0) {
strcpy((char*)gRCT2AddressSavedGamesPath2, path);
game_load_save(path);
gFirstTimeSave = 0;
return true;
} else if (_stricmp(extension, "sc6") == 0) {
// TODO scenario install
rct_scenario_basic scenarioBasic;
strcpy(scenarioBasic.path, path);
scenario_load_and_play_from_path(scenarioBasic.path);
return true;
} else if (_stricmp(extension, "td6") == 0 || _stricmp(extension, "td4") == 0) {
// TODO track design install
return true;
} else if (_stricmp(extension, "sv4") == 0) {
if (rct1_load_saved_game(path)) {
game_load_init();
}
} else if (_stricmp(extension, "sc4") == 0) {
if (rct1_load_scenario(path)) {
scenario_begin();
}
}
return false;
}
/**
*
* rct2: 0x00674C95
*/
int check_file_paths()
{
for (int pathId = 0; pathId < PATH_ID_END; pathId++) {
if (!check_file_path(pathId)) {
return 0;
}
}
return 1;
}
/**
*
* rct2: 0x00674CA5
*/
int check_file_path(int pathId)
{
const utf8* path = get_file_path(pathId);
SDL_RWops *file = SDL_RWFromFile(path, "rb");
switch (pathId) {
case PATH_ID_G1:
if (file == NULL) {
log_fatal("Could not find file %s", path);
return 0;
}
break;
case PATH_ID_CUSTOM1:
if (file != NULL) {
// Store file size in music_custom1_size @ 0x009AF164
gRideMusicInfoList[36]->length = (uint32)SDL_RWsize(file);
}
break;
case PATH_ID_CUSTOM2:
if (file != NULL) {
// Store file size in music_custom2_size @ 0x009AF16E
gRideMusicInfoList[37]->length = (uint32)SDL_RWsize(file);
}
break;
}
if (file != NULL) {
SDL_RWclose(file);
}
return 1;
}
void rct2_update()
{
int tickCount = SDL_GetTicks();
gTicksSinceLastUpdate = min(tickCount - gLastTickCount, 500);
gLastTickCount = tickCount;
if (game_is_not_paused()) {
gPaletteEffectFrame += gTicksSinceLastUpdate;
}
date_update_real_time_of_day();
// TODO: screenshot countdown process
network_update();
// check_cmdline_arg();
// Screens
if (gIntroState != INTRO_STATE_NONE) {
intro_update();
} else if ((gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) && !gOpenRCT2Headless) {
title_update();
} else {
game_update();
}
//stop_completed_sounds(); // removes other sounds that are no longer playing in directsound
twitch_update();
chat_update();
console_update();
}
/**
*
* rct2: 0x00674E6C
*/
const utf8 *get_file_path(int pathId)
{
static utf8 path[MAX_PATH];
strcpy(path, gConfigGeneral.game_path);
// Make sure base path is terminated with a slash
if (strlen(path) == 0 || path[strlen(path) - 1] != platform_get_path_separator()) {
if (strlen(path) >= MAX_PATH - 1) {
log_error("Path for %s too long", RCT2FilePaths[pathId]);
path[0] = '\0';
return path;
}
char separator[] = {platform_get_path_separator(), 0};
strcat(path, separator);
}
// Concatenate file path
if (strlen(path) + strlen(RCT2FilePaths[pathId]) > MAX_PATH) {
log_error("Path for %s too long", RCT2FilePaths[pathId]);
path[0] = '\0';
return path;
}
char *pathp = path + strnlen(path, sizeof(path));
strcat(path, RCT2FilePaths[pathId]);
while (*pathp) {
if (*pathp == '\\') *pathp = platform_get_path_separator();
pathp++;
}
return path;
}
uint32 get_file_extension_type(const utf8 *path)
{
const utf8 *extension = path_get_extension(path);
if (strcicmp(extension, ".dat") == 0) return FILE_EXTENSION_DAT;
if (strcicmp(extension, ".sc4") == 0) return FILE_EXTENSION_SC4;
if (strcicmp(extension, ".sv4") == 0) return FILE_EXTENSION_SV4;
if (strcicmp(extension, ".td4") == 0) return FILE_EXTENSION_TD4;
if (strcicmp(extension, ".sc6") == 0) return FILE_EXTENSION_SC6;
if (strcicmp(extension, ".sv6") == 0) return FILE_EXTENSION_SV6;
if (strcicmp(extension, ".td6") == 0) return FILE_EXTENSION_TD6;
return FILE_EXTENSION_UNKNOWN;
}