2017-06-01 21:55:10 +02:00
|
|
|
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
|
2017-03-24 03:47:43 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
* 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
|
|
|
|
|
2017-03-25 04:16:40 +01:00
|
|
|
#include <exception>
|
2017-03-26 22:04:14 +02:00
|
|
|
#include <memory>
|
|
|
|
#include <string>
|
2017-09-04 23:17:35 +02:00
|
|
|
#ifdef __EMSCRIPTEN__
|
|
|
|
#include <emscripten.h>
|
|
|
|
#endif // __EMSCRIPTEN__
|
2017-06-04 00:10:20 +02:00
|
|
|
#include "audio/AudioContext.h"
|
2017-03-24 03:47:43 +01:00
|
|
|
#include "Context.h"
|
2017-03-25 15:57:02 +01:00
|
|
|
#include "ui/UiContext.h"
|
2017-03-26 22:04:14 +02:00
|
|
|
#include "core/Console.hpp"
|
|
|
|
#include "core/File.h"
|
2017-06-26 23:00:01 +02:00
|
|
|
#include "core/FileScanner.h"
|
2017-03-26 22:04:14 +02:00
|
|
|
#include "core/FileStream.hpp"
|
|
|
|
#include "core/Guard.hpp"
|
2017-07-16 09:54:34 +02:00
|
|
|
#include "core/Math.hpp"
|
2017-06-07 00:42:28 +02:00
|
|
|
#include "core/MemoryStream.h"
|
2017-06-26 23:00:01 +02:00
|
|
|
#include "core/Path.hpp"
|
2017-03-26 22:04:14 +02:00
|
|
|
#include "core/String.hpp"
|
|
|
|
#include "FileClassifier.h"
|
|
|
|
#include "network/network.h"
|
2017-06-22 19:56:32 +02:00
|
|
|
#include "object/ObjectManager.h"
|
2017-03-26 22:04:14 +02:00
|
|
|
#include "object/ObjectRepository.h"
|
|
|
|
#include "OpenRCT2.h"
|
|
|
|
#include "ParkImporter.h"
|
|
|
|
#include "platform/crash.h"
|
|
|
|
#include "PlatformEnvironment.h"
|
|
|
|
#include "ride/TrackDesignRepository.h"
|
|
|
|
#include "scenario/ScenarioRepository.h"
|
|
|
|
#include "title/TitleScreen.h"
|
|
|
|
#include "title/TitleSequenceManager.h"
|
2017-06-11 16:16:47 +02:00
|
|
|
#include "ui/WindowManager.h"
|
2017-03-26 22:04:14 +02:00
|
|
|
#include "Version.h"
|
|
|
|
|
2017-09-18 17:05:28 +02:00
|
|
|
#include "audio/audio.h"
|
|
|
|
#include "config/Config.h"
|
|
|
|
#include "drawing/lightfx.h"
|
2017-09-22 07:56:24 +02:00
|
|
|
#include "Editor.h"
|
2017-09-18 17:05:28 +02:00
|
|
|
#include "game.h"
|
|
|
|
#include "input.h"
|
|
|
|
#include "interface/chat.h"
|
|
|
|
#include "interface/console.h"
|
|
|
|
#include "interface/themes.h"
|
|
|
|
#include "intro.h"
|
|
|
|
#include "localisation/localisation.h"
|
|
|
|
#include "network/http.h"
|
|
|
|
#include "network/network.h"
|
|
|
|
#include "network/twitch.h"
|
|
|
|
#include "object_list.h"
|
|
|
|
#include "platform/platform.h"
|
|
|
|
#include "rct1.h"
|
|
|
|
#include "rct2/interop.h"
|
|
|
|
#include "util/util.h"
|
2017-03-24 03:47:43 +01:00
|
|
|
|
|
|
|
using namespace OpenRCT2;
|
2017-03-28 20:58:15 +02:00
|
|
|
using namespace OpenRCT2::Audio;
|
2017-03-25 04:16:40 +01:00
|
|
|
using namespace OpenRCT2::Ui;
|
2017-03-24 03:47:43 +01:00
|
|
|
|
2017-03-25 04:16:40 +01:00
|
|
|
namespace OpenRCT2
|
2017-03-24 03:47:43 +01:00
|
|
|
{
|
2017-03-25 04:16:40 +01:00
|
|
|
class Context : public IContext
|
2017-03-24 03:47:43 +01:00
|
|
|
{
|
2017-03-25 04:16:40 +01:00
|
|
|
private:
|
2017-03-26 22:04:14 +02:00
|
|
|
// Dependencies
|
2017-07-10 21:43:20 +02:00
|
|
|
IPlatformEnvironment * const _env = nullptr;
|
|
|
|
IAudioContext * const _audioContext = nullptr;
|
|
|
|
IUiContext * const _uiContext = nullptr;
|
2017-03-26 22:04:14 +02:00
|
|
|
|
|
|
|
// Services
|
2017-07-10 21:43:20 +02:00
|
|
|
IObjectRepository * _objectRepository = nullptr;
|
|
|
|
IObjectManager * _objectManager = nullptr;
|
|
|
|
ITrackDesignRepository * _trackDesignRepository = nullptr;
|
|
|
|
IScenarioRepository * _scenarioRepository = nullptr;
|
2017-03-26 22:04:14 +02:00
|
|
|
|
2017-07-23 00:42:14 +02:00
|
|
|
// Game states
|
|
|
|
TitleScreen * _titleScreen = nullptr;
|
|
|
|
|
2017-07-20 18:44:31 +02:00
|
|
|
bool _initialised = false;
|
2017-06-26 00:45:19 +02:00
|
|
|
bool _isWindowMinimised = false;
|
|
|
|
uint32 _lastTick = 0;
|
|
|
|
uint32 _accumulator = 0;
|
|
|
|
uint32 _lastUpdateTick = 0;
|
2017-09-07 12:02:21 +02:00
|
|
|
bool _variableFrame = false;
|
2017-03-26 22:04:14 +02:00
|
|
|
|
|
|
|
/** If set, will end the OpenRCT2 game loop. Intentially private to this module so that the flag can not be set back to false. */
|
|
|
|
bool _finished = false;
|
2017-03-25 04:16:40 +01:00
|
|
|
|
|
|
|
public:
|
|
|
|
// Singleton of Context.
|
|
|
|
// Remove this when GetContext() is no longer called so that
|
|
|
|
// multiple instances can be created in parallel
|
|
|
|
static Context * Instance;
|
|
|
|
|
|
|
|
public:
|
2017-06-11 13:53:37 +02:00
|
|
|
Context(IPlatformEnvironment * env, IAudioContext * audioContext, IUiContext * uiContext)
|
|
|
|
: _env(env),
|
|
|
|
_audioContext(audioContext),
|
2017-03-28 20:58:15 +02:00
|
|
|
_uiContext(uiContext)
|
2017-03-25 04:16:40 +01:00
|
|
|
{
|
|
|
|
Instance = this;
|
|
|
|
}
|
2017-03-24 03:47:43 +01:00
|
|
|
|
2017-03-25 04:16:40 +01:00
|
|
|
~Context() override
|
|
|
|
{
|
2017-03-26 22:04:14 +02:00
|
|
|
network_close();
|
|
|
|
http_dispose();
|
|
|
|
language_close_all();
|
2017-06-26 01:02:56 +02:00
|
|
|
object_manager_unload_all_objects();
|
|
|
|
gfx_object_check_all_images_freed();
|
|
|
|
gfx_unload_g2();
|
|
|
|
gfx_unload_g1();
|
2017-03-26 22:04:14 +02:00
|
|
|
config_release();
|
|
|
|
#ifndef DISABLE_NETWORK
|
|
|
|
EVP_MD_CTX_destroy(gHashCTX);
|
|
|
|
#endif // DISABLE_NETWORK
|
|
|
|
rct2_interop_dispose();
|
2017-07-20 18:44:31 +02:00
|
|
|
|
2017-07-23 00:42:14 +02:00
|
|
|
delete _titleScreen;
|
|
|
|
|
2017-07-20 18:44:31 +02:00
|
|
|
delete _scenarioRepository;
|
|
|
|
delete _trackDesignRepository;
|
|
|
|
delete _objectManager;
|
|
|
|
delete _objectRepository;
|
|
|
|
|
2017-03-25 04:16:40 +01:00
|
|
|
Instance = nullptr;
|
|
|
|
}
|
|
|
|
|
2017-03-28 20:58:15 +02:00
|
|
|
IAudioContext * GetAudioContext() override
|
|
|
|
{
|
|
|
|
return _audioContext;
|
|
|
|
}
|
|
|
|
|
2017-03-25 04:16:40 +01:00
|
|
|
IUiContext * GetUiContext() override
|
|
|
|
{
|
|
|
|
return _uiContext;
|
|
|
|
}
|
|
|
|
|
|
|
|
sint32 RunOpenRCT2(int argc, char * * argv) override
|
|
|
|
{
|
2017-06-03 23:13:20 +02:00
|
|
|
if (Initialise())
|
|
|
|
{
|
|
|
|
Launch();
|
|
|
|
}
|
2017-03-25 04:16:40 +01:00
|
|
|
return gExitCode;
|
|
|
|
}
|
2017-03-26 22:04:14 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Causes the OpenRCT2 game loop to finish.
|
|
|
|
*/
|
2017-05-06 22:53:51 +02:00
|
|
|
void Finish() override
|
2017-03-26 22:04:14 +02:00
|
|
|
{
|
|
|
|
_finished = true;
|
|
|
|
}
|
|
|
|
|
2017-07-28 19:43:48 +02:00
|
|
|
void Quit() override
|
|
|
|
{
|
|
|
|
gSavePromptMode = PM_QUIT;
|
2017-08-06 05:22:00 +02:00
|
|
|
context_open_window(WC_SAVE_PROMPT);
|
2017-07-28 19:43:48 +02:00
|
|
|
}
|
|
|
|
|
2017-07-28 20:11:12 +02:00
|
|
|
std::string GetPathLegacy(sint32 pathId) override
|
|
|
|
{
|
|
|
|
static const char * const LegacyFileNames[PATH_ID_END] =
|
|
|
|
{
|
|
|
|
nullptr,
|
|
|
|
nullptr,
|
|
|
|
"css1.dat",
|
|
|
|
"css2.dat",
|
|
|
|
"css4.dat",
|
|
|
|
"css5.dat",
|
|
|
|
"css6.dat",
|
|
|
|
"css7.dat",
|
|
|
|
"css8.dat",
|
|
|
|
"css9.dat",
|
|
|
|
"css11.dat",
|
|
|
|
"css12.dat",
|
|
|
|
"css13.dat",
|
|
|
|
"css14.dat",
|
|
|
|
"css15.dat",
|
|
|
|
"css3.dat",
|
|
|
|
"css17.dat",
|
|
|
|
"css18.dat",
|
|
|
|
"css19.dat",
|
|
|
|
"css20.dat",
|
|
|
|
"css21.dat",
|
|
|
|
"css22.dat",
|
|
|
|
nullptr,
|
|
|
|
"css23.dat",
|
|
|
|
"css24.dat",
|
|
|
|
"css25.dat",
|
|
|
|
"css26.dat",
|
|
|
|
"css27.dat",
|
|
|
|
"css28.dat",
|
|
|
|
"css29.dat",
|
|
|
|
"css30.dat",
|
|
|
|
"css31.dat",
|
|
|
|
"css32.dat",
|
|
|
|
"css33.dat",
|
|
|
|
"css34.dat",
|
|
|
|
"css35.dat",
|
|
|
|
"css36.dat",
|
|
|
|
"css37.dat",
|
|
|
|
"css38.dat",
|
|
|
|
"CUSTOM1.WAV",
|
|
|
|
"CUSTOM2.WAV",
|
|
|
|
"css39.dat",
|
|
|
|
"css40.dat",
|
|
|
|
"css41.dat",
|
|
|
|
nullptr,
|
|
|
|
"css42.dat",
|
|
|
|
"css43.dat",
|
|
|
|
"css44.dat",
|
|
|
|
"css45.dat",
|
|
|
|
"css46.dat",
|
|
|
|
"css50.dat"
|
|
|
|
};
|
|
|
|
|
|
|
|
std::string result;
|
|
|
|
if (pathId == PATH_ID_CSS50)
|
|
|
|
{
|
|
|
|
auto dataPath = _env->GetDirectoryPath(DIRBASE::RCT1, DIRID::DATA);
|
|
|
|
result = Path::Combine(dataPath, "css17.dat");
|
|
|
|
}
|
|
|
|
else if (pathId >= 0 && pathId < PATH_ID_END)
|
|
|
|
{
|
|
|
|
auto fileName = LegacyFileNames[pathId];
|
|
|
|
if (fileName != nullptr)
|
|
|
|
{
|
|
|
|
auto dataPath = _env->GetDirectoryPath(DIRBASE::RCT2, DIRID::DATA);
|
|
|
|
result = Path::Combine(dataPath, fileName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-06-04 00:10:20 +02:00
|
|
|
bool Initialise() final override
|
2017-03-26 22:04:14 +02:00
|
|
|
{
|
2017-07-20 18:44:31 +02:00
|
|
|
if (_initialised)
|
|
|
|
{
|
|
|
|
throw std::runtime_error("Context already initialised.");
|
|
|
|
}
|
|
|
|
_initialised = true;
|
|
|
|
|
2017-03-26 22:04:14 +02:00
|
|
|
#ifndef DISABLE_NETWORK
|
|
|
|
gHashCTX = EVP_MD_CTX_create();
|
|
|
|
Guard::Assert(gHashCTX != nullptr, "EVP_MD_CTX_create failed");
|
|
|
|
#endif // DISABLE_NETWORK
|
|
|
|
|
|
|
|
crash_init();
|
|
|
|
|
|
|
|
if (!rct2_interop_setup_segment())
|
|
|
|
{
|
|
|
|
log_fatal("Unable to load RCT2 data sector");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gConfigGeneral.last_run_version != nullptr && String::Equals(gConfigGeneral.last_run_version, OPENRCT2_VERSION))
|
|
|
|
{
|
|
|
|
gOpenRCT2ShowChangelog = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gOpenRCT2ShowChangelog = true;
|
|
|
|
gConfigGeneral.last_run_version = String::Duplicate(OPENRCT2_VERSION);
|
|
|
|
config_save_default();
|
|
|
|
}
|
|
|
|
|
2017-10-14 22:16:51 +02:00
|
|
|
// TODO add configuration option to allow multiple instances
|
|
|
|
// if (!gOpenRCT2Headless && !platform_lock_single_instance()) {
|
|
|
|
// log_fatal("OpenRCT2 is already running.");
|
|
|
|
// return false;
|
|
|
|
// } //This comment was relocated so it would stay where it was in relation to the following lines of code.
|
|
|
|
|
|
|
|
_objectRepository = CreateObjectRepository(_env);
|
|
|
|
_objectManager = CreateObjectManager(_objectRepository);
|
|
|
|
_trackDesignRepository = CreateTrackDesignRepository(_env);
|
|
|
|
_scenarioRepository = CreateScenarioRepository(_env);
|
|
|
|
|
|
|
|
if (!language_open(gConfigGeneral.language))
|
|
|
|
{
|
|
|
|
log_error("Failed to open configured language...");
|
|
|
|
if (!language_open(LANGUAGE_ENGLISH_UK))
|
|
|
|
{
|
|
|
|
log_fatal("Failed to open fallback language...");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-29 16:21:33 +02:00
|
|
|
if (platform_process_is_elevated())
|
|
|
|
{
|
2017-10-14 22:16:51 +02:00
|
|
|
std::string elevationWarning = language_get_string(STR_ADMIN_NOT_RECOMMENDED);
|
2017-07-29 16:21:33 +02:00
|
|
|
if (gOpenRCT2Headless)
|
|
|
|
{
|
|
|
|
Console::Error::WriteLine(elevationWarning.c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_uiContext->ShowMessageBox(elevationWarning);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-28 20:11:12 +02:00
|
|
|
auto rct2InstallPath = GetOrPromptRCT2Path();
|
|
|
|
if (rct2InstallPath.empty())
|
2017-06-13 01:38:56 +02:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2017-07-28 20:11:12 +02:00
|
|
|
_env->SetBasePath(DIRBASE::RCT2, rct2InstallPath);
|
2017-06-13 01:38:56 +02:00
|
|
|
|
2017-03-26 22:04:14 +02:00
|
|
|
if (!gOpenRCT2Headless)
|
|
|
|
{
|
2017-06-26 00:45:19 +02:00
|
|
|
_uiContext->CreateWindow();
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-10-14 22:16:51 +02:00
|
|
|
|
|
|
|
|
2017-03-26 22:04:14 +02:00
|
|
|
|
|
|
|
// TODO Ideally we want to delay this until we show the title so that we can
|
|
|
|
// still open the game window and draw a progress screen for the creation
|
|
|
|
// of the object cache.
|
|
|
|
_objectRepository->LoadOrConstruct();
|
|
|
|
|
|
|
|
// TODO Like objects, this can take a while if there are a lot of track designs
|
|
|
|
// its also really something really we might want to do in the background
|
|
|
|
// as its not required until the player wants to place a new ride.
|
|
|
|
_trackDesignRepository->Scan();
|
|
|
|
|
|
|
|
_scenarioRepository->Scan();
|
|
|
|
TitleSequenceManager::Scan();
|
|
|
|
|
|
|
|
if (!gOpenRCT2Headless)
|
|
|
|
{
|
|
|
|
audio_init();
|
|
|
|
audio_populate_devices();
|
2017-06-26 01:02:56 +02:00
|
|
|
audio_init_ride_sounds_and_info();
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
http_init();
|
2017-06-06 18:55:13 +02:00
|
|
|
network_set_env(_env);
|
2017-06-26 01:02:56 +02:00
|
|
|
chat_init();
|
2017-03-26 22:04:14 +02:00
|
|
|
theme_manager_initialise();
|
2017-06-26 23:00:01 +02:00
|
|
|
CopyOriginalUserFilesOver();
|
2017-03-26 22:04:14 +02:00
|
|
|
|
|
|
|
rct2_interop_setup_hooks();
|
|
|
|
|
2017-06-26 01:02:56 +02:00
|
|
|
if (!gOpenRCT2NoGraphics)
|
2017-03-26 22:04:14 +02:00
|
|
|
{
|
2017-06-26 01:02:56 +02:00
|
|
|
LoadBaseGraphics();
|
|
|
|
#ifdef __ENABLE_LIGHTFX__
|
|
|
|
lightfx_init();
|
|
|
|
#endif
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
2017-06-26 01:02:56 +02:00
|
|
|
gScenarioTicks = 0;
|
|
|
|
util_srand((uint32)time(0));
|
|
|
|
input_reset_place_obj_modifier();
|
|
|
|
viewport_init_all();
|
|
|
|
game_init_all(150);
|
2017-07-23 00:42:14 +02:00
|
|
|
|
|
|
|
_titleScreen = new TitleScreen();
|
2017-03-26 22:04:14 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-08-23 08:04:36 +02:00
|
|
|
bool LoadParkFromFile(const std::string &path, bool loadTitleScreenOnFail = false) final override
|
2017-07-02 20:10:22 +02:00
|
|
|
{
|
|
|
|
auto fs = FileStream(path, FILE_MODE_OPEN);
|
2017-08-23 08:04:36 +02:00
|
|
|
return LoadParkFromStream(&fs, path, loadTitleScreenOnFail);
|
2017-07-02 20:10:22 +02:00
|
|
|
}
|
|
|
|
|
2017-06-04 00:10:20 +02:00
|
|
|
private:
|
2017-07-28 20:11:12 +02:00
|
|
|
std::string GetOrPromptRCT2Path()
|
|
|
|
{
|
|
|
|
auto result = std::string();
|
|
|
|
if (String::IsNullOrEmpty(gCustomRCT2DataPath))
|
|
|
|
{
|
|
|
|
// Check install directory
|
|
|
|
if (gConfigGeneral.rct2_path == nullptr || !platform_original_game_data_exists(gConfigGeneral.rct2_path))
|
|
|
|
{
|
|
|
|
log_verbose("install directory does not exist or invalid directory selected, %s", gConfigGeneral.rct2_path);
|
|
|
|
if (!config_find_or_browse_install_directory())
|
|
|
|
{
|
|
|
|
utf8 path[MAX_PATH];
|
|
|
|
config_get_default_path(path, sizeof(path));
|
|
|
|
Console::Error::WriteLine("An RCT2 install directory must be specified! Please edit \"game_path\" in %s.\n", path);
|
|
|
|
return std::string();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result = std::string(gConfigGeneral.rct2_path);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = std::string(gCustomRCT2DataPath);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-06-26 01:02:56 +02:00
|
|
|
bool LoadBaseGraphics()
|
|
|
|
{
|
2017-07-11 21:54:21 +02:00
|
|
|
if (!gfx_load_g1(_env))
|
2017-06-26 01:02:56 +02:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!gfx_load_g2())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
gfx_load_csg();
|
|
|
|
font_sprite_initialise_characters();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-26 22:04:14 +02:00
|
|
|
/**
|
|
|
|
* Launches the game, after command line arguments have been parsed and processed.
|
|
|
|
*/
|
|
|
|
void Launch()
|
|
|
|
{
|
|
|
|
gIntroState = INTRO_STATE_NONE;
|
|
|
|
if ((gOpenRCT2StartupAction == STARTUP_ACTION_TITLE) && gConfigGeneral.play_intro)
|
|
|
|
{
|
|
|
|
gOpenRCT2StartupAction = STARTUP_ACTION_INTRO;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (gOpenRCT2StartupAction) {
|
|
|
|
case STARTUP_ACTION_INTRO:
|
|
|
|
gIntroState = INTRO_STATE_PUBLISHER_BEGIN;
|
|
|
|
title_load();
|
|
|
|
break;
|
|
|
|
case STARTUP_ACTION_TITLE:
|
|
|
|
title_load();
|
|
|
|
break;
|
|
|
|
case STARTUP_ACTION_OPEN:
|
|
|
|
{
|
|
|
|
// A path that includes "://" is illegal with all common filesystems, so it is almost certainly a URL
|
|
|
|
// This way all cURL supported protocols, like http, ftp, scp and smb are automatically handled
|
|
|
|
if (strstr(gOpenRCT2StartupActionPath, "://") != nullptr)
|
|
|
|
{
|
|
|
|
#ifndef DISABLE_HTTP
|
|
|
|
// Download park and open it using its temporary filename
|
2017-06-07 00:42:28 +02:00
|
|
|
void * data;
|
|
|
|
size_t dataSize = http_download_park(gOpenRCT2StartupActionPath, &data);
|
|
|
|
if (dataSize == 0)
|
2017-03-26 22:04:14 +02:00
|
|
|
{
|
|
|
|
title_load();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-06-07 00:42:28 +02:00
|
|
|
auto ms = MemoryStream(data, dataSize, MEMORY_ACCESS::OWNER);
|
2017-08-23 08:04:36 +02:00
|
|
|
if (!LoadParkFromStream(&ms, gOpenRCT2StartupActionPath, true))
|
2017-07-02 20:10:22 +02:00
|
|
|
{
|
|
|
|
Console::Error::WriteLine("Failed to load '%s'", gOpenRCT2StartupActionPath);
|
|
|
|
title_load();
|
|
|
|
break;
|
|
|
|
}
|
2017-03-26 22:04:14 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-07-02 20:10:22 +02:00
|
|
|
try
|
|
|
|
{
|
2017-08-23 08:04:36 +02:00
|
|
|
if (!LoadParkFromFile(gOpenRCT2StartupActionPath, true))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2017-07-02 20:10:22 +02:00
|
|
|
}
|
|
|
|
catch (const std::exception &ex)
|
|
|
|
{
|
|
|
|
Console::Error::WriteLine("Failed to load '%s'", gOpenRCT2StartupActionPath);
|
|
|
|
Console::Error::WriteLine("%s", ex.what());
|
|
|
|
title_load();
|
|
|
|
break;
|
|
|
|
}
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
gScreenFlags = SCREEN_FLAGS_PLAYING;
|
|
|
|
|
|
|
|
#ifndef DISABLE_NETWORK
|
|
|
|
if (gNetworkStart == NETWORK_MODE_SERVER)
|
|
|
|
{
|
|
|
|
if (gNetworkStartPort == 0)
|
|
|
|
{
|
|
|
|
gNetworkStartPort = gConfigNetwork.default_port;
|
|
|
|
}
|
|
|
|
|
2017-05-04 12:48:18 +02:00
|
|
|
if (String::IsNullOrEmpty(gNetworkStartAddress))
|
|
|
|
{
|
|
|
|
gNetworkStartAddress = gConfigNetwork.listen_address;
|
|
|
|
}
|
|
|
|
|
2017-03-26 22:04:14 +02:00
|
|
|
if (String::IsNullOrEmpty(gCustomPassword))
|
|
|
|
{
|
|
|
|
network_set_password(gConfigNetwork.default_password);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
network_set_password(gCustomPassword);
|
|
|
|
}
|
2017-05-04 12:48:18 +02:00
|
|
|
network_begin_server(gNetworkStartPort, gNetworkStartAddress);
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
2017-07-16 09:54:34 +02:00
|
|
|
#endif // DISABLE_NETWORK
|
2017-03-26 22:04:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case STARTUP_ACTION_EDIT:
|
|
|
|
if (String::SizeOf(gOpenRCT2StartupActionPath) == 0)
|
|
|
|
{
|
2017-09-22 07:56:24 +02:00
|
|
|
Editor::Load();
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
2017-09-22 07:56:24 +02:00
|
|
|
else if (!Editor::LoadLandscape(gOpenRCT2StartupActionPath))
|
2017-03-26 22:04:14 +02:00
|
|
|
{
|
|
|
|
title_load();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef DISABLE_NETWORK
|
|
|
|
if (gNetworkStart == NETWORK_MODE_CLIENT)
|
|
|
|
{
|
|
|
|
if (gNetworkStartPort == 0)
|
|
|
|
{
|
|
|
|
gNetworkStartPort = gConfigNetwork.default_port;
|
|
|
|
}
|
|
|
|
network_begin_client(gNetworkStartHost, gNetworkStartPort);
|
|
|
|
}
|
|
|
|
#endif // DISABLE_NETWORK
|
|
|
|
|
|
|
|
RunGameLoop();
|
|
|
|
}
|
|
|
|
|
2017-07-10 21:43:20 +02:00
|
|
|
bool ShouldRunVariableFrame()
|
|
|
|
{
|
|
|
|
if (!gConfigGeneral.uncap_fps) return false;
|
|
|
|
if (gGameSpeed > 4) return false;
|
|
|
|
if (gOpenRCT2Headless) return false;
|
|
|
|
if (_uiContext->IsMinimised()) return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-26 22:04:14 +02:00
|
|
|
/**
|
|
|
|
* Run the main game loop until the finished flag is set.
|
|
|
|
*/
|
|
|
|
void RunGameLoop()
|
|
|
|
{
|
|
|
|
log_verbose("begin openrct2 loop");
|
|
|
|
_finished = false;
|
2017-07-10 21:43:20 +02:00
|
|
|
|
2017-09-04 23:17:35 +02:00
|
|
|
#ifndef __EMSCRIPTEN__
|
2017-09-07 12:02:21 +02:00
|
|
|
_variableFrame = ShouldRunVariableFrame();
|
2017-03-26 22:04:14 +02:00
|
|
|
do
|
|
|
|
{
|
2017-09-07 13:31:28 +02:00
|
|
|
RunFrame();
|
2017-09-07 13:21:09 +02:00
|
|
|
}
|
|
|
|
while (!_finished);
|
2017-09-04 23:17:35 +02:00
|
|
|
#else
|
2017-09-07 13:21:09 +02:00
|
|
|
emscripten_set_main_loop_arg([](void * vctx) ->
|
|
|
|
{
|
|
|
|
auto ctx = reinterpret_cast<Context *>(vctx);
|
|
|
|
ctx->RunFrame();
|
|
|
|
}, this, 0, 1);
|
2017-09-04 23:17:35 +02:00
|
|
|
#endif // __EMSCRIPTEN__
|
2017-03-26 22:04:14 +02:00
|
|
|
log_verbose("finish openrct2 loop");
|
|
|
|
}
|
|
|
|
|
2017-09-07 13:21:09 +02:00
|
|
|
void RunFrame()
|
|
|
|
{
|
|
|
|
// Make sure we catch the state change and reset it.
|
|
|
|
bool useVariableFrame = ShouldRunVariableFrame();
|
|
|
|
if (_variableFrame != useVariableFrame)
|
|
|
|
{
|
|
|
|
_lastTick = 0;
|
|
|
|
_variableFrame = useVariableFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (useVariableFrame)
|
|
|
|
{
|
|
|
|
RunVariableFrame();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
RunFixedFrame();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-26 22:04:14 +02:00
|
|
|
void RunFixedFrame()
|
|
|
|
{
|
2017-04-01 20:50:07 +02:00
|
|
|
uint32 currentTick = platform_get_ticks();
|
2017-07-10 21:43:20 +02:00
|
|
|
|
|
|
|
if (_lastTick == 0)
|
2017-03-26 22:04:14 +02:00
|
|
|
{
|
|
|
|
_lastTick = currentTick;
|
|
|
|
}
|
2017-07-10 21:43:20 +02:00
|
|
|
|
|
|
|
uint32 elapsed = currentTick - _lastTick;
|
|
|
|
_lastTick = currentTick;
|
2017-07-16 09:54:34 +02:00
|
|
|
_accumulator = Math::Min(_accumulator + elapsed, (uint32)GAME_UPDATE_MAX_THRESHOLD);
|
2017-07-10 21:43:20 +02:00
|
|
|
|
2017-06-26 00:45:19 +02:00
|
|
|
_uiContext->ProcessMessages();
|
2017-07-10 21:43:20 +02:00
|
|
|
|
2017-07-16 09:54:34 +02:00
|
|
|
if (_accumulator < GAME_UPDATE_TIME_MS)
|
2017-07-10 21:43:20 +02:00
|
|
|
{
|
2017-07-16 09:54:34 +02:00
|
|
|
platform_sleep(GAME_UPDATE_TIME_MS - _accumulator - 1);
|
2017-07-10 21:43:20 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-07-16 09:54:34 +02:00
|
|
|
_accumulator -= GAME_UPDATE_TIME_MS;
|
2017-07-10 21:43:20 +02:00
|
|
|
|
2017-06-26 00:45:19 +02:00
|
|
|
Update();
|
2017-07-10 21:43:20 +02:00
|
|
|
if (!_isWindowMinimised && !gOpenRCT2Headless)
|
2017-03-26 22:04:14 +02:00
|
|
|
{
|
2017-07-08 20:34:22 +02:00
|
|
|
drawing_engine_draw();
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RunVariableFrame()
|
|
|
|
{
|
2017-04-01 20:50:07 +02:00
|
|
|
uint32 currentTick = platform_get_ticks();
|
2017-07-10 21:43:20 +02:00
|
|
|
|
|
|
|
bool draw = !_isWindowMinimised && !gOpenRCT2Headless;
|
|
|
|
|
|
|
|
if (_lastTick == 0)
|
2017-03-26 22:04:14 +02:00
|
|
|
{
|
|
|
|
sprite_position_tween_reset();
|
2017-07-10 21:43:20 +02:00
|
|
|
_lastTick = currentTick;
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
|
|
|
|
2017-07-10 21:43:20 +02:00
|
|
|
uint32 elapsed = currentTick - _lastTick;
|
|
|
|
|
|
|
|
_lastTick = currentTick;
|
2017-07-16 09:54:34 +02:00
|
|
|
_accumulator = Math::Min(_accumulator + elapsed, (uint32)GAME_UPDATE_MAX_THRESHOLD);
|
2017-03-26 22:04:14 +02:00
|
|
|
|
2017-06-26 00:45:19 +02:00
|
|
|
_uiContext->ProcessMessages();
|
2017-03-26 22:04:14 +02:00
|
|
|
|
2017-07-16 09:54:34 +02:00
|
|
|
while (_accumulator >= GAME_UPDATE_TIME_MS)
|
2017-03-26 22:04:14 +02:00
|
|
|
{
|
|
|
|
// Get the original position of each sprite
|
2017-07-10 21:43:20 +02:00
|
|
|
if(draw)
|
|
|
|
sprite_position_tween_store_a();
|
2017-03-26 22:04:14 +02:00
|
|
|
|
2017-06-26 00:45:19 +02:00
|
|
|
Update();
|
2017-03-26 22:04:14 +02:00
|
|
|
|
2017-07-16 09:54:34 +02:00
|
|
|
_accumulator -= GAME_UPDATE_TIME_MS;
|
2017-03-26 22:04:14 +02:00
|
|
|
|
2017-07-10 21:43:20 +02:00
|
|
|
// Get the next position of each sprite
|
|
|
|
if(draw)
|
|
|
|
sprite_position_tween_store_b();
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
|
|
|
|
2017-07-10 21:43:20 +02:00
|
|
|
if (draw)
|
|
|
|
{
|
2017-07-16 09:54:34 +02:00
|
|
|
const float alpha = (float)_accumulator / GAME_UPDATE_TIME_MS;
|
2017-07-10 21:43:20 +02:00
|
|
|
sprite_position_tween_all(alpha);
|
2017-03-26 22:04:14 +02:00
|
|
|
|
2017-07-08 20:34:22 +02:00
|
|
|
drawing_engine_draw();
|
2017-03-26 22:04:14 +02:00
|
|
|
|
2017-07-10 21:43:20 +02:00
|
|
|
sprite_position_tween_restore();
|
|
|
|
}
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
|
|
|
|
2017-06-26 00:45:19 +02:00
|
|
|
void Update()
|
|
|
|
{
|
|
|
|
uint32 currentUpdateTick = platform_get_ticks();
|
|
|
|
gTicksSinceLastUpdate = std::min<uint32>(currentUpdateTick - _lastUpdateTick, 500);
|
|
|
|
_lastUpdateTick = currentUpdateTick;
|
|
|
|
|
|
|
|
if (game_is_not_paused())
|
|
|
|
{
|
|
|
|
gPaletteEffectFrame += gTicksSinceLastUpdate;
|
|
|
|
}
|
|
|
|
|
|
|
|
date_update_real_time_of_day();
|
|
|
|
|
|
|
|
if (gIntroState != INTRO_STATE_NONE)
|
|
|
|
{
|
|
|
|
intro_update();
|
|
|
|
}
|
|
|
|
else if ((gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) && !gOpenRCT2Headless)
|
|
|
|
{
|
2017-07-23 00:42:14 +02:00
|
|
|
_titleScreen->Update();
|
2017-06-26 00:45:19 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
game_update();
|
|
|
|
}
|
|
|
|
|
|
|
|
twitch_update();
|
|
|
|
chat_update();
|
|
|
|
console_update();
|
|
|
|
}
|
|
|
|
|
2017-08-23 08:04:36 +02:00
|
|
|
bool LoadParkFromStream(IStream * stream, const std::string &path, bool loadTitleScreenFirstOnFail)
|
2017-03-26 22:04:14 +02:00
|
|
|
{
|
2017-08-23 08:04:36 +02:00
|
|
|
ClassifiedFileInfo info;
|
2017-06-07 00:42:28 +02:00
|
|
|
if (TryClassifyFile(stream, &info))
|
2017-03-26 22:04:14 +02:00
|
|
|
{
|
|
|
|
if (info.Type == FILE_TYPE::SAVED_GAME ||
|
|
|
|
info.Type == FILE_TYPE::SCENARIO)
|
|
|
|
{
|
|
|
|
std::unique_ptr<IParkImporter> parkImporter;
|
2017-08-23 08:04:36 +02:00
|
|
|
if (info.Version <= FILE_TYPE_S4_CUTOFF)
|
2017-03-26 22:04:14 +02:00
|
|
|
{
|
2017-08-23 08:04:36 +02:00
|
|
|
// Save is an S4 (RCT1 format)
|
2017-03-26 22:04:14 +02:00
|
|
|
parkImporter.reset(ParkImporter::CreateS4());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-08-23 08:04:36 +02:00
|
|
|
// Save is an S6 (RCT2 format)
|
2017-06-22 19:57:25 +02:00
|
|
|
parkImporter.reset(ParkImporter::CreateS6(_objectRepository, _objectManager));
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
|
|
|
|
2017-09-18 12:55:48 +02:00
|
|
|
auto result = parkImporter->LoadFromStream(stream, info.Type == FILE_TYPE::SCENARIO, false, path.c_str());
|
2017-07-02 20:10:22 +02:00
|
|
|
if (result.Error == PARK_LOAD_ERROR_OK)
|
2017-03-26 22:04:14 +02:00
|
|
|
{
|
2017-07-02 20:10:22 +02:00
|
|
|
parkImporter->Import();
|
|
|
|
game_fix_save_vars();
|
|
|
|
sprite_position_tween_reset();
|
|
|
|
gScreenAge = 0;
|
|
|
|
gLastAutoSaveUpdate = AUTOSAVE_PAUSE;
|
|
|
|
if (info.Type == FILE_TYPE::SAVED_GAME)
|
2017-03-26 22:04:14 +02:00
|
|
|
{
|
2017-08-23 08:04:36 +02:00
|
|
|
if (network_get_mode() == NETWORK_MODE_CLIENT)
|
|
|
|
{
|
|
|
|
network_close();
|
|
|
|
}
|
2017-03-26 22:04:14 +02:00
|
|
|
game_load_init();
|
2017-08-23 08:04:36 +02:00
|
|
|
if (network_get_mode() == NETWORK_MODE_SERVER)
|
|
|
|
{
|
|
|
|
network_send_map();
|
|
|
|
}
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
2017-07-02 20:10:22 +02:00
|
|
|
else
|
2017-03-26 22:04:14 +02:00
|
|
|
{
|
2017-07-02 20:10:22 +02:00
|
|
|
scenario_begin();
|
2017-08-23 08:04:36 +02:00
|
|
|
if (network_get_mode() == NETWORK_MODE_SERVER)
|
|
|
|
{
|
|
|
|
network_send_map();
|
|
|
|
}
|
|
|
|
if (network_get_mode() == NETWORK_MODE_CLIENT)
|
|
|
|
{
|
|
|
|
network_close();
|
|
|
|
}
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
2017-08-23 08:04:36 +02:00
|
|
|
// This ensures that the newly loaded save reflects the user's
|
|
|
|
// 'show real names of guests' option, now that it's a global setting
|
|
|
|
peep_update_names(gConfigGeneral.show_real_names_of_guests);
|
2017-07-02 20:10:22 +02:00
|
|
|
return true;
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-08-23 08:04:36 +02:00
|
|
|
handle_park_load_failure_with_title_opt(&result, path.c_str(), loadTitleScreenFirstOnFail);
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
2017-08-23 08:04:36 +02:00
|
|
|
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Console::Error::WriteLine("Invalid file type.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Console::Error::WriteLine("Unable to detect file type.");
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2017-06-26 23:00:01 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Copy saved games and landscapes to user directory
|
|
|
|
*/
|
|
|
|
void CopyOriginalUserFilesOver()
|
|
|
|
{
|
|
|
|
CopyOriginalUserFilesOver(DIRID::SAVE, "*.sv6");
|
|
|
|
CopyOriginalUserFilesOver(DIRID::LANDSCAPE, "*.sc6");
|
|
|
|
}
|
|
|
|
|
|
|
|
void CopyOriginalUserFilesOver(DIRID dirid, const std::string &pattern)
|
|
|
|
{
|
|
|
|
auto src = _env->GetDirectoryPath(DIRBASE::RCT2, dirid);
|
|
|
|
auto dst = _env->GetDirectoryPath(DIRBASE::USER, dirid);
|
|
|
|
CopyOriginalUserFilesOver(src, dst, pattern);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CopyOriginalUserFilesOver(const std::string &srcRoot, const std::string &dstRoot, const std::string &pattern)
|
|
|
|
{
|
|
|
|
log_verbose("CopyOriginalUserFilesOver('%s', '%s', '%s')", srcRoot.c_str(), dstRoot.c_str(), pattern.c_str());
|
|
|
|
|
|
|
|
auto scanPattern = Path::Combine(srcRoot, pattern);
|
|
|
|
auto scanner = Path::ScanDirectory(scanPattern, true);
|
|
|
|
while (scanner->Next())
|
|
|
|
{
|
|
|
|
auto src = std::string(scanner->GetPath());
|
|
|
|
auto dst = Path::Combine(dstRoot, scanner->GetPathRelative());
|
|
|
|
auto dstDirectory = Path::GetDirectory(dst);
|
|
|
|
|
|
|
|
// Create the directory if necessary
|
|
|
|
if (!platform_directory_exists(dstDirectory.c_str()))
|
|
|
|
{
|
|
|
|
Console::WriteLine("Creating directory '%s'", dstDirectory.c_str());
|
|
|
|
if (!platform_ensure_directory_exists(dstDirectory.c_str()))
|
|
|
|
{
|
|
|
|
Console::Error::WriteLine("Could not create directory %s.", dstDirectory.c_str());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only copy the file if it doesn't already exist
|
|
|
|
if (!File::Exists(dst))
|
|
|
|
{
|
|
|
|
Console::WriteLine("Copying '%s' to '%s'", src.c_str(), dst.c_str());
|
|
|
|
if (!File::Copy(src, dst, false))
|
|
|
|
{
|
|
|
|
Console::Error::WriteLine("Failed to copy '%s' to '%s'", src.c_str(), dst.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delete scanner;
|
|
|
|
}
|
2017-03-25 04:16:40 +01:00
|
|
|
};
|
|
|
|
|
2017-06-04 00:10:20 +02:00
|
|
|
class PlainContext final : public Context
|
|
|
|
{
|
2017-06-11 13:53:37 +02:00
|
|
|
std::unique_ptr<IPlatformEnvironment> _env;
|
|
|
|
std::unique_ptr<IAudioContext> _audioContext;
|
|
|
|
std::unique_ptr<IUiContext> _uiContext;
|
2017-06-04 00:10:20 +02:00
|
|
|
|
|
|
|
public:
|
|
|
|
PlainContext()
|
2017-06-11 13:53:37 +02:00
|
|
|
: PlainContext(CreatePlatformEnvironment(), CreateDummyAudioContext(), CreateDummyUiContext())
|
2017-06-04 00:10:20 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-06-11 13:53:37 +02:00
|
|
|
PlainContext(IPlatformEnvironment * env, IAudioContext * audioContext, IUiContext * uiContext)
|
|
|
|
: Context(env, audioContext, uiContext)
|
2017-06-04 00:10:20 +02:00
|
|
|
{
|
2017-06-11 13:53:37 +02:00
|
|
|
_env = std::unique_ptr<IPlatformEnvironment>(env);
|
2017-06-04 00:10:20 +02:00
|
|
|
_audioContext = std::unique_ptr<IAudioContext>(audioContext);
|
|
|
|
_uiContext = std::unique_ptr<IUiContext>(uiContext);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-03-25 04:16:40 +01:00
|
|
|
Context * Context::Instance = nullptr;
|
|
|
|
|
|
|
|
IContext * CreateContext()
|
2017-03-24 03:47:43 +01:00
|
|
|
{
|
2017-06-04 00:10:20 +02:00
|
|
|
return new PlainContext();
|
2017-03-24 03:47:43 +01:00
|
|
|
}
|
|
|
|
|
2017-06-11 13:53:37 +02:00
|
|
|
IContext * CreateContext(IPlatformEnvironment * env, Audio::IAudioContext * audioContext, IUiContext * uiContext)
|
2017-03-24 17:26:43 +01:00
|
|
|
{
|
2017-06-11 13:53:37 +02:00
|
|
|
return new Context(env, audioContext, uiContext);
|
2017-03-24 17:26:43 +01:00
|
|
|
}
|
|
|
|
|
2017-03-25 04:16:40 +01:00
|
|
|
IContext * GetContext()
|
2017-03-24 03:47:43 +01:00
|
|
|
{
|
2017-03-25 04:16:40 +01:00
|
|
|
return Context::Instance;
|
2017-03-24 03:47:43 +01:00
|
|
|
}
|
|
|
|
}
|
2017-03-25 15:57:02 +01:00
|
|
|
|
|
|
|
extern "C"
|
|
|
|
{
|
2017-09-10 11:02:16 +02:00
|
|
|
void context_init()
|
|
|
|
{
|
|
|
|
GetContext()->GetUiContext()->GetWindowManager()->Init();
|
|
|
|
}
|
|
|
|
|
2017-08-23 08:04:36 +02:00
|
|
|
bool context_load_park_from_file(const utf8 * path)
|
|
|
|
{
|
|
|
|
return GetContext()->LoadParkFromFile(path);
|
|
|
|
}
|
|
|
|
|
2017-03-26 22:04:14 +02:00
|
|
|
void openrct2_write_full_version_info(utf8 * buffer, size_t bufferSize)
|
|
|
|
{
|
2017-05-06 20:17:46 +02:00
|
|
|
String::Set(buffer, bufferSize, gVersionInfoFull);
|
2017-03-26 22:04:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void openrct2_finish()
|
|
|
|
{
|
|
|
|
GetContext()->Finish();
|
|
|
|
}
|
|
|
|
|
2017-03-25 15:57:02 +01:00
|
|
|
void context_setcurrentcursor(sint32 cursor)
|
|
|
|
{
|
|
|
|
GetContext()->GetUiContext()->SetCursor((CURSOR_ID)cursor);
|
|
|
|
}
|
2017-03-25 18:42:14 +01:00
|
|
|
|
|
|
|
void context_hide_cursor()
|
|
|
|
{
|
|
|
|
GetContext()->GetUiContext()->SetCursorVisible(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void context_show_cursor()
|
|
|
|
{
|
|
|
|
GetContext()->GetUiContext()->SetCursorVisible(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void context_get_cursor_position(sint32 * x, sint32 * y)
|
|
|
|
{
|
|
|
|
GetContext()->GetUiContext()->GetCursorPosition(x, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
void context_get_cursor_position_scaled(sint32 * x, sint32 * y)
|
|
|
|
{
|
|
|
|
context_get_cursor_position(x, y);
|
|
|
|
|
|
|
|
// Compensate for window scaling.
|
|
|
|
*x = (sint32)ceilf(*x / gConfigGeneral.window_scale);
|
|
|
|
*y = (sint32)ceilf(*y / gConfigGeneral.window_scale);
|
|
|
|
}
|
|
|
|
|
|
|
|
void context_set_cursor_position(sint32 x, sint32 y)
|
|
|
|
{
|
|
|
|
GetContext()->GetUiContext()->SetCursorPosition(x, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
const CursorState * context_get_cursor_state()
|
|
|
|
{
|
|
|
|
return GetContext()->GetUiContext()->GetCursorState();
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint8 * context_get_keys_state()
|
|
|
|
{
|
|
|
|
return GetContext()->GetUiContext()->GetKeysState();
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint8 * context_get_keys_pressed()
|
|
|
|
{
|
|
|
|
return GetContext()->GetUiContext()->GetKeysPressed();
|
|
|
|
}
|
|
|
|
|
2017-03-26 04:36:22 +02:00
|
|
|
TextInputSession * context_start_text_input(utf8 * buffer, size_t maxLength)
|
2017-03-25 18:42:14 +01:00
|
|
|
{
|
2017-03-26 04:36:22 +02:00
|
|
|
return GetContext()->GetUiContext()->StartTextInput(buffer, maxLength);
|
2017-03-25 18:42:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void context_stop_text_input()
|
|
|
|
{
|
|
|
|
GetContext()->GetUiContext()->StopTextInput();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool context_is_input_active()
|
|
|
|
{
|
|
|
|
return GetContext()->GetUiContext()->IsTextInputActive();
|
|
|
|
}
|
2017-03-26 04:36:22 +02:00
|
|
|
|
|
|
|
void context_trigger_resize()
|
|
|
|
{
|
|
|
|
return GetContext()->GetUiContext()->TriggerResize();
|
|
|
|
}
|
|
|
|
|
|
|
|
void context_set_fullscreen_mode(sint32 mode)
|
|
|
|
{
|
|
|
|
return GetContext()->GetUiContext()->SetFullscreenMode((FULLSCREEN_MODE)mode);
|
|
|
|
}
|
|
|
|
|
2017-07-11 15:56:17 +02:00
|
|
|
void context_recreate_window()
|
|
|
|
{
|
|
|
|
GetContext()->GetUiContext()->RecreateWindow();
|
|
|
|
}
|
|
|
|
|
2017-03-26 04:36:22 +02:00
|
|
|
sint32 context_get_resolutions(Resolution * * outResolutions)
|
|
|
|
{
|
|
|
|
auto resolutions = GetContext()->GetUiContext()->GetFullscreenResolutions();
|
|
|
|
sint32 count = (sint32)resolutions.size();
|
|
|
|
*outResolutions = Memory::AllocateArray<Resolution>(count);
|
|
|
|
Memory::CopyArray(*outResolutions, resolutions.data(), count);
|
|
|
|
return count;
|
|
|
|
}
|
2017-03-26 22:42:07 +02:00
|
|
|
|
|
|
|
sint32 context_get_width()
|
|
|
|
{
|
|
|
|
return GetContext()->GetUiContext()->GetWidth();
|
|
|
|
}
|
|
|
|
|
|
|
|
sint32 context_get_height()
|
|
|
|
{
|
|
|
|
return GetContext()->GetUiContext()->GetHeight();
|
|
|
|
}
|
2017-04-01 14:38:52 +02:00
|
|
|
|
|
|
|
bool context_has_focus()
|
|
|
|
{
|
|
|
|
return GetContext()->GetUiContext()->HasFocus();
|
|
|
|
}
|
|
|
|
|
|
|
|
void context_set_cursor_trap(bool value)
|
|
|
|
{
|
|
|
|
GetContext()->GetUiContext()->SetCursorTrap(value);
|
|
|
|
}
|
2017-04-01 18:27:39 +02:00
|
|
|
|
2017-06-11 16:16:47 +02:00
|
|
|
rct_window * context_open_window(rct_windowclass wc)
|
|
|
|
{
|
|
|
|
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
|
|
|
|
return windowManager->OpenWindow(wc);
|
|
|
|
}
|
|
|
|
|
2017-09-05 20:55:18 +02:00
|
|
|
rct_window * context_open_window_view(rct_windowclass wc)
|
|
|
|
{
|
|
|
|
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
|
|
|
|
return windowManager->OpenView(wc);
|
|
|
|
}
|
|
|
|
|
2017-09-06 15:09:18 +02:00
|
|
|
rct_window * context_open_detail_window(uint8 type, sint32 id)
|
|
|
|
{
|
|
|
|
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
|
|
|
|
return windowManager->OpenDetails(type, id);
|
|
|
|
}
|
|
|
|
|
2017-09-10 11:02:16 +02:00
|
|
|
rct_window * context_open_intent(Intent * intent)
|
|
|
|
{
|
|
|
|
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
|
|
|
|
return windowManager->OpenIntent(intent);
|
|
|
|
}
|
|
|
|
|
2017-10-06 23:03:48 +02:00
|
|
|
void context_broadcast_intent(Intent * intent)
|
|
|
|
{
|
|
|
|
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
|
|
|
|
windowManager->BroadcastIntent(intent);
|
|
|
|
}
|
|
|
|
|
2017-09-06 16:09:35 +02:00
|
|
|
rct_window * context_show_error(rct_string_id title, rct_string_id message)
|
|
|
|
{
|
|
|
|
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
|
|
|
|
return windowManager->ShowError(title, message);
|
|
|
|
}
|
|
|
|
|
2017-06-11 20:04:50 +02:00
|
|
|
void context_input_handle_keyboard(bool isTitle)
|
2017-06-11 17:31:54 +02:00
|
|
|
{
|
|
|
|
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
|
2017-06-11 20:04:50 +02:00
|
|
|
windowManager->HandleKeyboard(isTitle);
|
2017-06-11 17:31:54 +02:00
|
|
|
}
|
|
|
|
|
2017-06-14 20:47:22 +02:00
|
|
|
bool context_read_bmp(void * * outPixels, uint32 * outWidth, uint32 * outHeight, const utf8 * path)
|
|
|
|
{
|
|
|
|
return GetContext()->GetUiContext()->ReadBMP(outPixels, outWidth, outHeight, std::string(path));
|
|
|
|
}
|
|
|
|
|
2017-07-28 19:43:48 +02:00
|
|
|
void context_quit()
|
|
|
|
{
|
|
|
|
GetContext()->Quit();
|
|
|
|
}
|
|
|
|
|
2017-07-28 20:11:12 +02:00
|
|
|
const utf8 * context_get_path_legacy(sint32 pathId)
|
|
|
|
{
|
|
|
|
static utf8 result[MAX_PATH];
|
|
|
|
auto path = GetContext()->GetPathLegacy(pathId);
|
|
|
|
String::Set(result, sizeof(result), path.c_str());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-04-01 18:27:39 +02:00
|
|
|
bool platform_open_common_file_dialog(utf8 * outFilename, file_dialog_desc * desc, size_t outSize)
|
|
|
|
{
|
2017-06-03 23:13:20 +02:00
|
|
|
try
|
2017-04-01 18:27:39 +02:00
|
|
|
{
|
2017-06-03 23:13:20 +02:00
|
|
|
FileDialogDesc desc2;
|
|
|
|
desc2.Type = (FILE_DIALOG_TYPE)desc->type;
|
|
|
|
desc2.Title = String::ToStd(desc->title);
|
|
|
|
desc2.InitialDirectory = String::ToStd(desc->initial_directory);
|
|
|
|
desc2.DefaultFilename = String::ToStd(desc->default_filename);
|
|
|
|
for (const auto &filter : desc->filters)
|
2017-04-01 18:27:39 +02:00
|
|
|
{
|
2017-06-03 23:13:20 +02:00
|
|
|
if (filter.name != nullptr)
|
|
|
|
{
|
|
|
|
desc2.Filters.push_back({ String::ToStd(filter.name), String::ToStd(filter.pattern) });
|
|
|
|
}
|
2017-04-01 18:27:39 +02:00
|
|
|
}
|
2017-06-03 23:13:20 +02:00
|
|
|
std::string result = GetContext()->GetUiContext()->ShowFileDialog(desc2);
|
|
|
|
String::Set(outFilename, outSize, result.c_str());
|
|
|
|
return !result.empty();
|
|
|
|
}
|
|
|
|
catch (const std::exception &ex)
|
|
|
|
{
|
|
|
|
log_error(ex.what());
|
|
|
|
outFilename[0] = '\0';
|
|
|
|
return false;
|
2017-04-01 18:27:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
utf8 * platform_open_directory_browser(const utf8 * title)
|
|
|
|
{
|
2017-06-03 23:13:20 +02:00
|
|
|
try
|
|
|
|
{
|
|
|
|
std::string result = GetContext()->GetUiContext()->ShowDirectoryDialog(title);
|
|
|
|
return String::Duplicate(result.c_str());
|
|
|
|
}
|
|
|
|
catch (const std::exception &ex)
|
|
|
|
{
|
|
|
|
log_error(ex.what());
|
|
|
|
return nullptr;
|
|
|
|
}
|
2017-04-01 18:27:39 +02:00
|
|
|
}
|
2017-06-06 15:34:30 +02:00
|
|
|
|
|
|
|
bool platform_place_string_on_clipboard(utf8* target)
|
|
|
|
{
|
|
|
|
return GetContext()->GetUiContext()->SetClipboardText(target);
|
|
|
|
}
|
2017-03-25 15:57:02 +01:00
|
|
|
}
|