diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 2d4bd86de6..807a3b4217 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -31,7 +31,8 @@ "includePath": [ "/usr/include", "/usr/local/include", - "${workspaceRoot}" + "${workspaceRoot}", + "${workspaceRoot}/src" ], "defines": [], "intelliSenseMode": "clang-x64", diff --git a/OpenRCT2.xcodeproj/project.pbxproj b/OpenRCT2.xcodeproj/project.pbxproj index fffaab12c2..499268585b 100644 --- a/OpenRCT2.xcodeproj/project.pbxproj +++ b/OpenRCT2.xcodeproj/project.pbxproj @@ -42,6 +42,10 @@ 9346F9DB208A191900C77D91 /* GuestPathfinding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9346F9D7208A191900C77D91 /* GuestPathfinding.cpp */; }; 9346F9DC208A191900C77D91 /* GuestPathfinding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9346F9D7208A191900C77D91 /* GuestPathfinding.cpp */; }; 9346F9DD208A191900C77D91 /* GuestPathfinding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9346F9D7208A191900C77D91 /* GuestPathfinding.cpp */; }; + 933F2CB720935653001B33FD /* LocalisationService.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 933F2CB620935653001B33FD /* LocalisationService.cpp */; }; + 933F2CB820935653001B33FD /* LocalisationService.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 933F2CB620935653001B33FD /* LocalisationService.cpp */; }; + 933F2CB920935653001B33FD /* LocalisationService.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 933F2CB620935653001B33FD /* LocalisationService.cpp */; }; + 933F2CBB20935668001B33FD /* LocalisationService.h in Headers */ = {isa = PBXBuildFile; fileRef = 933F2CBA20935668001B33FD /* LocalisationService.h */; }; C61ADB1F1FB6A0A70024F2EF /* TopToolbar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C61ADB1E1FB6A0A60024F2EF /* TopToolbar.cpp */; }; C61ADB211FB7DC060024F2EF /* Scenery.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C61ADB201FB7DC060024F2EF /* Scenery.cpp */; }; C61ADB231FBBCB8B0024F2EF /* GameBottomToolbar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C61ADB221FBBCB8A0024F2EF /* GameBottomToolbar.cpp */; }; @@ -645,7 +649,6 @@ 4C7B53BA1FFF935B00A52E21 /* user.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = user.h; sourceTree = ""; }; 4C7B53BB1FFF935B00A52E21 /* UTF8.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UTF8.cpp; sourceTree = ""; }; 4C7B53C61FFF94F900A52E21 /* ConversionTables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConversionTables.cpp; sourceTree = ""; }; - 4C7B53C71FFF94F900A52E21 /* ConversionTables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConversionTables.h; sourceTree = ""; }; 4C7B53C91FFF991000A52E21 /* Language.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Language.h; sourceTree = ""; }; 4C7B53CB1FFF995100A52E21 /* Font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Font.h; sourceTree = ""; }; 4C7B53CD200029CE00A52E21 /* Line.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Line.cpp; sourceTree = ""; }; @@ -864,6 +867,8 @@ 9308D9FD209908090079EE96 /* Surface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Surface.h; sourceTree = ""; }; 9346F9D6208A191900C77D91 /* Guest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Guest.cpp; sourceTree = ""; }; 9346F9D7208A191900C77D91 /* GuestPathfinding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GuestPathfinding.cpp; sourceTree = ""; }; + 933F2CB620935653001B33FD /* LocalisationService.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LocalisationService.cpp; sourceTree = ""; }; + 933F2CBA20935668001B33FD /* LocalisationService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocalisationService.h; sourceTree = ""; }; C61ADB1E1FB6A0A60024F2EF /* TopToolbar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TopToolbar.cpp; sourceTree = ""; }; C61ADB201FB7DC060024F2EF /* Scenery.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Scenery.cpp; sourceTree = ""; }; C61ADB221FBBCB8A0024F2EF /* GameBottomToolbar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GameBottomToolbar.cpp; sourceTree = ""; }; @@ -1991,7 +1996,6 @@ isa = PBXGroup; children = ( 4C7B53C61FFF94F900A52E21 /* ConversionTables.cpp */, - 4C7B53C71FFF94F900A52E21 /* ConversionTables.h */, 4C7B53AA1FFF935B00A52E21 /* Convert.cpp */, 4C7B53AB1FFF935B00A52E21 /* Currency.cpp */, 4C7B53AC1FFF935B00A52E21 /* Currency.h */, @@ -2001,6 +2005,8 @@ 4C7B53B01FFF935B00A52E21 /* FormatCodes.h */, 4C7B53B11FFF935B00A52E21 /* Language.cpp */, 4C7B53C91FFF991000A52E21 /* Language.h */, + 933F2CB620935653001B33FD /* LocalisationService.cpp */, + 933F2CBA20935668001B33FD /* LocalisationService.h */, 4C7B53B31FFF935B00A52E21 /* LanguagePack.cpp */, 4C7B53B41FFF935B00A52E21 /* LanguagePack.h */, 4C7B53B51FFF935B00A52E21 /* Localisation.cpp */, @@ -2716,6 +2722,7 @@ C6352B851F477022006CCEE3 /* DataSerialiserTraits.h in Headers */, C67B28192002D7F200109C93 /* Window_internal.h in Headers */, C6352B971F477032006CCEE3 /* SetParkEntranceFeeAction.hpp in Headers */, + 933F2CBB20935668001B33FD /* LocalisationService.h in Headers */, C6352B861F477022006CCEE3 /* Endianness.h in Headers */, C6352B941F477032006CCEE3 /* PlaceParkEntranceAction.hpp in Headers */, C6352B911F477032006CCEE3 /* GameAction.h in Headers */, @@ -3021,6 +3028,7 @@ C685E5191F8907850090598F /* NewRide.cpp in Sources */, 9346F9DB208A191900C77D91 /* GuestPathfinding.cpp in Sources */, C654DF361F69C0430040F43D /* Player.cpp in Sources */, + 933F2CB720935653001B33FD /* LocalisationService.cpp in Sources */, F76C88791EC5324E00FA49E2 /* AudioContext.cpp in Sources */, C666EE7A1F37ACB10061AA04 /* Themes.cpp in Sources */, C666EE7F1F37ACB10061AA04 /* Viewport.cpp in Sources */, @@ -3174,6 +3182,7 @@ F76C85C01EC4E88300FA49E2 /* UriHandler.cpp in Sources */, C688786220289A0A0084B384 /* MapGen.cpp in Sources */, C68878A820289B2A0084B384 /* NewsItem.cpp in Sources */, + 933F2CB820935653001B33FD /* LocalisationService.cpp in Sources */, F76C85C41EC4E88300FA49E2 /* Config.cpp in Sources */, C688789120289B140084B384 /* Console.cpp in Sources */, C688792920289B9B0084B384 /* Chairlift.cpp in Sources */, @@ -3436,6 +3445,7 @@ 9346F9DA208A191900C77D91 /* Guest.cpp in Sources */, F7D7749E1EC6713200BE6EBC /* Cli.cpp in Sources */, 9308DA03209908090079EE96 /* Surface.cpp in Sources */, + 933F2CB920935653001B33FD /* LocalisationService.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/openrct2.proj b/openrct2.proj index d873e37bb0..c116f428b9 100644 --- a/openrct2.proj +++ b/openrct2.proj @@ -62,10 +62,10 @@ $(RootDir).dependencies - https://github.com/OpenRCT2/Dependencies/releases/download/v16/openrct2-libs-v16-x86-windows-static-winssl.zip - 827d86c1571bb73504b23929746b20d8094aae40 - https://github.com/OpenRCT2/Dependencies/releases/download/v16/openrct2-libs-v16-x64-windows-static-winssl.zip - 5904b7e0562ace419b7c87d618e5ad00ba664b36 + https://github.com/OpenRCT2/Dependencies/releases/download/v17/openrct2-libs-v17-x86-windows-static-winssl.zip + 4f9fb5feebe4a4ed461b2a545b2fd1c91ef6527b + https://github.com/OpenRCT2/Dependencies/releases/download/v17/openrct2-libs-v17-x64-windows-static-winssl.zip + 5aab386f9eeaad102b330fcddea2b4cc9e3c7067 1.8.0 https://github.com/google/googletest/archive/release-1.8.0.zip 667f873ab7a4d246062565fad32fb6d8e203ee73 diff --git a/scripts/linux/install.sh b/scripts/linux/install.sh index f5ec2f2314..13c8aa2f9c 100755 --- a/scripts/linux/install.sh +++ b/scripts/linux/install.sh @@ -99,7 +99,7 @@ if [[ "$(uname)" == "Darwin" ]]; then elif [[ $(uname) == "Linux" ]]; then # Clone discord-rpc for Discord's Rich Presence support # Use tagged release to prevent upstream changes from breaking our code - git clone https://github.com/discordapp/discord-rpc -b v3.2.0 + git clone https://github.com/IntelOrca/discord-rpc -b fix/134-iothreadholder # Use rapidjson with a hack for GCC 8, while awaiting a fix upstream: # https://github.com/Tencent/rapidjson/issues/1205 git clone https://github.com/janisozaur/rapidjson discord-rpc/thirdparty/rapidjson -b patch-1 diff --git a/src/openrct2-cli/Cli.cpp b/src/openrct2-cli/Cli.cpp index e3f354a71c..07d0a7444e 100644 --- a/src/openrct2-cli/Cli.cpp +++ b/src/openrct2-cli/Cli.cpp @@ -36,7 +36,6 @@ int main(int argc, const char * * argv) // Run OpenRCT2 with a plain context auto context = CreateContext(); context->RunOpenRCT2(argc, argv); - delete context; } return gExitCode; } diff --git a/src/openrct2-ui/Ui.cpp b/src/openrct2-ui/Ui.cpp index b94c3e6720..7a6b834e2e 100644 --- a/src/openrct2-ui/Ui.cpp +++ b/src/openrct2-ui/Ui.cpp @@ -29,6 +29,12 @@ using namespace OpenRCT2; using namespace OpenRCT2::Audio; using namespace OpenRCT2::Ui; +template +static std::shared_ptr to_shared(std::unique_ptr&& src) +{ + return std::shared_ptr(std::move(src)); +} + /** * Main entry point for non-Windows systems. Windows instead uses its own DLL proxy. */ @@ -47,21 +53,16 @@ int main(int argc, const char * * argv) // Run OpenRCT2 with a plain context auto context = CreateContext(); context->RunOpenRCT2(argc, argv); - delete context; } else { // Run OpenRCT2 with a UI context - auto env = CreatePlatformEnvironment(); - auto audioContext = CreateAudioContext(); - auto uiContext = CreateUiContext(env); + auto env = to_shared(CreatePlatformEnvironment()); + auto audioContext = to_shared(CreateAudioContext()); + auto uiContext = to_shared(CreateUiContext(env)); auto context = CreateContext(env, audioContext, uiContext); context->RunOpenRCT2(argc, argv); - - delete context; - delete uiContext; - delete audioContext; } } return gExitCode; diff --git a/src/openrct2-ui/UiContext.Linux.cpp b/src/openrct2-ui/UiContext.Linux.cpp index 8d9fa7cd1a..5aba86cebc 100644 --- a/src/openrct2-ui/UiContext.Linux.cpp +++ b/src/openrct2-ui/UiContext.Linux.cpp @@ -374,7 +374,7 @@ namespace OpenRCT2::Ui static void ThrowMissingDialogApp() { - IUiContext * uiContext = GetContext()->GetUiContext(); + auto uiContext = GetContext()->GetUiContext(); std::string dialogMissingWarning = language_get_string(STR_MISSING_DIALOG_APPLICATION_ERROR); uiContext->ShowMessageBox(dialogMissingWarning); throw std::runtime_error(dialogMissingWarning); diff --git a/src/openrct2-ui/UiContext.cpp b/src/openrct2-ui/UiContext.cpp index e652f041de..7696ca05cb 100644 --- a/src/openrct2-ui/UiContext.cpp +++ b/src/openrct2-ui/UiContext.cpp @@ -34,7 +34,7 @@ #include #include #include "CursorRepository.h" -#include "drawing/engines/DrawingEngines.h" +#include "drawing/engines/DrawingEngineFactory.hpp" #include "input/KeyboardShortcuts.h" #include "SDLException.h" #include "TextComposition.h" @@ -94,7 +94,7 @@ private: public: InGameConsole& GetInGameConsole() { return _inGameConsole; } - explicit UiContext(IPlatformEnvironment * env) + explicit UiContext(const std::shared_ptr& env) : _platformUiContext(CreatePlatformUiContext()), _windowManager(CreateWindowManager()), _keyboardShortcuts(env) @@ -261,20 +261,9 @@ public: } // Drawing - IDrawingEngine * CreateDrawingEngine(DRAWING_ENGINE_TYPE type) override + std::shared_ptr GetDrawingEngineFactory() override { - switch ((sint32)type) { - case DRAWING_ENGINE_SOFTWARE: - return CreateSoftwareDrawingEngine(this); - case DRAWING_ENGINE_SOFTWARE_WITH_HARDWARE_DISPLAY: - return CreateHardwareDisplayDrawingEngine(this); -#ifndef DISABLE_OPENGL - case DRAWING_ENGINE_OPENGL: - return CreateOpenGLDrawingEngine(this); -#endif - default: - return nullptr; - } + return std::make_shared(); } // Text input @@ -814,13 +803,13 @@ private: } }; -IUiContext * OpenRCT2::Ui::CreateUiContext(IPlatformEnvironment * env) +std::unique_ptr OpenRCT2::Ui::CreateUiContext(const std::shared_ptr& env) { - return new UiContext(env); + return std::make_unique(env); } InGameConsole& OpenRCT2::Ui::GetInGameConsole() { - auto uiContext = static_cast(GetContext()->GetUiContext()); + auto uiContext = std::static_pointer_cast(GetContext()->GetUiContext()); return uiContext->GetInGameConsole(); } diff --git a/src/openrct2-ui/UiContext.h b/src/openrct2-ui/UiContext.h index 0f879e0e8a..4aafb72ef7 100644 --- a/src/openrct2-ui/UiContext.h +++ b/src/openrct2-ui/UiContext.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include @@ -43,7 +44,7 @@ namespace OpenRCT2 virtual std::string ShowDirectoryDialog(SDL_Window * window, const std::string &title) abstract; }; - IUiContext * CreateUiContext(IPlatformEnvironment * env); + std::unique_ptr CreateUiContext(const std::shared_ptr& env); IPlatformUiContext * CreatePlatformUiContext(); InGameConsole& GetInGameConsole(); diff --git a/src/openrct2-ui/audio/AudioContext.cpp b/src/openrct2-ui/audio/AudioContext.cpp index f5c0616a2a..d66b5bda7d 100644 --- a/src/openrct2-ui/audio/AudioContext.cpp +++ b/src/openrct2-ui/audio/AudioContext.cpp @@ -94,8 +94,8 @@ namespace OpenRCT2::Audio void StopVehicleSounds() override { } }; - IAudioContext * CreateAudioContext() + std::unique_ptr CreateAudioContext() { - return new AudioContext(); + return std::make_unique(); } } // namespace OpenRCT2::Audio diff --git a/src/openrct2-ui/audio/AudioContext.h b/src/openrct2-ui/audio/AudioContext.h index 3a04fe6381..b2a9f62ff5 100644 --- a/src/openrct2-ui/audio/AudioContext.h +++ b/src/openrct2-ui/audio/AudioContext.h @@ -1,6 +1,7 @@ #pragma once +#include #include #include #include @@ -69,5 +70,6 @@ namespace OpenRCT2::Audio IAudioMixer * Create(); } - IAudioContext * CreateAudioContext(); + std::unique_ptr CreateAudioContext(); + } // namespace OpenRCT2::Audio diff --git a/src/openrct2-ui/drawing/engines/DrawingEngineFactory.hpp b/src/openrct2-ui/drawing/engines/DrawingEngineFactory.hpp new file mode 100644 index 0000000000..bcdb26fd07 --- /dev/null +++ b/src/openrct2-ui/drawing/engines/DrawingEngineFactory.hpp @@ -0,0 +1,57 @@ +#pragma region Copyright (c) 2014-2017 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 once + +#include +#include +#include + +namespace OpenRCT2 +{ + namespace Ui + { + using namespace OpenRCT2::Drawing; + + interface IUiContext; + + std::unique_ptr CreateSoftwareDrawingEngine(const std::shared_ptr& uiContext); + std::unique_ptr CreateHardwareDisplayDrawingEngine(const std::shared_ptr& uiContext); +#ifndef DISABLE_OPENGL + std::unique_ptr CreateOpenGLDrawingEngine(const std::shared_ptr& uiContext); +#endif + + class DrawingEngineFactory final : public IDrawingEngineFactory + { + public: + std::unique_ptr Create(DRAWING_ENGINE_TYPE type, const std::shared_ptr& uiContext) override + { + switch ((sint32)type) { + case DRAWING_ENGINE_SOFTWARE: + return CreateSoftwareDrawingEngine(uiContext); + case DRAWING_ENGINE_SOFTWARE_WITH_HARDWARE_DISPLAY: + return CreateHardwareDisplayDrawingEngine(uiContext); +#ifndef DISABLE_OPENGL + case DRAWING_ENGINE_OPENGL: + return CreateOpenGLDrawingEngine(uiContext); +#endif + default: + return nullptr; + } + } + }; + } +} diff --git a/src/openrct2-ui/drawing/engines/DrawingEngines.h b/src/openrct2-ui/drawing/engines/DrawingEngines.h deleted file mode 100644 index 00b36cd355..0000000000 --- a/src/openrct2-ui/drawing/engines/DrawingEngines.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma region Copyright (c) 2014-2017 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 once - -#include - -namespace OpenRCT2 -{ - namespace Drawing - { - interface IDrawingEngine; - } - - namespace Ui - { - interface IUiContext; - - Drawing::IDrawingEngine * CreateSoftwareDrawingEngine(IUiContext * uiContext); - Drawing::IDrawingEngine * CreateHardwareDisplayDrawingEngine(IUiContext * uiContext); -#ifndef DISABLE_OPENGL - Drawing::IDrawingEngine * CreateOpenGLDrawingEngine(IUiContext * uiContext); -#endif - } -} diff --git a/src/openrct2-ui/drawing/engines/HardwareDisplayDrawingEngine.cpp b/src/openrct2-ui/drawing/engines/HardwareDisplayDrawingEngine.cpp index 6c2bcaab81..b999d41ce1 100644 --- a/src/openrct2-ui/drawing/engines/HardwareDisplayDrawingEngine.cpp +++ b/src/openrct2-ui/drawing/engines/HardwareDisplayDrawingEngine.cpp @@ -22,7 +22,7 @@ #include #include #include -#include "DrawingEngines.h" +#include "DrawingEngineFactory.hpp" #include #include @@ -37,7 +37,7 @@ class HardwareDisplayDrawingEngine final : public X8DrawingEngine private: constexpr static uint32 DIRTY_VISUAL_TIME = 32; - IUiContext * const _uiContext; + std::shared_ptr const _uiContext; SDL_Window * _window = nullptr; SDL_Renderer * _sdlRenderer = nullptr; SDL_Texture * _screenTexture = nullptr; @@ -60,7 +60,7 @@ private: bool smoothNN = false; public: - explicit HardwareDisplayDrawingEngine(IUiContext * uiContext) + explicit HardwareDisplayDrawingEngine(const std::shared_ptr& uiContext) : X8DrawingEngine(uiContext), _uiContext(uiContext) { @@ -415,7 +415,7 @@ private: } }; -IDrawingEngine * OpenRCT2::Ui::CreateHardwareDisplayDrawingEngine(IUiContext * uiContext) +std::unique_ptr OpenRCT2::Ui::CreateHardwareDisplayDrawingEngine(const std::shared_ptr& uiContext) { - return new HardwareDisplayDrawingEngine(uiContext); + return std::make_unique(uiContext); } diff --git a/src/openrct2-ui/drawing/engines/SoftwareDrawingEngine.cpp b/src/openrct2-ui/drawing/engines/SoftwareDrawingEngine.cpp index 77fdbb4f7a..50c41c2f58 100644 --- a/src/openrct2-ui/drawing/engines/SoftwareDrawingEngine.cpp +++ b/src/openrct2-ui/drawing/engines/SoftwareDrawingEngine.cpp @@ -23,7 +23,7 @@ #include #include #include -#include "DrawingEngines.h" +#include "DrawingEngineFactory.hpp" using namespace OpenRCT2; using namespace OpenRCT2::Drawing; @@ -32,14 +32,14 @@ using namespace OpenRCT2::Ui; class SoftwareDrawingEngine final : public X8DrawingEngine { private: - IUiContext * const _uiContext; + std::shared_ptr const _uiContext; SDL_Window * _window = nullptr; SDL_Surface * _surface = nullptr; SDL_Surface * _RGBASurface = nullptr; SDL_Palette * _palette = nullptr; public: - explicit SoftwareDrawingEngine(IUiContext * uiContext) + explicit SoftwareDrawingEngine(const std::shared_ptr& uiContext) : X8DrawingEngine(uiContext), _uiContext(uiContext) { @@ -163,7 +163,7 @@ private: } }; -IDrawingEngine * OpenRCT2::Ui::CreateSoftwareDrawingEngine(IUiContext * uiContext) +std::unique_ptr OpenRCT2::Ui::CreateSoftwareDrawingEngine(const std::shared_ptr& uiContext) { - return new SoftwareDrawingEngine(uiContext); + return std::make_unique(uiContext); } diff --git a/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp b/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp index 7976a47812..a9929c0206 100644 --- a/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp +++ b/src/openrct2-ui/drawing/engines/opengl/OpenGLDrawingEngine.cpp @@ -31,7 +31,7 @@ #include #include #include -#include "../DrawingEngines.h" +#include "../DrawingEngineFactory.hpp" #include "ApplyPaletteShader.h" #include "DrawCommands.h" #include "DrawLineShader.h" @@ -121,7 +121,7 @@ public: class OpenGLDrawingEngine : public IDrawingEngine { private: - IUiContext * const _uiContext = nullptr; + std::shared_ptr const _uiContext; SDL_Window * _window = nullptr; SDL_GLContext _context = nullptr; @@ -144,7 +144,7 @@ public: SDL_Color Palette[256]; vec4 GLPalette[256]; - explicit OpenGLDrawingEngine(IUiContext * uiContext) + explicit OpenGLDrawingEngine(const std::shared_ptr& uiContext) : _uiContext(uiContext) { _window = (SDL_Window *)_uiContext->GetWindow(); @@ -419,9 +419,9 @@ private: } }; -IDrawingEngine * OpenRCT2::Ui::CreateOpenGLDrawingEngine(IUiContext * uiContext) +std::unique_ptr OpenRCT2::Ui::CreateOpenGLDrawingEngine(const std::shared_ptr& uiContext) { - return new OpenGLDrawingEngine(uiContext); + return std::make_unique(uiContext); } OpenGLDrawingContext::OpenGLDrawingContext(OpenGLDrawingEngine * engine) diff --git a/src/openrct2-ui/input/KeyboardShortcuts.cpp b/src/openrct2-ui/input/KeyboardShortcuts.cpp index e111c7b878..dda93d267e 100644 --- a/src/openrct2-ui/input/KeyboardShortcuts.cpp +++ b/src/openrct2-ui/input/KeyboardShortcuts.cpp @@ -32,12 +32,17 @@ using namespace OpenRCT2::Input; // Remove when the C calls are removed static KeyboardShortcuts * _instance; -KeyboardShortcuts::KeyboardShortcuts(IPlatformEnvironment * env) +KeyboardShortcuts::KeyboardShortcuts(const std::shared_ptr& env) : _env(env) { _instance = this; } +KeyboardShortcuts::~KeyboardShortcuts() +{ + _instance = nullptr; +} + void KeyboardShortcuts::Reset() { for (size_t i = 0; i < SHORTCUT_COUNT; i++) diff --git a/src/openrct2-ui/input/KeyboardShortcuts.h b/src/openrct2-ui/input/KeyboardShortcuts.h index 28c9482bf3..205bcdee45 100644 --- a/src/openrct2-ui/input/KeyboardShortcuts.h +++ b/src/openrct2-ui/input/KeyboardShortcuts.h @@ -16,6 +16,7 @@ #pragma once +#include #include #define SHIFT 0x100 @@ -119,11 +120,12 @@ namespace OpenRCT2 constexpr static sint32 CURRENT_FILE_VERSION = 1; static const uint16 DefaultKeys[SHORTCUT_COUNT]; - IPlatformEnvironment * const _env; + std::shared_ptr const _env; uint16 _keys[SHORTCUT_COUNT]; public: - KeyboardShortcuts(IPlatformEnvironment * env); + KeyboardShortcuts(const std::shared_ptr& env); + ~KeyboardShortcuts(); void Reset(); bool Load(); diff --git a/src/openrct2-ui/interface/InGameConsole.cpp b/src/openrct2-ui/interface/InGameConsole.cpp index d4eb7bf1a6..f1415ba5ab 100644 --- a/src/openrct2-ui/interface/InGameConsole.cpp +++ b/src/openrct2-ui/interface/InGameConsole.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "InGameConsole.h" @@ -256,7 +257,7 @@ void InGameConsole::Draw(rct_drawpixelinfo * dpi) const } // TTF looks far better without the outlines - if (!gUseTrueTypeFont) + if (!LocalisationService_UseTrueTypeFont()) { textColour |= COLOUR_FLAG_OUTLINE; } diff --git a/src/openrct2-ui/windows/About.cpp b/src/openrct2-ui/windows/About.cpp index f8af10b775..8f70adf07c 100644 --- a/src/openrct2-ui/windows/About.cpp +++ b/src/openrct2-ui/windows/About.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #define WW 400 @@ -249,7 +250,7 @@ static void window_about_openrct2_paint(rct_window *w, rct_drawpixelinfo *dpi) // Copyright disclaimer; hidden when using truetype fonts to prevent // the text from overlapping the changelog button. - if (!gUseTrueTypeFont) + if (!LocalisationService_UseTrueTypeFont()) { gfx_draw_string_centred_wrapped(dpi, nullptr, x, y, width, STR_ABOUT_OPENRCT2_DESCRIPTION_3, w->colours[2]); } diff --git a/src/openrct2-ui/windows/Options.cpp b/src/openrct2-ui/windows/Options.cpp index a65dc60646..96eed6565c 100644 --- a/src/openrct2-ui/windows/Options.cpp +++ b/src/openrct2-ui/windows/Options.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -1150,7 +1151,7 @@ static void window_options_mousedown(rct_window *w, rct_widgetindex widgetIndex, gDropdownItemsArgs[i - 1] = (uintptr_t)LanguagesDescriptors[i].native_name; } window_options_show_dropdown(w, widget, LANGUAGE_COUNT - 1); - dropdown_set_checked(gCurrentLanguage - 1, true); + dropdown_set_checked(LocalisationService_GetCurrentLanguage() - 1, true); break; case WIDX_DATE_FORMAT_DROPDOWN: for (size_t i = 0; i < 4; i++) { @@ -1389,8 +1390,8 @@ static void window_options_dropdown(rct_window *w, rct_widgetindex widgetIndex, break; case WIDX_LANGUAGE_DROPDOWN: { - sint32 fallbackLanguage = gCurrentLanguage; - if (dropdownIndex != gCurrentLanguage - 1) { + auto fallbackLanguage = LocalisationService_GetCurrentLanguage(); + if (dropdownIndex != LocalisationService_GetCurrentLanguage() - 1) { if (!language_open(dropdownIndex + 1)) { // Failed to open language file, try to recover by falling @@ -1635,7 +1636,7 @@ static void window_options_invalidate(rct_window *w) case WINDOW_OPTIONS_PAGE_CULTURE: // Language - set_format_arg(0, char*, LanguagesDescriptors[gCurrentLanguage].native_name); + set_format_arg(0, char*, LanguagesDescriptors[LocalisationService_GetCurrentLanguage()].native_name); // Currency: pounds, dollars, etc. (10 total) window_options_culture_widgets[WIDX_CURRENCY].text = CurrencyDescriptors[gConfigGeneral.currency_format].stringId; diff --git a/src/openrct2-ui/windows/TitleScenarioSelect.cpp b/src/openrct2-ui/windows/TitleScenarioSelect.cpp index d03991f4fc..5e5e9eb95a 100644 --- a/src/openrct2-ui/windows/TitleScenarioSelect.cpp +++ b/src/openrct2-ui/windows/TitleScenarioSelect.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -282,7 +283,7 @@ static void window_scenarioselect_mousedown(rct_window *w, rct_widgetindex widge static sint32 get_scenario_list_item_size() { - if (!gUseTrueTypeFont) + if (!LocalisationService_UseTrueTypeFont()) return 24; // Scenario title diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index c49da24781..f2cb2d08b9 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -64,7 +64,7 @@ #include "interface/Viewport.h" #include "Intro.h" #include "localisation/Date.h" -#include "localisation/Language.h" +#include "localisation/LocalisationService.h" #include "network/DiscordService.h" #include "network/http.h" #include "network/network.h" @@ -74,6 +74,7 @@ using namespace OpenRCT2; using namespace OpenRCT2::Audio; +using namespace OpenRCT2::Localisation; using namespace OpenRCT2::Ui; namespace OpenRCT2 @@ -82,17 +83,18 @@ namespace OpenRCT2 { private: // Dependencies - IPlatformEnvironment * const _env = nullptr; - IAudioContext * const _audioContext = nullptr; - IUiContext * const _uiContext = nullptr; + std::shared_ptr const _env; + std::shared_ptr const _audioContext; + std::shared_ptr const _uiContext; // Services + std::shared_ptr _localisationService; IObjectRepository * _objectRepository = nullptr; IObjectManager * _objectManager = nullptr; ITrackDesignRepository * _trackDesignRepository = nullptr; IScenarioRepository * _scenarioRepository = nullptr; #ifdef __ENABLE_DISCORD__ - DiscordService * _discordService = nullptr; + std::unique_ptr _discordService; #endif StdInOutConsole _stdInOutConsole; @@ -116,10 +118,14 @@ namespace OpenRCT2 static Context * Instance; public: - Context(IPlatformEnvironment * env, IAudioContext * audioContext, IUiContext * uiContext) + Context( + const std::shared_ptr& env, + const std::shared_ptr& audioContext, + const std::shared_ptr& uiContext) : _env(env), _audioContext(audioContext), - _uiContext(uiContext) + _uiContext(uiContext), + _localisationService(std::make_shared(env)) { Instance = this; } @@ -128,7 +134,6 @@ namespace OpenRCT2 { window_close_all(); http_dispose(); - language_close_all(); object_manager_unload_all_objects(); gfx_object_check_all_images_freed(); gfx_unload_g2(); @@ -140,9 +145,6 @@ namespace OpenRCT2 delete _titleScreen; -#ifdef __ENABLE_DISCORD__ - delete _discordService; -#endif delete _scenarioRepository; delete _trackDesignRepository; delete _objectManager; @@ -151,21 +153,26 @@ namespace OpenRCT2 Instance = nullptr; } - IAudioContext * GetAudioContext() override + std::shared_ptr GetAudioContext() override { return _audioContext; } - IUiContext * GetUiContext() override + std::shared_ptr GetUiContext() override { return _uiContext; } - IPlatformEnvironment * GetPlatformEnvironment() override + std::shared_ptr GetPlatformEnvironment() override { return _env; } + Localisation::LocalisationService& GetLocalisationService() override + { + return *_localisationService; + } + IObjectManager * GetObjectManager() override { return _objectManager; @@ -344,22 +351,30 @@ namespace OpenRCT2 _trackDesignRepository = CreateTrackDesignRepository(_env); _scenarioRepository = CreateScenarioRepository(_env); #ifdef __ENABLE_DISCORD__ - _discordService = new DiscordService(); + _discordService = std::make_unique(); #endif - if (!language_open(gConfigGeneral.language)) + try { - log_error("Failed to open configured language..."); - if (!language_open(LANGUAGE_ENGLISH_UK)) + _localisationService->OpenLanguage(gConfigGeneral.language, *_objectManager); + } + catch (const std::exception& e) + { + log_error("Failed to open configured language: %s", e.what()); + try { - log_fatal("Failed to open fallback language..."); + _localisationService->OpenLanguage(LANGUAGE_ENGLISH_UK, *_objectManager); + } + catch (const std::exception&) + { + log_fatal("Failed to open fallback language: %s", e.what()); return false; } } if (platform_process_is_elevated()) { - std::string elevationWarning = language_get_string(STR_ADMIN_NOT_RECOMMENDED); + std::string elevationWarning = _localisationService->GetString(STR_ADMIN_NOT_RECOMMENDED); if (gOpenRCT2Headless) { Console::Error::WriteLine(elevationWarning.c_str()); @@ -378,14 +393,14 @@ namespace OpenRCT2 // 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(); + _objectRepository->LoadOrConstruct(_localisationService->GetCurrentLanguage()); // 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(); + _trackDesignRepository->Scan(_localisationService->GetCurrentLanguage()); - _scenarioRepository->Scan(); + _scenarioRepository->Scan(_localisationService->GetCurrentLanguage()); TitleSequenceManager::Scan(); if (!gOpenRCT2Headless) @@ -548,7 +563,7 @@ namespace OpenRCT2 bool LoadBaseGraphics() { - if (!gfx_load_g1(_env)) + if (!gfx_load_g1(*_env)) { return false; } @@ -900,37 +915,19 @@ namespace OpenRCT2 } }; - class PlainContext final : public Context - { - std::unique_ptr _env; - std::unique_ptr _audioContext; - std::unique_ptr _uiContext; - - public: - PlainContext() - : PlainContext(CreatePlatformEnvironment(), CreateDummyAudioContext(), CreateDummyUiContext()) - { - } - - PlainContext(IPlatformEnvironment * env, IAudioContext * audioContext, IUiContext * uiContext) - : Context(env, audioContext, uiContext) - { - _env = std::unique_ptr(env); - _audioContext = std::unique_ptr(audioContext); - _uiContext = std::unique_ptr(uiContext); - } - }; - Context * Context::Instance = nullptr; - IContext * CreateContext() + std::unique_ptr CreateContext() { - return new PlainContext(); + return std::make_unique(CreatePlatformEnvironment(), CreateDummyAudioContext(), CreateDummyUiContext()); } - IContext * CreateContext(IPlatformEnvironment * env, Audio::IAudioContext * audioContext, IUiContext * uiContext) + std::unique_ptr CreateContext( + const std::shared_ptr& env, + const std::shared_ptr& audioContext, + const std::shared_ptr& uiContext) { - return new Context(env, audioContext, uiContext); + return std::make_unique(env, audioContext, uiContext); } IContext * GetContext() diff --git a/src/openrct2/Context.h b/src/openrct2/Context.h index 9d9bc1b2e9..b5252e413f 100644 --- a/src/openrct2/Context.h +++ b/src/openrct2/Context.h @@ -18,6 +18,7 @@ #include "common.h" +#include #include interface IObjectManager; @@ -76,6 +77,11 @@ namespace OpenRCT2 interface IAudioContext; } + namespace Localisation + { + class LocalisationService; + } + namespace Ui { interface IUiContext; @@ -88,9 +94,10 @@ namespace OpenRCT2 { virtual ~IContext() = default; - virtual Audio::IAudioContext * GetAudioContext() abstract; - virtual Ui::IUiContext * GetUiContext() abstract; - virtual IPlatformEnvironment * GetPlatformEnvironment() abstract; + virtual std::shared_ptr GetAudioContext() abstract; + virtual std::shared_ptr GetUiContext() abstract; + virtual std::shared_ptr GetPlatformEnvironment() abstract; + virtual Localisation::LocalisationService& GetLocalisationService() abstract; virtual IObjectManager * GetObjectManager() abstract; virtual IObjectRepository * GetObjectRepository() abstract; virtual ITrackDesignRepository * GetTrackDesignRepository() abstract; @@ -111,8 +118,11 @@ namespace OpenRCT2 virtual std::string GetPathLegacy(sint32 pathId) abstract; }; - IContext * CreateContext(); - IContext * CreateContext(IPlatformEnvironment * env, Audio::IAudioContext * audioContext, Ui::IUiContext * uiContext); + std::unique_ptr CreateContext(); + std::unique_ptr CreateContext( + const std::shared_ptr& env, + const std::shared_ptr& audioContext, + const std::shared_ptr& uiContext); IContext * GetContext(); } // namespace OpenRCT2 diff --git a/src/openrct2/PlatformEnvironment.cpp b/src/openrct2/PlatformEnvironment.cpp index 40a56f3364..814d454f27 100644 --- a/src/openrct2/PlatformEnvironment.cpp +++ b/src/openrct2/PlatformEnvironment.cpp @@ -109,9 +109,9 @@ private: } }; -IPlatformEnvironment * OpenRCT2::CreatePlatformEnvironment(DIRBASE_VALUES basePaths) +std::unique_ptr OpenRCT2::CreatePlatformEnvironment(DIRBASE_VALUES basePaths) { - return new PlatformEnvironment(basePaths); + return std::make_unique(basePaths); } static std::string GetOpenRCT2DirectoryName() @@ -123,7 +123,7 @@ static std::string GetOpenRCT2DirectoryName() #endif } -IPlatformEnvironment * OpenRCT2::CreatePlatformEnvironment() +std::unique_ptr OpenRCT2::CreatePlatformEnvironment() { auto subDirectory = GetOpenRCT2DirectoryName(); diff --git a/src/openrct2/PlatformEnvironment.h b/src/openrct2/PlatformEnvironment.h index 36223a4694..77e0c3b865 100644 --- a/src/openrct2/PlatformEnvironment.h +++ b/src/openrct2/PlatformEnvironment.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include "common.h" @@ -82,6 +83,7 @@ namespace OpenRCT2 virtual void SetBasePath(DIRBASE base, const std::string &path) abstract; }; - IPlatformEnvironment * CreatePlatformEnvironment(DIRBASE_VALUES basePaths); - IPlatformEnvironment * CreatePlatformEnvironment(); + std::unique_ptr CreatePlatformEnvironment(DIRBASE_VALUES basePaths); + std::unique_ptr CreatePlatformEnvironment(); + } // namespace OpenRCT2 diff --git a/src/openrct2/audio/Audio.cpp b/src/openrct2/audio/Audio.cpp index 8672512a26..d744899879 100644 --- a/src/openrct2/audio/Audio.cpp +++ b/src/openrct2/audio/Audio.cpp @@ -157,7 +157,7 @@ void audio_populate_devices() { SafeFree(gAudioDevices); - IAudioContext * audioContext = OpenRCT2::GetContext()->GetAudioContext(); + auto audioContext = OpenRCT2::GetContext()->GetAudioContext(); std::vector devices = audioContext->GetOutputDevices(); // Replace blanks with localised unknown string diff --git a/src/openrct2/audio/AudioContext.h b/src/openrct2/audio/AudioContext.h index fc6a00f75b..c7079f5706 100644 --- a/src/openrct2/audio/AudioContext.h +++ b/src/openrct2/audio/AudioContext.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include #include "../common.h" @@ -59,5 +60,6 @@ namespace OpenRCT2::Audio virtual void StopVehicleSounds() abstract; }; - IAudioContext * CreateDummyAudioContext(); + std::unique_ptr CreateDummyAudioContext(); + } // namespace OpenRCT2::Audio diff --git a/src/openrct2/audio/AudioMixer.cpp b/src/openrct2/audio/AudioMixer.cpp index 955ef4170b..edd2f64108 100644 --- a/src/openrct2/audio/AudioMixer.cpp +++ b/src/openrct2/audio/AudioMixer.cpp @@ -28,13 +28,13 @@ using namespace OpenRCT2::Audio; static IAudioMixer * GetMixer() { - IAudioContext * audioContext = GetContext()->GetAudioContext(); + auto audioContext = GetContext()->GetAudioContext(); return audioContext->GetMixer(); } void Mixer_Init(const char * device) { - IAudioContext * audioContext = GetContext()->GetAudioContext(); + auto audioContext = GetContext()->GetAudioContext(); if (device == nullptr) { device = ""; @@ -140,7 +140,7 @@ void * Mixer_Play_Music(sint32 pathId, sint32 loop, sint32 streaming) { const utf8 * path = context_get_path_legacy(pathId); - IAudioContext * audioContext = GetContext()->GetAudioContext(); + auto audioContext = GetContext()->GetAudioContext(); IAudioSource * source = audioContext->CreateStreamFromWAV(path); if (source != nullptr) { diff --git a/src/openrct2/audio/DummyAudioContext.cpp b/src/openrct2/audio/DummyAudioContext.cpp index 7161ae21e5..a4b94a9321 100644 --- a/src/openrct2/audio/DummyAudioContext.cpp +++ b/src/openrct2/audio/DummyAudioContext.cpp @@ -45,8 +45,8 @@ namespace OpenRCT2::Audio void StopVehicleSounds() override { } }; - IAudioContext * CreateDummyAudioContext() + std::unique_ptr CreateDummyAudioContext() { - return new DummyAudioContext(); + return std::make_unique(); } } // namespace OpenRCT2::Audio diff --git a/src/openrct2/cmdline/RootCommands.cpp b/src/openrct2/cmdline/RootCommands.cpp index 37e246fdb3..2b864c82ac 100644 --- a/src/openrct2/cmdline/RootCommands.cpp +++ b/src/openrct2/cmdline/RootCommands.cpp @@ -399,12 +399,8 @@ static exitcode_t HandleCommandScanObjects([[maybe_unused]] CommandLineArgEnumer auto context = std::unique_ptr(OpenRCT2::CreateContext()); auto env = context->GetPlatformEnvironment(); - auto objectRepository = std::unique_ptr(CreateObjectRepository(env)); - - // HACK: set gCurrentLanguage otherwise it be wrong for the index file - gCurrentLanguage = gConfigGeneral.language; - - objectRepository->Construct(); + auto objectRepository = CreateObjectRepository(env); + objectRepository->Construct(gConfigGeneral.language); return EXITCODE_OK; } diff --git a/src/openrct2/config/Config.cpp b/src/openrct2/config/Config.cpp index e09e6cf205..e321585bc5 100644 --- a/src/openrct2/config/Config.cpp +++ b/src/openrct2/config/Config.cpp @@ -742,7 +742,7 @@ bool config_find_or_browse_install_directory() { while (true) { - IUiContext * uiContext = GetContext()->GetUiContext(); + auto uiContext = GetContext()->GetUiContext(); uiContext->ShowMessageBox("OpenRCT2 needs files from the original RollerCoaster Tycoon 2 in order to work. \nPlease select the directory where you installed RollerCoaster Tycoon 2."); std::string installPath = uiContext->ShowDirectoryDialog("Please select your RCT2 directory"); diff --git a/src/openrct2/core/FileIndex.hpp b/src/openrct2/core/FileIndex.hpp index f834e4f2ee..eb4ee05725 100644 --- a/src/openrct2/core/FileIndex.hpp +++ b/src/openrct2/core/FileIndex.hpp @@ -107,11 +107,11 @@ public: * Queries and directories and loads the index header. If the index is up to date, * the items are loaded from the index and returned, otherwise the index is rebuilt. */ - std::vector LoadOrBuild() const + std::vector LoadOrBuild(sint32 language) const { std::vector items; auto scanResult = Scan(); - auto readIndexResult = ReadIndexFile(scanResult.Stats); + auto readIndexResult = ReadIndexFile(language, scanResult.Stats); if (std::get<0>(readIndexResult)) { // Index was loaded @@ -120,15 +120,15 @@ public: else { // Index was not loaded - items = Build(scanResult); + items = Build(language, scanResult); } return items; } - std::vector Rebuild() const + std::vector Rebuild(sint32 language) const { auto scanResult = Scan(); - auto items = Build(scanResult); + auto items = Build(language, scanResult); return items; } @@ -137,7 +137,7 @@ protected: * Loads the given file and creates the item representing the data to store in the index. * TODO Use std::optional when C++17 is available. */ - virtual std::tuple Create(const std::string &path) const abstract; + virtual std::tuple Create(sint32 language, const std::string &path) const abstract; /** * Serialises an index item to the given stream. @@ -180,7 +180,8 @@ private: return ScanResult(stats, files); } - void BuildRange(const ScanResult &scanResult, + void BuildRange(sint32 language, + const ScanResult &scanResult, size_t rangeStart, size_t rangeEnd, std::vector& items, @@ -198,7 +199,7 @@ private: log_verbose("FileIndex:Indexing '%s'", filePath.c_str()); } - auto item = Create(filePath); + auto item = Create(language, filePath); if (std::get<0>(item)) { items.push_back(std::get<1>(item)); @@ -208,7 +209,7 @@ private: } } - std::vector Build(const ScanResult &scanResult) const + std::vector Build(sint32 language, const ScanResult &scanResult) const { std::vector allItems; Console::WriteLine("Building %s (%zu items)", _name.c_str(), scanResult.Files.size()); @@ -245,6 +246,7 @@ private: jobPool.AddTask(std::bind(&FileIndex::BuildRange, this, + language, std::cref(scanResult), rangeStart, rangeStart + stepSize, @@ -262,7 +264,7 @@ private: allItems.insert(allItems.end(), itr.begin(), itr.end()); } - WriteIndexFile(scanResult.Stats, allItems); + WriteIndexFile(language, scanResult.Stats, allItems); } auto endTime = std::chrono::high_resolution_clock::now(); @@ -272,7 +274,7 @@ private: return allItems; } - std::tuple> ReadIndexFile(const DirectoryStats &stats) const + std::tuple> ReadIndexFile(sint32 language, const DirectoryStats &stats) const { bool loadedItems = false; std::vector items; @@ -289,7 +291,7 @@ private: header.MagicNumber == _magicNumber && header.VersionA == FILE_INDEX_VERSION && header.VersionB == _version && - header.LanguageId == gCurrentLanguage && + header.LanguageId == language && header.Stats.TotalFiles == stats.TotalFiles && header.Stats.TotalFileSize == stats.TotalFileSize && header.Stats.FileDateModifiedChecksum == stats.FileDateModifiedChecksum && @@ -318,7 +320,7 @@ private: return std::make_tuple(loadedItems, items); } - void WriteIndexFile(const DirectoryStats &stats, const std::vector &items) const + void WriteIndexFile(sint32 language, const DirectoryStats &stats, const std::vector &items) const { try { @@ -331,7 +333,7 @@ private: header.MagicNumber = _magicNumber; header.VersionA = FILE_INDEX_VERSION; header.VersionB = _version; - header.LanguageId = gCurrentLanguage; + header.LanguageId = language; header.Stats = stats; header.NumItems = (uint32)items.size(); fs.WriteValue(header); diff --git a/src/openrct2/core/Guard.hpp b/src/openrct2/core/Guard.hpp index eb127feb5e..a8b32197a2 100644 --- a/src/openrct2/core/Guard.hpp +++ b/src/openrct2/core/Guard.hpp @@ -16,6 +16,7 @@ #pragma once +#include #include #include @@ -52,6 +53,15 @@ namespace Guard va_end(args); } + template + static void ArgumentNotNull(const std::shared_ptr& argument, const char * message = nullptr, ...) + { + va_list args; + va_start(args, message); + Assert_VA(argument != nullptr, message, args); + va_end(args); + } + template static void ArgumentInRange(T argument, T min, T max, const char * message = nullptr, ...) { diff --git a/src/openrct2/drawing/Drawing.h b/src/openrct2/drawing/Drawing.h index 7e4b8378fb..b2023e5dee 100644 --- a/src/openrct2/drawing/Drawing.h +++ b/src/openrct2/drawing/Drawing.h @@ -20,6 +20,11 @@ #include "../common.h" #include "../interface/Colour.h" +namespace OpenRCT2 +{ + interface IPlatformEnvironment; +} + struct rct_g1_element { uint8* offset; // 0x00 sint16 width; // 0x04 @@ -281,7 +286,7 @@ void gfx_fill_rect_inset(rct_drawpixelinfo* dpi, sint16 left, sint16 top, sint16 void gfx_filter_rect(rct_drawpixelinfo *dpi, sint32 left, sint32 top, sint32 right, sint32 bottom, FILTER_PALETTE_ID palette); // sprite -bool gfx_load_g1(void * platformEnvironment); +bool gfx_load_g1(const OpenRCT2::IPlatformEnvironment& env); bool gfx_load_g2(); bool gfx_load_csg(); void gfx_unload_g1(); diff --git a/src/openrct2/drawing/Font.cpp b/src/openrct2/drawing/Font.cpp index 1c47307140..d745b7985a 100644 --- a/src/openrct2/drawing/Font.cpp +++ b/src/openrct2/drawing/Font.cpp @@ -17,6 +17,7 @@ #include "../core/Util.hpp" #include "../localisation/FormatCodes.h" #include "../localisation/Language.h" +#include "../localisation/LocalisationService.h" #include "../sprites.h" #include "Drawing.h" #include "Font.h" @@ -158,7 +159,7 @@ sint32 font_get_line_height(sint32 fontSpriteBase) { sint32 fontSize = font_get_size_from_sprite_base(fontSpriteBase); #ifndef NO_TTF - if (gUseTrueTypeFont) { + if (LocalisationService_UseTrueTypeFont()) { return gCurrentTTFFontSet->size[fontSize].line_height; } else { #endif // NO_TTF @@ -265,7 +266,7 @@ bool font_supports_string_ttf(const utf8 *text, sint32 fontSize) bool font_supports_string(const utf8 *text, sint32 fontSize) { - if (gUseTrueTypeFont) { + if (LocalisationService_UseTrueTypeFont()) { return font_supports_string_ttf(text, fontSize); } else { return font_supports_string_sprite(text); diff --git a/src/openrct2/drawing/IDrawingEngine.h b/src/openrct2/drawing/IDrawingEngine.h index 448622967a..490545fc96 100644 --- a/src/openrct2/drawing/IDrawingEngine.h +++ b/src/openrct2/drawing/IDrawingEngine.h @@ -16,6 +16,7 @@ #pragma once +#include #include "../common.h" enum DRAWING_ENGINE @@ -40,8 +41,14 @@ enum DRAWING_ENGINE_FLAGS struct rct_drawpixelinfo; struct rct_palette_entry; +namespace OpenRCT2::Ui +{ + interface IUiContext; +} // namespace OpenRCT2::Ui + namespace OpenRCT2::Drawing { + enum class DRAWING_ENGINE_TYPE; interface IDrawingContext; interface IDrawingEngine @@ -70,6 +77,12 @@ namespace OpenRCT2::Drawing virtual void InvalidateImage(uint32 image) abstract; }; + interface IDrawingEngineFactory + { + virtual ~IDrawingEngineFactory() { } + virtual std::unique_ptr Create(DRAWING_ENGINE_TYPE type, const std::shared_ptr& uiContext) abstract; + }; + interface IRainDrawer { virtual ~IRainDrawer() { } diff --git a/src/openrct2/drawing/NewDrawing.cpp b/src/openrct2/drawing/NewDrawing.cpp index c7efa10dfb..32a4e71605 100644 --- a/src/openrct2/drawing/NewDrawing.cpp +++ b/src/openrct2/drawing/NewDrawing.cpp @@ -31,8 +31,8 @@ using namespace OpenRCT2::Drawing; using namespace OpenRCT2::Paint; using namespace OpenRCT2::Ui; -static sint32 _drawingEngineType = DRAWING_ENGINE_SOFTWARE; -static IDrawingEngine * _drawingEngine = nullptr; +static sint32 _drawingEngineType = DRAWING_ENGINE_SOFTWARE; +static std::shared_ptr _drawingEngine; // TODO move this to Context static Painter * _painter = nullptr; @@ -71,7 +71,8 @@ void drawing_engine_init() auto context = GetContext(); auto uiContext = context->GetUiContext(); - auto drawingEngine = uiContext->CreateDrawingEngine((DRAWING_ENGINE_TYPE)_drawingEngineType); + auto drawingEngineFactory = uiContext->GetDrawingEngineFactory(); + auto drawingEngine = drawingEngineFactory->Create((DRAWING_ENGINE_TYPE)_drawingEngineType, uiContext); if (drawingEngine == nullptr) { @@ -98,14 +99,12 @@ void drawing_engine_init() { drawingEngine->Initialise(); drawingEngine->SetVSync(gConfigGeneral.use_vsync); - _drawingEngine = drawingEngine; + _drawingEngine = std::shared_ptr(std::move(drawingEngine)); } catch (const std::exception &ex) { delete _painter; _painter = nullptr; - delete drawingEngine; - drawingEngine = nullptr; if (_drawingEngineType == DRAWING_ENGINE_SOFTWARE) { _drawingEngineType = DRAWING_ENGINE_NONE; @@ -131,7 +130,7 @@ void drawing_engine_resize() { if (_drawingEngine != nullptr) { - IUiContext * uiContext = GetContext()->GetUiContext(); + auto uiContext = GetContext()->GetUiContext(); _drawingEngine->Resize(uiContext->GetWidth(), uiContext->GetHeight()); } } @@ -149,7 +148,7 @@ void drawing_engine_draw() if (_drawingEngine != nullptr && _painter != nullptr) { _drawingEngine->BeginDraw(); - _painter->Paint(_drawingEngine); + _painter->Paint(*_drawingEngine); _drawingEngine->EndDraw(); } } @@ -164,7 +163,6 @@ void drawing_engine_copy_rect(sint32 x, sint32 y, sint32 width, sint32 height, s void drawing_engine_dispose() { - delete _drawingEngine; delete _painter; _drawingEngine = nullptr; _painter = nullptr; diff --git a/src/openrct2/drawing/ScrollingText.cpp b/src/openrct2/drawing/ScrollingText.cpp index d8c1ceb417..72851990fb 100644 --- a/src/openrct2/drawing/ScrollingText.cpp +++ b/src/openrct2/drawing/ScrollingText.cpp @@ -18,6 +18,7 @@ #include "../config/Config.h" #include "../interface/Colour.h" #include "../localisation/Localisation.h" +#include "../localisation/LocalisationService.h" #include "../paint/Paint.h" #include "../sprites.h" #include "Drawing.h" @@ -1459,7 +1460,7 @@ sint32 scrolling_text_setup(paint_session * session, rct_string_id stringId, uin const sint16* scrollingModePositions = _scrollPositions[scrollingMode]; memset(scrollText->bitmap, 0, 320 * 8); - if (gUseTrueTypeFont) { + if (LocalisationService_UseTrueTypeFont()) { scrolling_text_set_bitmap_for_ttf(scrollString, scroll, scrollText->bitmap, scrollingModePositions); } else { scrolling_text_set_bitmap_for_sprite(scrollString, scroll, scrollText->bitmap, scrollingModePositions); diff --git a/src/openrct2/drawing/Sprite.cpp b/src/openrct2/drawing/Sprite.cpp index 1d4ca49e9a..2d800ce902 100644 --- a/src/openrct2/drawing/Sprite.cpp +++ b/src/openrct2/drawing/Sprite.cpp @@ -227,14 +227,12 @@ bool gTinyFontAntiAliased = false; * * rct2: 0x00678998 */ -bool gfx_load_g1(void * platformEnvironment) +bool gfx_load_g1(const IPlatformEnvironment& env) { - auto env = (IPlatformEnvironment *)platformEnvironment; - log_verbose("gfx_load_g1(...)"); try { - auto path = Path::Combine(env->GetDirectoryPath(DIRBASE::RCT2, DIRID::DATA), "g1.dat"); + auto path = Path::Combine(env.GetDirectoryPath(DIRBASE::RCT2, DIRID::DATA), "g1.dat"); auto fs = FileStream(path, FILE_MODE_OPEN); _g1.header = fs.ReadValue(); @@ -269,7 +267,7 @@ bool gfx_load_g1(void * platformEnvironment) log_fatal("Unable to load g1 graphics"); if (!gOpenRCT2Headless) { - IUiContext * uiContext = GetContext()->GetUiContext(); + auto uiContext = GetContext()->GetUiContext(); uiContext->ShowMessageBox("Unable to load g1.dat. Your RollerCoaster Tycoon 2 path may be incorrectly set."); } return false; @@ -332,7 +330,7 @@ bool gfx_load_g2() log_fatal("Unable to load g2 graphics"); if (!gOpenRCT2Headless) { - IUiContext * uiContext = GetContext()->GetUiContext(); + auto uiContext = GetContext()->GetUiContext(); uiContext->ShowMessageBox("Unable to load g2.dat"); } } diff --git a/src/openrct2/drawing/String.cpp b/src/openrct2/drawing/String.cpp index 164635f7bd..9659c1b1c0 100644 --- a/src/openrct2/drawing/String.cpp +++ b/src/openrct2/drawing/String.cpp @@ -20,6 +20,7 @@ #include "../drawing/Drawing.h" #include "../interface/Viewport.h" #include "../localisation/Localisation.h" +#include "../localisation/LocalisationService.h" #include "../platform/platform.h" #include "../sprites.h" #include "../util/Util.h" @@ -822,7 +823,7 @@ void ttf_draw_string(rct_drawpixelinfo *dpi, const_utf8string text, sint32 colou info.x = x; info.y = y; - if (gUseTrueTypeFont) { + if (LocalisationService_UseTrueTypeFont()) { info.flags |= TEXT_DRAW_FLAG_TTF; } @@ -851,7 +852,7 @@ static sint32 ttf_get_string_width(const utf8 *text) info.maxY = 0; info.flags |= TEXT_DRAW_FLAG_NO_DRAW; - if (gUseTrueTypeFont) { + if (LocalisationService_UseTrueTypeFont()) { info.flags |= TEXT_DRAW_FLAG_TTF; } @@ -877,7 +878,7 @@ void gfx_draw_string_with_y_offsets(rct_drawpixelinfo *dpi, const utf8 *text, si info.flags |= TEXT_DRAW_FLAG_Y_OFFSET_EFFECT; - if (!forceSpriteFont && gUseTrueTypeFont) { + if (!forceSpriteFont && LocalisationService_UseTrueTypeFont()) { info.flags |= TEXT_DRAW_FLAG_TTF; } diff --git a/src/openrct2/drawing/TTF.cpp b/src/openrct2/drawing/TTF.cpp index 043cebd5bc..1b6d54c86e 100644 --- a/src/openrct2/drawing/TTF.cpp +++ b/src/openrct2/drawing/TTF.cpp @@ -21,6 +21,7 @@ #include "../config/Config.h" #include "../localisation/Localisation.h" +#include "../localisation/LocalisationService.h" #include "../OpenRCT2.h" #include "../platform/platform.h" #include "TTF.h" @@ -157,7 +158,7 @@ static void ttf_surface_cache_dispose_all() void ttf_toggle_hinting() { - if (!gUseTrueTypeFont) + if (!LocalisationService_UseTrueTypeFont()) { return; } diff --git a/src/openrct2/drawing/X8DrawingEngine.cpp b/src/openrct2/drawing/X8DrawingEngine.cpp index 3fc5d77361..c5aa68a3f7 100644 --- a/src/openrct2/drawing/X8DrawingEngine.cpp +++ b/src/openrct2/drawing/X8DrawingEngine.cpp @@ -133,7 +133,7 @@ void X8RainDrawer::Restore() #pragma GCC diagnostic ignored "-Wsuggest-final-methods" #endif -X8DrawingEngine::X8DrawingEngine(Ui::IUiContext * uiContext) +X8DrawingEngine::X8DrawingEngine(const std::shared_ptr& uiContext) { _drawingContext = new X8DrawingContext(this); #ifdef __ENABLE_LIGHTFX__ diff --git a/src/openrct2/drawing/X8DrawingEngine.h b/src/openrct2/drawing/X8DrawingEngine.h index 2d6dd6d6a0..737633ff48 100644 --- a/src/openrct2/drawing/X8DrawingEngine.h +++ b/src/openrct2/drawing/X8DrawingEngine.h @@ -91,7 +91,7 @@ namespace OpenRCT2 X8DrawingContext * _drawingContext; public: - explicit X8DrawingEngine(Ui::IUiContext * uiContext); + explicit X8DrawingEngine(const std::shared_ptr& uiContext); ~X8DrawingEngine() override; void Initialise() override; diff --git a/src/openrct2/interface/Fonts.cpp b/src/openrct2/interface/Fonts.cpp index c2c48e7a84..5e58ac578c 100644 --- a/src/openrct2/interface/Fonts.cpp +++ b/src/openrct2/interface/Fonts.cpp @@ -21,6 +21,9 @@ #include "../drawing/TTF.h" #include "../localisation/Language.h" +#include "../localisation/LocalisationService.h" + +using namespace OpenRCT2::Localisation; #ifndef NO_TTF uint8 const HINTING_DISABLED = 0; @@ -100,19 +103,19 @@ TTFFontSetDescriptor TTFFontArialUnicode = { { // clang-format on #endif // NO_TTF -static void LoadSpriteFont() +static void LoadSpriteFont(LocalisationService& localisationService) { ttf_dispose(); - gUseTrueTypeFont = false; + localisationService.UseTrueTypeFont(false); #ifndef NO_TTF gCurrentTTFFontSet = nullptr; #endif // NO_TTF } #ifndef NO_TTF -static bool LoadFont(TTFFontSetDescriptor * font) +static bool LoadFont(LocalisationService& localisationService, TTFFontSetDescriptor * font) { - gUseTrueTypeFont = true; + localisationService.UseTrueTypeFont(true); gCurrentTTFFontSet = font; ttf_dispose(); @@ -120,7 +123,7 @@ static bool LoadFont(TTFFontSetDescriptor * font) return fontInitialised; } -static bool LoadCustomConfigFont() +static bool LoadCustomConfigFont(LocalisationService& localisationService) { static TTFFontSetDescriptor TTFFontCustom = { { { gConfigFonts.file_name, gConfigFonts.font_name, gConfigFonts.size_tiny, gConfigFonts.x_offset, gConfigFonts.y_offset, @@ -134,7 +137,7 @@ static bool LoadCustomConfigFont() } }; ttf_dispose(); - gUseTrueTypeFont = true; + localisationService.UseTrueTypeFont(true); gCurrentTTFFontSet = &TTFFontCustom; bool fontInitialised = ttf_initialise(); @@ -142,16 +145,17 @@ static bool LoadCustomConfigFont() } #endif // NO_TTF -void TryLoadFonts() +void TryLoadFonts(LocalisationService& localisationService) { #ifndef NO_TTF - TTFontFamily const * fontFamily = LanguagesDescriptors[gCurrentLanguage].font_family; + auto currentLanguage = localisationService.GetCurrentLanguage(); + TTFontFamily const * fontFamily = LanguagesDescriptors[currentLanguage].font_family; if (fontFamily != FAMILY_OPENRCT2_SPRITE) { if (!String::IsNullOrEmpty(gConfigFonts.file_name)) { - if (LoadCustomConfigFont()) + if (LoadCustomConfigFont(localisationService)) { return; } @@ -160,7 +164,7 @@ void TryLoadFonts() for (auto &font : *fontFamily) { - if (LoadFont(font)) + if (LoadFont(localisationService, font)) { return; } @@ -175,7 +179,7 @@ void TryLoadFonts() for (auto &font : TTFFamilySansSerif) { - if (LoadFont(font)) + if (LoadFont(localisationService, font)) { return; } @@ -188,5 +192,5 @@ void TryLoadFonts() } } #endif // NO_TTF - LoadSpriteFont(); + LoadSpriteFont(localisationService); } diff --git a/src/openrct2/interface/Fonts.h b/src/openrct2/interface/Fonts.h index a1a30c6184..44be1a51f9 100644 --- a/src/openrct2/interface/Fonts.h +++ b/src/openrct2/interface/Fonts.h @@ -17,6 +17,11 @@ #ifndef OPENRCT2_FONTS_H #define OPENRCT2_FONTS_H +namespace OpenRCT2::Localisation +{ + class LocalisationService; +} + #include "../drawing/Font.h" #ifndef NO_TTF @@ -35,6 +40,6 @@ extern TTFFontSetDescriptor TTFFontArialUnicode; #define FONT(x) FONT_OPENRCT2_SPRITE #endif // NO_TTF -void TryLoadFonts(); +void TryLoadFonts(OpenRCT2::Localisation::LocalisationService& localisationService); #endif // OPENRCT2_FONTS_H diff --git a/src/openrct2/interface/Screenshot.cpp b/src/openrct2/interface/Screenshot.cpp index e559f09eda..fc47453757 100644 --- a/src/openrct2/interface/Screenshot.cpp +++ b/src/openrct2/interface/Screenshot.cpp @@ -448,7 +448,6 @@ sint32 cmdline_for_screenshot(const char * * argv, sint32 argc, ScreenshotOption { std::printf("%s\n", e.what()); drawing_engine_dispose(); - delete context; return -1; } @@ -518,7 +517,6 @@ sint32 cmdline_for_screenshot(const char * * argv, sint32 argc, ScreenshotOption { std::printf("Weather can only be set to an integer value from 1 till 6."); drawing_engine_dispose(); - delete context; return -1; } @@ -583,6 +581,5 @@ sint32 cmdline_for_screenshot(const char * * argv, sint32 argc, ScreenshotOption free(dpi.bits); drawing_engine_dispose(); } - delete context; return 1; } diff --git a/src/openrct2/localisation/Language.cpp b/src/openrct2/localisation/Language.cpp index 792a5cba2a..ea67d80f7d 100644 --- a/src/openrct2/localisation/Language.cpp +++ b/src/openrct2/localisation/Language.cpp @@ -22,10 +22,10 @@ #include "../interface/Fonts.h" #include "../interface/FontFamilies.h" #include "../object/ObjectManager.h" -#include "LanguagePack.h" - #include "../platform/platform.h" +#include "LanguagePack.h" #include "Localisation.h" +#include "LocalisationService.h" // clang-format off const language_descriptor LanguagesDescriptors[LANGUAGE_COUNT] = @@ -55,12 +55,6 @@ const language_descriptor LanguagesDescriptors[LANGUAGE_COUNT] = }; // clang-format on -sint32 gCurrentLanguage = LANGUAGE_UNDEFINED; -bool gUseTrueTypeFont = false; - -static ILanguagePack * _languageFallback = nullptr; -static ILanguagePack * _languageCurrent = nullptr; - // clang-format off const utf8 BlackUpArrowString[] = { (utf8)(uint8)0xC2, (utf8)(uint8)0x8E, (utf8)(uint8)0xE2, (utf8)(uint8)0x96, (utf8)(uint8)0xB2, (utf8)(uint8)0x00 }; const utf8 BlackDownArrowString[] = { (utf8)(uint8)0xC2, (utf8)(uint8)0x8E, (utf8)(uint8)0xE2, (utf8)(uint8)0x96, (utf8)(uint8)0xBC, (utf8)(uint8)0x00 }; @@ -100,130 +94,53 @@ uint8 language_get_id_from_locale(const char * locale) const char * language_get_string(rct_string_id id) { - const char * result = nullptr; - if (id == STR_EMPTY) - { - result = ""; - } - else if (id != STR_NONE) - { - if (_languageCurrent != nullptr) - { - result = _languageCurrent->GetString(id); - } - if (result == nullptr && _languageFallback != nullptr) - { - result = _languageFallback->GetString(id); - } - if (result == nullptr) - { - result = "(undefined string)"; - } - } - return result; -} - -static utf8 * GetLanguagePath(utf8 * buffer, size_t bufferSize, uint32 languageId) -{ - const char * locale = LanguagesDescriptors[languageId].locale; - - platform_get_openrct_data_path(buffer, bufferSize); - Path::Append(buffer, bufferSize, "language"); - Path::Append(buffer, bufferSize, locale); - String::Append(buffer, bufferSize, ".txt"); - return buffer; + const auto& localisationService = OpenRCT2::GetContext()->GetLocalisationService(); + return localisationService.GetString(id); } bool language_open(sint32 id) { - char filename[MAX_PATH]; - - language_close_all(); - if (id == LANGUAGE_UNDEFINED) + auto context = OpenRCT2::GetContext(); + auto& localisationService = context->GetLocalisationService(); + auto objectManager = context->GetObjectManager(); + try + { + localisationService.OpenLanguage(id, *objectManager); + return true; + } + catch (const std::exception&) { return false; } - - if (id != LANGUAGE_ENGLISH_UK) - { - GetLanguagePath(filename, sizeof(filename), LANGUAGE_ENGLISH_UK); - _languageFallback = LanguagePackFactory::FromFile(LANGUAGE_ENGLISH_UK, filename); - } - - GetLanguagePath(filename, sizeof(filename), id); - _languageCurrent = LanguagePackFactory::FromFile(id, filename); - if (_languageCurrent != nullptr) - { - gCurrentLanguage = id; - TryLoadFonts(); - - // Objects and their localised strings need to be refreshed - auto context = OpenRCT2::GetContext(); - context->GetObjectManager()->ResetObjects(); - return true; - } - - return false; } -void language_close_all() -{ - SafeDelete(_languageFallback); - SafeDelete(_languageCurrent); - gCurrentLanguage = LANGUAGE_UNDEFINED; -} - -constexpr rct_string_id NONSTEX_BASE_STRING_ID = 3463; -constexpr uint16 MAX_OBJECT_CACHED_STRINGS = 2048; - bool language_get_localised_scenario_strings(const utf8 *scenarioFilename, rct_string_id *outStringIds) { - outStringIds[0] = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename, 0); - outStringIds[1] = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename, 1); - outStringIds[2] = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename, 2); + const auto& localisationService = OpenRCT2::GetContext()->GetLocalisationService(); + auto result = localisationService.GetLocalisedScenarioStrings(scenarioFilename); + outStringIds[0] = std::get<0>(result); + outStringIds[1] = std::get<1>(result); + outStringIds[2] = std::get<2>(result); return outStringIds[0] != STR_NONE || outStringIds[1] != STR_NONE || outStringIds[2] != STR_NONE; } -static bool _availableObjectStringIdsInitialised = false; -static std::stack _availableObjectStringIds; - void language_free_object_string(rct_string_id stringId) { - if (stringId != 0) - { - if (_languageCurrent != nullptr) - { - _languageCurrent->RemoveString(stringId); - } - _availableObjectStringIds.push(stringId); - } + auto& localisationService = OpenRCT2::GetContext()->GetLocalisationService(); + localisationService.FreeObjectString(stringId); } rct_string_id language_get_object_override_string_id(const char * identifier, uint8 index) { - if (_languageCurrent == nullptr) - { - return STR_NONE; - } - return _languageCurrent->GetObjectOverrideStringId(identifier, index); + const auto& localisationService = OpenRCT2::GetContext()->GetLocalisationService(); + return localisationService.GetObjectOverrideStringId(identifier, index); } rct_string_id language_allocate_object_string(const std::string &target) { - if (!_availableObjectStringIdsInitialised) - { - _availableObjectStringIdsInitialised = true; - for (rct_string_id stringId = NONSTEX_BASE_STRING_ID + MAX_OBJECT_CACHED_STRINGS; stringId >= NONSTEX_BASE_STRING_ID; stringId--) - { - _availableObjectStringIds.push(stringId); - } - } - - rct_string_id stringId = _availableObjectStringIds.top(); - _availableObjectStringIds.pop(); - _languageCurrent->SetString(stringId, target); - return stringId; + auto& localisationService = OpenRCT2::GetContext()->GetLocalisationService(); + return localisationService.AllocateObjectString(target); } diff --git a/src/openrct2/localisation/Language.h b/src/openrct2/localisation/Language.h index 8725d8454c..e2b77ba462 100644 --- a/src/openrct2/localisation/Language.h +++ b/src/openrct2/localisation/Language.h @@ -86,9 +86,6 @@ struct language_descriptor { extern const language_descriptor LanguagesDescriptors[LANGUAGE_COUNT]; -extern sint32 gCurrentLanguage; -extern bool gUseTrueTypeFont; - extern const utf8 BlackUpArrowString[]; extern const utf8 BlackDownArrowString[]; extern const utf8 BlackLeftArrowString[]; @@ -98,7 +95,6 @@ extern const utf8 CheckBoxMarkString[]; uint8 language_get_id_from_locale(const char * locale); const char *language_get_string(rct_string_id id); bool language_open(sint32 id); -void language_close_all(); uint32 utf8_get_next(const utf8 *char_ptr, const utf8 **nextchar_ptr); utf8 *utf8_write_codepoint(utf8 *dst, uint32 codepoint); diff --git a/src/openrct2/localisation/LocalisationService.cpp b/src/openrct2/localisation/LocalisationService.cpp new file mode 100644 index 0000000000..1721626b2c --- /dev/null +++ b/src/openrct2/localisation/LocalisationService.cpp @@ -0,0 +1,166 @@ +#pragma region Copyright (c) 2018 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 +#include "../Context.h" +#include "../core/Path.hpp" +#include "../interface/Fonts.h" +#include "../object/ObjectManager.h" +#include "../PlatformEnvironment.h" +#include "Language.h" +#include "LanguagePack.h" +#include "LocalisationService.h" +#include "StringIds.h" + +using namespace OpenRCT2; +using namespace OpenRCT2::Localisation; + +static constexpr rct_string_id NONSTEX_BASE_STRING_ID = 3463; +static constexpr uint16 MAX_OBJECT_CACHED_STRINGS = 2048; + +LocalisationService::LocalisationService(const std::shared_ptr& env) + : _env(env) +{ + for (rct_string_id stringId = NONSTEX_BASE_STRING_ID + MAX_OBJECT_CACHED_STRINGS; stringId >= NONSTEX_BASE_STRING_ID; stringId--) + { + _availableObjectStringIds.push(stringId); + } +} + +// Define implementation here to avoid including LanguagePack.h in header +LocalisationService::~LocalisationService() +{ +} + +const char * LocalisationService::GetString(rct_string_id id) const +{ + const char * result = nullptr; + if (id == STR_EMPTY) + { + result = ""; + } + else if (id != STR_NONE) + { + if (_languageCurrent != nullptr) + { + result = _languageCurrent->GetString(id); + } + if (result == nullptr && _languageFallback != nullptr) + { + result = _languageFallback->GetString(id); + } + if (result == nullptr) + { + result = "(undefined string)"; + } + } + return result; +} + +std::string LocalisationService::GetLanguagePath(uint32 languageId) const +{ + auto locale = std::string(LanguagesDescriptors[languageId].locale); + auto languageDirectory = _env->GetDirectoryPath(DIRBASE::OPENRCT2, DIRID::LANGUAGE); + auto languagePath = Path::Combine(languageDirectory, locale + ".txt"); + return languagePath; +} + +void LocalisationService::OpenLanguage(sint32 id, IObjectManager& objectManager) +{ + CloseLanguages(); + if (id == LANGUAGE_UNDEFINED) + { + throw std::invalid_argument("id was undefined"); + } + + std::string filename; + if (id != LANGUAGE_ENGLISH_UK) + { + filename = GetLanguagePath(LANGUAGE_ENGLISH_UK); + _languageFallback = std::unique_ptr(LanguagePackFactory::FromFile(LANGUAGE_ENGLISH_UK, filename.c_str())); + } + + filename = GetLanguagePath(id); + _languageCurrent = std::unique_ptr(LanguagePackFactory::FromFile(id, filename.c_str())); + if (_languageCurrent != nullptr) + { + _currentLanguage = id; + TryLoadFonts(*this); + + // Objects and their localised strings need to be refreshed + objectManager.ResetObjects(); + } + else + { + throw std::runtime_error("Unable to open language " + std::to_string(id)); + } +} + +void LocalisationService::CloseLanguages() +{ + _languageFallback = nullptr; + _languageCurrent = nullptr; + _currentLanguage = LANGUAGE_UNDEFINED; +} + +std::tuple LocalisationService::GetLocalisedScenarioStrings(const std::string& scenarioFilename) const +{ + auto result0 = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename.c_str(), 0); + auto result1 = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename.c_str(), 1); + auto result2 = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename.c_str(), 2); + return std::make_tuple(result0, result1, result2); +} + +rct_string_id LocalisationService::GetObjectOverrideStringId(const char * identifier, uint8 index) const +{ + if (_languageCurrent == nullptr) + { + return STR_NONE; + } + return _languageCurrent->GetObjectOverrideStringId(identifier, index); +} + +rct_string_id LocalisationService::AllocateObjectString(const std::string& target) +{ + auto stringId = _availableObjectStringIds.top(); + _availableObjectStringIds.pop(); + _languageCurrent->SetString(stringId, target); + return stringId; +} + +void LocalisationService::FreeObjectString(rct_string_id stringId) +{ + if (stringId != STR_EMPTY) + { + if (_languageCurrent != nullptr) + { + _languageCurrent->RemoveString(stringId); + } + _availableObjectStringIds.push(stringId); + } +} + +sint32 LocalisationService_GetCurrentLanguage() +{ + const auto& localisationService = GetContext()->GetLocalisationService(); + return localisationService.GetCurrentLanguage(); +} + +bool LocalisationService_UseTrueTypeFont() +{ + const auto& localisationService = GetContext()->GetLocalisationService(); + return localisationService.UseTrueTypeFont(); +} diff --git a/src/openrct2/localisation/LocalisationService.h b/src/openrct2/localisation/LocalisationService.h new file mode 100644 index 0000000000..91bb5ecdfd --- /dev/null +++ b/src/openrct2/localisation/LocalisationService.h @@ -0,0 +1,66 @@ +#pragma region Copyright (c) 2018 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 +#include +#include +#include +#include "../common.h" + +interface ILanguagePack; +interface IObjectManager; + +namespace OpenRCT2 +{ + interface IPlatformEnvironment; +} + +namespace OpenRCT2::Localisation +{ + class LocalisationService + { + private: + const std::shared_ptr _env; + sint32 _currentLanguage{}; + bool _useTrueTypeFont{}; + std::unique_ptr _languageFallback; + std::unique_ptr _languageCurrent; + std::stack _availableObjectStringIds; + + public: + sint32 GetCurrentLanguage() const { return _currentLanguage; } + bool UseTrueTypeFont() const { return _useTrueTypeFont; } + void UseTrueTypeFont(bool value) { _useTrueTypeFont = value; } + + LocalisationService(const std::shared_ptr& env); + ~LocalisationService(); + + const char * GetString(rct_string_id id) const; + std::tuple GetLocalisedScenarioStrings(const std::string& scenarioFilename) const; + rct_string_id GetObjectOverrideStringId(const char * identifier, uint8 index) const; + std::string GetLanguagePath(uint32 languageId) const; + + void OpenLanguage(sint32 id, IObjectManager& objectManager); + void CloseLanguages(); + rct_string_id AllocateObjectString(const std::string& target); + void FreeObjectString(rct_string_id stringId); + }; +} + +// Legacy getters +// TODO Remove usages of these and instead call via shared reference +sint32 LocalisationService_GetCurrentLanguage(); +bool LocalisationService_UseTrueTypeFont(); diff --git a/src/openrct2/network/DiscordService.cpp b/src/openrct2/network/DiscordService.cpp index de5af9a966..5adc318a84 100644 --- a/src/openrct2/network/DiscordService.cpp +++ b/src/openrct2/network/DiscordService.cpp @@ -30,7 +30,7 @@ constexpr const char * APPLICATION_ID = "378612438200877056"; constexpr const char * STEAM_APP_ID = nullptr; constexpr const uint32 REFRESH_INTERVAL = 5 * GAME_UPDATE_FPS; // 5 seconds -static void OnReady() +static void OnReady(const DiscordUser * request) { log_verbose("DiscordService::OnReady()"); } diff --git a/src/openrct2/network/Network.cpp b/src/openrct2/network/Network.cpp index 35802c7ac2..648dea1d7e 100644 --- a/src/openrct2/network/Network.cpp +++ b/src/openrct2/network/Network.cpp @@ -142,7 +142,7 @@ Network::~Network() CloseConnection(); } -void Network::SetEnvironment(IPlatformEnvironment * env) +void Network::SetEnvironment(const std::shared_ptr& env) { _env = env; } @@ -2545,9 +2545,9 @@ void Network::Client_Handle_GAMEINFO(NetworkConnection& connection, NetworkPacke network_chat_show_server_greeting(); } -void network_set_env(void * env) +void network_set_env(const std::shared_ptr& env) { - gNetwork.SetEnvironment((IPlatformEnvironment *)env); + gNetwork.SetEnvironment(env); } void network_close() @@ -3327,7 +3327,7 @@ sint32 network_get_pickup_peep_old_x(uint8 playerid) { return _pickup_peep_old_x void network_send_chat(const char* text) {} void network_send_password(const char* password) {} void network_close() {} -void network_set_env(void * env) {} +void network_set_env(const std::shared_ptr&) {} void network_shutdown_client() {} void network_set_password(const char* password) {} uint8 network_get_current_player_id() { return 0; } diff --git a/src/openrct2/network/network.h b/src/openrct2/network/network.h index 271df19d1c..e0eb279dce 100644 --- a/src/openrct2/network/network.h +++ b/src/openrct2/network/network.h @@ -16,6 +16,7 @@ #pragma once +#include #include enum { @@ -49,12 +50,16 @@ struct GameAction; struct rct_peep; struct LocationXYZ16; +namespace OpenRCT2 +{ + interface IPlatformEnvironment; +} + #ifndef DISABLE_NETWORK #include #include #include -#include #include #include #include @@ -79,17 +84,12 @@ enum { struct ObjectRepositoryItem; -namespace OpenRCT2 -{ - interface IPlatformEnvironment; -} - class Network { public: Network(); ~Network(); - void SetEnvironment(OpenRCT2::IPlatformEnvironment * env); + void SetEnvironment(const std::shared_ptr& env); bool Init(); void Close(); bool BeginClient(const char* host, uint16 port); @@ -259,7 +259,7 @@ private: std::string _chatLogFilenameFormat = "%Y%m%d-%H%M%S.txt"; std::string _serverLogPath; std::string _serverLogFilenameFormat = "%Y%m%d-%H%M%S.txt"; - OpenRCT2::IPlatformEnvironment * _env = nullptr; + std::shared_ptr _env; void UpdateServer(); void UpdateClient(); @@ -301,7 +301,7 @@ private: #endif /* DISABLE_NETWORK */ -void network_set_env(void * env); +void network_set_env(const std::shared_ptr& env); void network_close(); void network_shutdown_client(); sint32 network_begin_client(const char *host, sint32 port); diff --git a/src/openrct2/object/Object.cpp b/src/openrct2/object/Object.cpp index fa83f26d0a..d5a664a491 100644 --- a/src/openrct2/object/Object.cpp +++ b/src/openrct2/object/Object.cpp @@ -76,6 +76,11 @@ std::string Object::GetString(uint8 index) const return sz; } +std::string Object::GetString(sint32 language, uint8 index) const +{ + return GetStringTable().GetString(language, index); +} + rct_object_entry Object::GetScgWallsHeader() { return Object::CreateHeader("SCGWALLS", 207140231, 3518650219); @@ -711,6 +716,11 @@ std::string Object::GetName() const return GetString(OBJ_STRING_ID_NAME); } +std::string Object::GetName(sint32 language) const +{ + return GetString(language, OBJ_STRING_ID_NAME); +} + #ifdef __WARN_SUGGEST_FINAL_METHODS__ #pragma GCC diagnostic pop #endif diff --git a/src/openrct2/object/Object.h b/src/openrct2/object/Object.h index 43c78a6c16..69b279660d 100644 --- a/src/openrct2/object/Object.h +++ b/src/openrct2/object/Object.h @@ -147,6 +147,7 @@ protected: std::string GetOverrideString(uint8 index) const; std::string GetString(uint8 index) const; + std::string GetString(sint32 language, uint8 index) const; void SetSourceGame(const uint8 sourceGame); bool IsRCT1Object(); @@ -172,6 +173,7 @@ public: virtual uint8 GetObjectType() const final { return _objectEntry.flags & 0x0F; } virtual std::string GetName() const; + virtual std::string GetName(sint32 language) const; virtual void SetRepositoryItem(ObjectRepositoryItem * item) const { } diff --git a/src/openrct2/object/ObjectRepository.cpp b/src/openrct2/object/ObjectRepository.cpp index 42a5f18799..c71b860787 100644 --- a/src/openrct2/object/ObjectRepository.cpp +++ b/src/openrct2/object/ObjectRepository.cpp @@ -43,6 +43,7 @@ #include "../config/Config.h" #include "../localisation/Localisation.h" +#include "../localisation/LocalisationService.h" #include "../object/Object.h" #include "ObjectList.h" #include "../platform/platform.h" @@ -86,21 +87,21 @@ private: IObjectRepository& _objectRepository; public: - explicit ObjectFileIndex(IObjectRepository& objectRepository, IPlatformEnvironment * env) : + explicit ObjectFileIndex(IObjectRepository& objectRepository, const IPlatformEnvironment& env) : FileIndex("object index", MAGIC_NUMBER, VERSION, - env->GetFilePath(PATHID::CACHE_OBJECTS), + env.GetFilePath(PATHID::CACHE_OBJECTS), std::string(PATTERN), std::vector({ - env->GetDirectoryPath(DIRBASE::OPENRCT2, DIRID::OBJECT), - env->GetDirectoryPath(DIRBASE::USER, DIRID::OBJECT) })), + env.GetDirectoryPath(DIRBASE::OPENRCT2, DIRID::OBJECT), + env.GetDirectoryPath(DIRBASE::USER, DIRID::OBJECT) })), _objectRepository(objectRepository) { } public: - std::tuple Create(const std::string &path) const override + std::tuple Create(sint32 language, const std::string &path) const override { auto extension = Path::GetExtension(path); if (String::Equals(extension, ".json", true)) @@ -111,7 +112,7 @@ public: ObjectRepositoryItem item = { 0 }; item.ObjectEntry = *object->GetObjectEntry(); item.Path = String::Duplicate(path); - item.Name = String::Duplicate(object->GetName()); + item.Name = String::Duplicate(object->GetName(language)); object->SetRepositoryItem(&item); delete object; return std::make_tuple(true, item); @@ -208,15 +209,15 @@ private: class ObjectRepository final : public IObjectRepository { - IPlatformEnvironment * const _env = nullptr; - ObjectFileIndex const _fileIndex; - std::vector _items; - ObjectEntryMap _itemMap; + std::shared_ptr const _env; + ObjectFileIndex const _fileIndex; + std::vector _items; + ObjectEntryMap _itemMap; public: - explicit ObjectRepository(IPlatformEnvironment * env) + explicit ObjectRepository(const std::shared_ptr& env) : _env(env), - _fileIndex(*this, env) + _fileIndex(*this, *env) { } @@ -225,17 +226,17 @@ public: ClearItems(); } - void LoadOrConstruct() override + void LoadOrConstruct(sint32 language) override { ClearItems(); - auto items = _fileIndex.LoadOrBuild(); + auto items = _fileIndex.LoadOrBuild(language); AddItems(items); SortItems(); } - void Construct() override + void Construct(sint32 language) override { - auto items = _fileIndex.Rebuild(); + auto items = _fileIndex.Rebuild(language); AddItems(items); SortItems(); } @@ -445,7 +446,8 @@ private: void ScanObject(const std::string &path) { - auto result = _fileIndex.Create(path); + auto language = LocalisationService_GetCurrentLanguage(); + auto result = _fileIndex.Create(language, path); if (std::get<0>(result)) { auto ori = std::get<1>(result); @@ -637,7 +639,7 @@ private: } }; -IObjectRepository * CreateObjectRepository(IPlatformEnvironment * env) +IObjectRepository * CreateObjectRepository(const std::shared_ptr& env) { return new ObjectRepository(env); } @@ -665,8 +667,9 @@ const rct_object_entry * object_list_find(rct_object_entry * entry) void object_list_load() { auto context = GetContext(); - IObjectRepository * objectRepository = context->GetObjectRepository(); - objectRepository->LoadOrConstruct(); + const auto& localisationService = context->GetLocalisationService(); + auto objectRepository = context->GetObjectRepository(); + objectRepository->LoadOrConstruct(localisationService.GetCurrentLanguage()); IObjectManager * objectManager = context->GetObjectManager(); objectManager->UnloadAll(); diff --git a/src/openrct2/object/ObjectRepository.h b/src/openrct2/object/ObjectRepository.h index 998f0ce379..9798710bb4 100644 --- a/src/openrct2/object/ObjectRepository.h +++ b/src/openrct2/object/ObjectRepository.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include "../common.h" #include "../object/Object.h" @@ -28,6 +29,11 @@ namespace OpenRCT2 interface IPlatformEnvironment; } +namespace OpenRCT2::Localisation +{ + class LocalisationService; +} + struct rct_drawpixelinfo; struct ObjectRepositoryItem @@ -58,8 +64,8 @@ interface IObjectRepository { virtual ~IObjectRepository() = default; - virtual void LoadOrConstruct() abstract; - virtual void Construct() abstract; + virtual void LoadOrConstruct(sint32 language) abstract; + virtual void Construct(sint32 language) abstract; virtual size_t GetNumObjects() const abstract; virtual const ObjectRepositoryItem * GetObjects() const abstract; virtual const ObjectRepositoryItem * FindObject(const utf8 * name) const abstract; @@ -77,7 +83,7 @@ interface IObjectRepository virtual void WritePackedObjects(IStream * stream, std::vector &objects) abstract; }; -IObjectRepository * CreateObjectRepository(OpenRCT2::IPlatformEnvironment * env); +IObjectRepository * CreateObjectRepository(const std::shared_ptr& env); bool IsObjectCustom(const ObjectRepositoryItem * object); diff --git a/src/openrct2/object/StringTable.cpp b/src/openrct2/object/StringTable.cpp index cc1a5568b5..7139b377c1 100644 --- a/src/openrct2/object/StringTable.cpp +++ b/src/openrct2/object/StringTable.cpp @@ -19,6 +19,7 @@ #include "../core/String.hpp" #include "../localisation/Language.h" #include "../localisation/LanguagePack.h" +#include "../localisation/LocalisationService.h" #include "Object.h" #include "StringTable.h" @@ -100,6 +101,18 @@ std::string StringTable::GetString(uint8 id) const return std::string(); } +std::string StringTable::GetString(uint8 language, uint8 id) const +{ + for (auto &string : _strings) + { + if (string.LanguageId == language && string.Id == id) + { + return string.Text; + } + } + return std::string(); +} + void StringTable::SetString(uint8 id, uint8 language, const std::string &text) { StringTableEntry entry; @@ -111,7 +124,8 @@ void StringTable::SetString(uint8 id, uint8 language, const std::string &text) void StringTable::Sort() { - std::sort(_strings.begin(), _strings.end(), [](const StringTableEntry &a, const StringTableEntry &b) -> bool + auto targetLanguage = LocalisationService_GetCurrentLanguage(); + std::sort(_strings.begin(), _strings.end(), [targetLanguage](const StringTableEntry &a, const StringTableEntry &b) -> bool { if (a.Id == b.Id) { @@ -120,11 +134,11 @@ void StringTable::Sort() return String::Compare(a.Text, b.Text, true) < 0; } - if (a.LanguageId == gCurrentLanguage) + if (a.LanguageId == targetLanguage) { return true; } - if (b.LanguageId == gCurrentLanguage) + if (b.LanguageId == targetLanguage) { return false; } diff --git a/src/openrct2/object/StringTable.h b/src/openrct2/object/StringTable.h index 34fa8de21a..0dd9c148be 100644 --- a/src/openrct2/object/StringTable.h +++ b/src/openrct2/object/StringTable.h @@ -56,5 +56,6 @@ public: void Read(IReadObjectContext * context, IStream * stream, uint8 id); void Sort(); std::string GetString(uint8 id) const; + std::string GetString(uint8 language, uint8 id) const; void SetString(uint8 id, uint8 language, const std::string &text); }; diff --git a/src/openrct2/paint/Paint.cpp b/src/openrct2/paint/Paint.cpp index 3315053eff..fbbe4a63bc 100644 --- a/src/openrct2/paint/Paint.cpp +++ b/src/openrct2/paint/Paint.cpp @@ -20,6 +20,7 @@ #include "../drawing/Drawing.h" #include "../interface/Viewport.h" #include "../localisation/Localisation.h" +#include "../localisation/LocalisationService.h" #include "Paint.h" #include "sprite/Sprite.h" #include "tile_element/TileElement.h" @@ -1201,7 +1202,7 @@ void paint_draw_money_structs(rct_drawpixelinfo * dpi, paint_string_struct * ps) // Use sprite font unless the currency contains characters unsupported by the sprite font bool forceSpriteFont = false; const currency_descriptor& currencyDesc = CurrencyDescriptors[gConfigGeneral.currency_format]; - if (gUseTrueTypeFont && font_supports_string_sprite(currencyDesc.symbol_unicode)) + if (LocalisationService_UseTrueTypeFont() && font_supports_string_sprite(currencyDesc.symbol_unicode)) { forceSpriteFont = true; } diff --git a/src/openrct2/paint/Painter.cpp b/src/openrct2/paint/Painter.cpp index d1168c95c5..550debda9c 100644 --- a/src/openrct2/paint/Painter.cpp +++ b/src/openrct2/paint/Painter.cpp @@ -34,21 +34,21 @@ using namespace OpenRCT2::Drawing; using namespace OpenRCT2::Paint; using namespace OpenRCT2::Ui; -Painter::Painter(IUiContext * uiContext) +Painter::Painter(const std::shared_ptr& uiContext) : _uiContext(uiContext) { } -void Painter::Paint(IDrawingEngine * de) +void Painter::Paint(IDrawingEngine& de) { - auto dpi = de->GetDrawingPixelInfo(); + auto dpi = de.GetDrawingPixelInfo(); if (gIntroState != INTRO_STATE_NONE) { intro_draw(dpi); } else { - de->PaintWindows(); + de.PaintWindows(); update_palette_effects(); chat_draw(dpi); @@ -62,7 +62,7 @@ void Painter::Paint(IDrawingEngine * de) gfx_draw_pickedup_peep(dpi); gfx_invalidate_pickedup_peep(); - de->PaintRain(); + de.PaintRain(); } if (gConfigGeneral.show_fps) diff --git a/src/openrct2/paint/Painter.h b/src/openrct2/paint/Painter.h index a91f4f9bd4..2e73c9de69 100644 --- a/src/openrct2/paint/Painter.h +++ b/src/openrct2/paint/Painter.h @@ -38,15 +38,15 @@ namespace OpenRCT2 class Painter final { private: - Ui::IUiContext * const _uiContext; + std::shared_ptr const _uiContext; time_t _lastSecond = 0; sint32 _currentFPS = 0; sint32 _frames = 0; public: - explicit Painter(Ui::IUiContext * uiContext); - void Paint(Drawing::IDrawingEngine * de); + explicit Painter(const std::shared_ptr& uiContext); + void Paint(Drawing::IDrawingEngine& de); private: void PaintFPS(rct_drawpixelinfo * dpi); diff --git a/src/openrct2/ride/TrackDesignRepository.cpp b/src/openrct2/ride/TrackDesignRepository.cpp index 66e59538c3..36d7de2202 100644 --- a/src/openrct2/ride/TrackDesignRepository.cpp +++ b/src/openrct2/ride/TrackDesignRepository.cpp @@ -26,6 +26,7 @@ #include "../core/FileStream.hpp" #include "../core/Path.hpp" #include "../core/String.hpp" +#include "../localisation/LocalisationService.h" #include "../object/ObjectRepository.h" #include "../object/RideObject.h" #include "../PlatformEnvironment.h" @@ -66,21 +67,21 @@ private: static constexpr auto PATTERN = "*.td4;*.td6"; public: - explicit TrackDesignFileIndex(IPlatformEnvironment * env) : + explicit TrackDesignFileIndex(const IPlatformEnvironment &env) : FileIndex("track design index", MAGIC_NUMBER, VERSION, - env->GetFilePath(PATHID::CACHE_TRACKS), + env.GetFilePath(PATHID::CACHE_TRACKS), std::string(PATTERN), std::vector({ - env->GetDirectoryPath(DIRBASE::RCT1, DIRID::TRACK), - env->GetDirectoryPath(DIRBASE::RCT2, DIRID::TRACK), - env->GetDirectoryPath(DIRBASE::USER, DIRID::TRACK) })) + env.GetDirectoryPath(DIRBASE::RCT1, DIRID::TRACK), + env.GetDirectoryPath(DIRBASE::RCT2, DIRID::TRACK), + env.GetDirectoryPath(DIRBASE::USER, DIRID::TRACK) })) { } public: - std::tuple Create(const std::string &path) const override + std::tuple Create(sint32, const std::string &path) const override { auto td6 = track_design_open(path.c_str()); if (td6 != nullptr) @@ -137,14 +138,14 @@ private: class TrackDesignRepository final : public ITrackDesignRepository { private: - IPlatformEnvironment * const _env; + std::shared_ptr const _env; TrackDesignFileIndex const _fileIndex; std::vector _items; public: - explicit TrackDesignRepository(IPlatformEnvironment * env) + explicit TrackDesignRepository(const std::shared_ptr& env) : _env(env), - _fileIndex(env) + _fileIndex(*env) { Guard::ArgumentNotNull(env); } @@ -284,10 +285,10 @@ public: return refs; } - void Scan() override + void Scan(sint32 language) override { _items.clear(); - auto trackDesigns = _fileIndex.LoadOrBuild(); + auto trackDesigns = _fileIndex.LoadOrBuild(language); for (const auto &td : trackDesigns) { _items.push_back(td); @@ -347,7 +348,8 @@ public: std::string newPath = Path::Combine(installDir, fileName); if (File::Copy(path, newPath, false)) { - auto td = _fileIndex.Create(path); + auto language = LocalisationService_GetCurrentLanguage(); + auto td = _fileIndex.Create(language, path); if (std::get<0>(td)) { _items.push_back(std::get<1>(td)); @@ -396,7 +398,7 @@ private: } }; -ITrackDesignRepository * CreateTrackDesignRepository(IPlatformEnvironment * env) +ITrackDesignRepository * CreateTrackDesignRepository(const std::shared_ptr& env) { return new TrackDesignRepository(env); } @@ -404,7 +406,7 @@ ITrackDesignRepository * CreateTrackDesignRepository(IPlatformEnvironment * env) void track_repository_scan() { ITrackDesignRepository * repo = GetContext()->GetTrackDesignRepository(); - repo->Scan(); + repo->Scan(LocalisationService_GetCurrentLanguage()); } bool track_repository_delete(const utf8 * path) diff --git a/src/openrct2/ride/TrackDesignRepository.h b/src/openrct2/ride/TrackDesignRepository.h index 7da6a7fdcc..40b83e762a 100644 --- a/src/openrct2/ride/TrackDesignRepository.h +++ b/src/openrct2/ride/TrackDesignRepository.h @@ -16,6 +16,7 @@ #pragma once +#include #include "../common.h" #include "RideGroupManager.h" @@ -45,13 +46,13 @@ interface ITrackDesignRepository virtual std::vector GetItemsForRideGroup(uint8 rideType, const RideGroup * rideGroup) const abstract; - virtual void Scan() abstract; + virtual void Scan(sint32 language) abstract; virtual bool Delete(const std::string &path) abstract; virtual std::string Rename(const std::string &path, const std::string &newName) abstract; virtual std::string Install(const std::string &path) abstract; }; -ITrackDesignRepository * CreateTrackDesignRepository(OpenRCT2::IPlatformEnvironment * env); +ITrackDesignRepository * CreateTrackDesignRepository(const std::shared_ptr& env); std::string GetNameFromTrackPath(const std::string &path); void track_repository_scan(); diff --git a/src/openrct2/scenario/ScenarioRepository.cpp b/src/openrct2/scenario/ScenarioRepository.cpp index 04c84c70d9..ba8d09be91 100644 --- a/src/openrct2/scenario/ScenarioRepository.cpp +++ b/src/openrct2/scenario/ScenarioRepository.cpp @@ -33,6 +33,7 @@ #include "../config/Config.h" #include "../localisation/Localisation.h" +#include "../localisation/LocalisationService.h" #include "../platform/platform.h" #include "Scenario.h" #include "../Game.h" @@ -128,21 +129,21 @@ private: static constexpr auto PATTERN = "*.sc4;*.sc6"; public: - explicit ScenarioFileIndex(IPlatformEnvironment * env) : + explicit ScenarioFileIndex(const IPlatformEnvironment& env) : FileIndex("scenario index", MAGIC_NUMBER, VERSION, - env->GetFilePath(PATHID::CACHE_SCENARIOS), + env.GetFilePath(PATHID::CACHE_SCENARIOS), std::string(PATTERN), std::vector({ - env->GetDirectoryPath(DIRBASE::RCT1, DIRID::SCENARIO), - env->GetDirectoryPath(DIRBASE::RCT2, DIRID::SCENARIO), - env->GetDirectoryPath(DIRBASE::USER, DIRID::SCENARIO) })) + env.GetDirectoryPath(DIRBASE::RCT1, DIRID::SCENARIO), + env.GetDirectoryPath(DIRBASE::RCT2, DIRID::SCENARIO), + env.GetDirectoryPath(DIRBASE::USER, DIRID::SCENARIO) })) { } protected: - std::tuple Create(const std::string &path) const override + std::tuple Create(sint32, const std::string &path) const override { scenario_index_entry entry; auto timestamp = File::GetLastModified(path); @@ -322,15 +323,15 @@ class ScenarioRepository final : public IScenarioRepository private: static constexpr uint32 HighscoreFileVersion = 1; - IPlatformEnvironment * const _env; + std::shared_ptr const _env; ScenarioFileIndex const _fileIndex; std::vector _scenarios; std::vector _highscores; public: - explicit ScenarioRepository(IPlatformEnvironment * env) + explicit ScenarioRepository(const std::shared_ptr& env) : _env(env), - _fileIndex(env) + _fileIndex(*env) { } @@ -339,13 +340,13 @@ public: ClearHighscores(); } - void Scan() override + void Scan(sint32 language) override { ImportMegaPark(); // Reload scenarios from index _scenarios.clear(); - auto scenarios = _fileIndex.LoadOrBuild(); + auto scenarios = _fileIndex.LoadOrBuild(language); for (auto scenario : scenarios) { AddScenario(scenario); @@ -415,11 +416,11 @@ public: return nullptr; } - bool TryRecordHighscore(const utf8 * scenarioFileName, money32 companyValue, const utf8 * name) override + bool TryRecordHighscore(sint32 language, const utf8 * scenarioFileName, money32 companyValue, const utf8 * name) override { // Scan the scenarios so we have a fresh list to query. This is to prevent the issue of scenario completions // not getting recorded, see #4951. - Scan(); + Scan(language); scenario_index_entry * scenario = GetByFilename(scenarioFileName); if (scenario != nullptr) @@ -729,7 +730,7 @@ private: static ScenarioRepository * _scenarioRepository; -IScenarioRepository * CreateScenarioRepository(IPlatformEnvironment * env) +IScenarioRepository * CreateScenarioRepository(const std::shared_ptr& env) { _scenarioRepository = new ScenarioRepository(env); return _scenarioRepository; @@ -743,7 +744,7 @@ IScenarioRepository * GetScenarioRepository() void scenario_repository_scan() { IScenarioRepository * repo = GetScenarioRepository(); - repo->Scan(); + repo->Scan(LocalisationService_GetCurrentLanguage()); } size_t scenario_repository_get_count() @@ -761,6 +762,6 @@ const scenario_index_entry *scenario_repository_get_by_index(size_t index) bool scenario_repository_try_record_highscore(const utf8 * scenarioFileName, money32 companyValue, const utf8 * name) { IScenarioRepository * repo = GetScenarioRepository(); - return repo->TryRecordHighscore(scenarioFileName, companyValue, name); + return repo->TryRecordHighscore(LocalisationService_GetCurrentLanguage(), scenarioFileName, companyValue, name); } diff --git a/src/openrct2/scenario/ScenarioRepository.h b/src/openrct2/scenario/ScenarioRepository.h index 4c83ed3fbe..f1aed054e6 100644 --- a/src/openrct2/scenario/ScenarioRepository.h +++ b/src/openrct2/scenario/ScenarioRepository.h @@ -16,6 +16,7 @@ #pragma once +#include #include "../common.h" struct rct_object_entry; @@ -63,7 +64,7 @@ interface IScenarioRepository /** * Scans the scenario directories and grabs the metadata for all the scenarios. */ - virtual void Scan() abstract; + virtual void Scan(sint32 language) abstract; virtual size_t GetCount() const abstract; virtual const scenario_index_entry * GetByIndex(size_t index) const abstract; @@ -74,10 +75,10 @@ interface IScenarioRepository virtual const scenario_index_entry * GetByInternalName(const utf8 * name) const abstract; virtual const scenario_index_entry * GetByPath(const utf8 * path) const abstract; - virtual bool TryRecordHighscore(const utf8 * scenarioFileName, money32 companyValue, const utf8 * name) abstract; + virtual bool TryRecordHighscore(sint32 language, const utf8 * scenarioFileName, money32 companyValue, const utf8 * name) abstract; }; -IScenarioRepository * CreateScenarioRepository(OpenRCT2::IPlatformEnvironment * env); +IScenarioRepository * CreateScenarioRepository(const std::shared_ptr& env); IScenarioRepository * GetScenarioRepository(); void scenario_repository_scan(); diff --git a/src/openrct2/ui/DummyUiContext.cpp b/src/openrct2/ui/DummyUiContext.cpp index 9d882d292c..036b81b60e 100644 --- a/src/openrct2/ui/DummyUiContext.cpp +++ b/src/openrct2/ui/DummyUiContext.cpp @@ -66,10 +66,18 @@ namespace OpenRCT2::Ui const uint8 * GetKeysPressed() override { return nullptr; } void SetKeysPressed(uint32 keysym, uint8 scancode) override { } - // Drawing - Drawing::IDrawingEngine * CreateDrawingEngine(Drawing::DRAWING_ENGINE_TYPE type) override + class X8DrawingEngineFactory final : public IDrawingEngineFactory { - return new X8DrawingEngine(this); + std::unique_ptr Create(DRAWING_ENGINE_TYPE type, const std::shared_ptr& uiContext) override + { + return std::make_unique(uiContext); + } + }; + + // Drawing + std::shared_ptr GetDrawingEngineFactory() override + { + return std::make_shared(); } // Text input @@ -92,8 +100,8 @@ namespace OpenRCT2::Ui ~DummyUiContext() { delete _windowManager; } }; - IUiContext * CreateDummyUiContext() + std::shared_ptr CreateDummyUiContext() { - return new DummyUiContext(); + return std::make_unique(); } } // namespace OpenRCT2::Ui diff --git a/src/openrct2/ui/UiContext.h b/src/openrct2/ui/UiContext.h index a511f794f5..b80c725ea2 100644 --- a/src/openrct2/ui/UiContext.h +++ b/src/openrct2/ui/UiContext.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include #include "../common.h" @@ -28,8 +29,7 @@ namespace OpenRCT2 { namespace Drawing { - enum class DRAWING_ENGINE_TYPE; - interface IDrawingEngine; + interface IDrawingEngineFactory; } // namespace Drawing namespace Ui @@ -130,7 +130,7 @@ namespace OpenRCT2 virtual void SetKeysPressed(uint32 keysym, uint8 scancode) abstract; // Drawing - virtual Drawing::IDrawingEngine * CreateDrawingEngine(Drawing::DRAWING_ENGINE_TYPE type) abstract; + virtual std::shared_ptr GetDrawingEngineFactory() abstract; // Text input virtual bool IsTextInputActive() abstract; @@ -149,6 +149,6 @@ namespace OpenRCT2 virtual bool SetClipboardText(const utf8* target) abstract; }; - IUiContext * CreateDummyUiContext(); + std::shared_ptr CreateDummyUiContext(); } // namespace Ui } // namespace OpenRCT2 diff --git a/test/tests/MultiLaunch.cpp b/test/tests/MultiLaunch.cpp index c0ab60b66c..dbd8b939cf 100644 --- a/test/tests/MultiLaunch.cpp +++ b/test/tests/MultiLaunch.cpp @@ -46,8 +46,6 @@ TEST(MultiLaunchTest, all) // Check ride count again ASSERT_EQ(gRideCount, 134); - - delete context; } SUCCEED(); } diff --git a/test/tests/RideRatings.cpp b/test/tests/RideRatings.cpp index 8b31312cae..52178af78b 100644 --- a/test/tests/RideRatings.cpp +++ b/test/tests/RideRatings.cpp @@ -91,6 +91,4 @@ TEST_F(RideRatings, all) expI++; } } - - delete context; } diff --git a/test/tests/TileElements.cpp b/test/tests/TileElements.cpp index 37d57dad6d..e305c980e8 100644 --- a/test/tests/TileElements.cpp +++ b/test/tests/TileElements.cpp @@ -27,19 +27,11 @@ protected: game_load_init(); SUCCEED(); } - - static void TearDownTestCase() - { - delete _context; - _context = nullptr; - SUCCEED(); - } - private: - static IContext * _context; + static std::shared_ptr _context; }; -IContext * TileElementWantsFootpathConnection::_context = nullptr; +std::shared_ptr TileElementWantsFootpathConnection::_context; TEST_F(TileElementWantsFootpathConnection, FlatPath) {