From dcb92a466dbc1684d2dc6d827b894fa83d2d693b Mon Sep 17 00:00:00 2001 From: Ted John Date: Tue, 28 Nov 2017 20:57:34 +0000 Subject: [PATCH] Add new base path for user data and config Refactor more path resolution to Platform2.cpp --- src/openrct2/PlatformEnvironment.cpp | 90 ++++++++++------- src/openrct2/PlatformEnvironment.h | 4 +- src/openrct2/core/FileIndex.hpp | 1 + src/openrct2/core/Path.cpp | 5 + src/openrct2/core/Path.hpp | 1 + src/openrct2/platform/Platform2.cpp | 139 ++++++++++++++++++++++++++- src/openrct2/platform/Platform2.h | 12 +++ 7 files changed, 215 insertions(+), 37 deletions(-) diff --git a/src/openrct2/PlatformEnvironment.cpp b/src/openrct2/PlatformEnvironment.cpp index c21f6c1810..a8be259eae 100644 --- a/src/openrct2/PlatformEnvironment.cpp +++ b/src/openrct2/PlatformEnvironment.cpp @@ -21,11 +21,11 @@ #include "core/Path.hpp" #include "core/String.hpp" #include "OpenRCT2.h" +#include "platform/platform.h" +#include "platform/Platform2.h" #include "PlatformEnvironment.h" #include "Version.h" -#include "platform/platform.h" - using namespace OpenRCT2; class PlatformEnvironment final : public IPlatformEnvironment @@ -36,7 +36,7 @@ private: public: PlatformEnvironment(DIRBASE_VALUES basePaths) { - for (sint32 i = 0; i < 4; i++) + for (sint32 i = 0; i < DIRBASE_COUNT; i++) { _basePath[i] = basePaths[i]; } @@ -59,6 +59,7 @@ public: break; case DIRBASE::OPENRCT2: case DIRBASE::USER: + case DIRBASE::CONFIG: directoryName = DirectoryNamesOpenRCT2[(size_t)did]; break; } @@ -68,21 +69,10 @@ public: std::string GetFilePath(PATHID pathid) const override { - const utf8 * fileName = FileNames[(size_t)pathid]; - const utf8 * basePath = _basePath[(size_t)DIRBASE::USER].c_str(); - if (pathid == PATHID::MP_DAT) - { - basePath = _basePath[(size_t)DIRBASE::RCT1].c_str(); - } - else if (pathid == PATHID::SCORES_RCT2) - { - basePath = _basePath[(size_t)DIRBASE::RCT2].c_str(); - } - - utf8 path[260]; - String::Set(path, sizeof(path), basePath); - Path::Append(path, sizeof(path), fileName); - return std::string(path); + auto dirbase = GetDefaultBaseDirectory(pathid); + auto basePath = GetDirectoryPath(dirbase); + auto fileName = FileNames[(size_t)pathid]; + return Path::Combine(basePath, fileName); } void SetBasePath(DIRBASE base, const std::string &path) override @@ -94,6 +84,30 @@ private: static const char * DirectoryNamesRCT2[]; static const char * DirectoryNamesOpenRCT2[]; static const char * FileNames[]; + + static DIRBASE GetDefaultBaseDirectory(PATHID pathid) + { + switch (pathid) + { + case PATHID::CONFIG: + case PATHID::CONFIG_KEYBOARD: + return DIRBASE::CONFIG; + case PATHID::CACHE_OBJECTS: + case PATHID::CACHE_TRACKS: + case PATHID::CACHE_SCENARIOS: + return DIRBASE::CACHE; + case PATHID::MP_DAT: + return DIRBASE::RCT1; + case PATHID::SCORES_RCT2: + return DIRBASE::RCT2; + case PATHID::NETWORK_GROUPS: + case PATHID::NETWORK_SERVERS: + case PATHID::NETWORK_USERS: + case PATHID::SCORES: + case PATHID::SCORES_LEGACY: + return DIRBASE::USER; + } + } }; IPlatformEnvironment * OpenRCT2::CreatePlatformEnvironment(DIRBASE_VALUES basePaths) @@ -103,32 +117,36 @@ IPlatformEnvironment * OpenRCT2::CreatePlatformEnvironment(DIRBASE_VALUES basePa IPlatformEnvironment * OpenRCT2::CreatePlatformEnvironment() { - utf8 userPath[MAX_PATH]; - platform_resolve_openrct_data_path(); platform_resolve_user_data_path(); - platform_get_user_directory(userPath, nullptr, sizeof(userPath)); - if (!platform_ensure_directory_exists(userPath)) - { - Console::Error::WriteLine("Could not create user directory '%s' (do you have write access to your documents folder?)", userPath); - return nullptr; - } - platform_get_exe_path(gExePath, sizeof(gExePath)); - log_verbose("Setting exe path to %s", gExePath); - config_set_defaults(); if (!config_open_default()) { config_save_default(); } - utf8 path[260]; - std::string basePaths[4]; + std::string basePaths[DIRBASE_COUNT]; basePaths[(size_t)DIRBASE::RCT1] = String::ToStd(gConfigGeneral.rct1_path); basePaths[(size_t)DIRBASE::RCT2] = String::ToStd(gConfigGeneral.rct2_path); - platform_get_openrct_data_path(path, sizeof(path)); - basePaths[(size_t)DIRBASE::OPENRCT2] = std::string(path); - platform_get_user_directory(path, nullptr, sizeof(path)); - basePaths[(size_t)DIRBASE::USER] = std::string(path); + basePaths[(size_t)DIRBASE::OPENRCT2] = Platform::GetInstallPath(); + basePaths[(size_t)DIRBASE::USER] = Path::Combine(Platform::GetFolderPath(SPECIAL_FOLDER::USER_DATA), "openrct2"); + basePaths[(size_t)DIRBASE::CONFIG] = Path::Combine(Platform::GetFolderPath(SPECIAL_FOLDER::USER_CONFIG), "openrct2"); + basePaths[(size_t)DIRBASE::CACHE] = Path::Combine(Platform::GetFolderPath(SPECIAL_FOLDER::USER_CACHE), "openrct2"); + + // Override paths that have been specified via the command line + if (!String::IsNullOrEmpty(gCustomRCT2DataPath)) + { + basePaths[(size_t)DIRBASE::RCT2] = gCustomRCT2DataPath; + } + if (!String::IsNullOrEmpty(gCustomOpenrctDataPath)) + { + basePaths[(size_t)DIRBASE::OPENRCT2] = gCustomOpenrctDataPath; + } + if (!String::IsNullOrEmpty(gCustomUserDataPath)) + { + basePaths[(size_t)DIRBASE::USER] = gCustomUserDataPath; + basePaths[(size_t)DIRBASE::CONFIG] = gCustomUserDataPath; + basePaths[(size_t)DIRBASE::CACHE] = gCustomUserDataPath; + } IPlatformEnvironment * env = OpenRCT2::CreatePlatformEnvironment(basePaths); @@ -137,6 +155,8 @@ IPlatformEnvironment * OpenRCT2::CreatePlatformEnvironment() log_verbose("DIRBASE::RCT2 : %s", env->GetDirectoryPath(DIRBASE::RCT2).c_str()); log_verbose("DIRBASE::OPENRCT2: %s", env->GetDirectoryPath(DIRBASE::OPENRCT2).c_str()); log_verbose("DIRBASE::USER : %s", env->GetDirectoryPath(DIRBASE::USER).c_str()); + log_verbose("DIRBASE::CONFIG : %s", env->GetDirectoryPath(DIRBASE::CONFIG).c_str()); + log_verbose("DIRBASE::CACHE : %s", env->GetDirectoryPath(DIRBASE::CACHE).c_str()); return env; } diff --git a/src/openrct2/PlatformEnvironment.h b/src/openrct2/PlatformEnvironment.h index 54b97dbeac..de4b9894b8 100644 --- a/src/openrct2/PlatformEnvironment.h +++ b/src/openrct2/PlatformEnvironment.h @@ -29,8 +29,10 @@ namespace OpenRCT2 RCT2, // Base directory for original RollerCoaster Tycoon 2 content. OPENRCT2, // Base directory for OpenRCT2 installation. USER, // Base directory for OpenRCT2 user content. + CONFIG, // Base directory for OpenRCT2 configuration. + CACHE, // Base directory for OpenRCT2 cache files. }; - constexpr sint32 DIRBASE_COUNT = 4; + constexpr sint32 DIRBASE_COUNT = 6; using DIRBASE_VALUES = std::string[DIRBASE_COUNT]; enum class DIRID diff --git a/src/openrct2/core/FileIndex.hpp b/src/openrct2/core/FileIndex.hpp index 8ab8db2074..91ad4b0638 100644 --- a/src/openrct2/core/FileIndex.hpp +++ b/src/openrct2/core/FileIndex.hpp @@ -253,6 +253,7 @@ private: try { log_verbose("FileIndex:Writing index: '%s'", _indexPath.c_str()); + Path::CreateDirectory(Path::GetDirectory(_indexPath)); auto fs = FileStream(_indexPath, FILE_MODE_WRITE); // Write header diff --git a/src/openrct2/core/Path.cpp b/src/openrct2/core/Path.cpp index 002d293190..a5849fc64d 100644 --- a/src/openrct2/core/Path.cpp +++ b/src/openrct2/core/Path.cpp @@ -79,6 +79,11 @@ namespace Path return buffer; } + void CreateDirectory(const std::string &path) + { + platform_ensure_directory_exists(path.c_str()); + } + std::string GetFileName(const std::string &path) { return GetFileName(path.c_str()); diff --git a/src/openrct2/core/Path.hpp b/src/openrct2/core/Path.hpp index 4c055fb48d..e5c4292263 100644 --- a/src/openrct2/core/Path.hpp +++ b/src/openrct2/core/Path.hpp @@ -33,6 +33,7 @@ namespace Path std::string GetDirectory(const std::string &path); utf8 * GetDirectory(const utf8 * path); utf8 * GetDirectory(utf8 * buffer, size_t bufferSize, const utf8 * path); + void CreateDirectory(const std::string &path); std::string GetFileName(const std::string &path); const utf8 * GetFileName(const utf8 * path); std::string GetFileNameWithoutExtension(const std::string &path); diff --git a/src/openrct2/platform/Platform2.cpp b/src/openrct2/platform/Platform2.cpp index 758b00b90c..47bf8312d5 100644 --- a/src/openrct2/platform/Platform2.cpp +++ b/src/openrct2/platform/Platform2.cpp @@ -14,8 +14,18 @@ *****************************************************************************/ #pragma endregion -#include "Platform2.h" +#include +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + #include +#else + #include +#endif + +#include "../core/Path.hpp" +#include "../core/String.hpp" +#include "Platform2.h" #include "platform.h" namespace Platform @@ -24,4 +34,131 @@ namespace Platform { return platform_get_ticks(); } + + std::string GetEnvironmentVariable(const std::string &name) + { +#ifdef _WIN32 + auto wname = String::ToUtf16(name); + wchar_t wvalue[MAX_PATH]; + GetEnvironmentVariableW(wname.c_str(), wvalue, sizeof(wvalue)); + return String::ToUtf8(wvalue); +#else + return String::ToStd(getenv(name.c_str())); +#endif + } + + static std::string GetEnvironmentPath(const char * name) + { + auto value = getenv(name); + if (value == nullptr) + { + return std::string(); + } + else + { + auto colon = std::strchr(value, ':'); + if (colon == nullptr) + { + return std::string(value); + } + else + { + return std::string(value, colon); + } + } + } + +#ifdef _WIN32 + std::string GetFolderPath(SPECIAL_FOLDER folder) + { + switch (folder) + { + // We currently store everything under Documents/OpenRCT2 + case SPECIAL_FOLDER::USER_CACHE: + case SPECIAL_FOLDER::USER_CONFIG: + case SPECIAL_FOLDER::USER_DATA: + { + wchar_t wpath[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, nullptr, 0, wpath))) + { + return ToUtf8(std::wstring(wpath)); + } + else + { + return GetFolderPath(SPECIAL_FOLDER::USER_HOME); + } + } + case SPECIAL_FOLDER::USER_HOME: + { + wchar_t wpath[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_PROFILE | CSIDL_FLAG_CREATE, nullptr, 0, wpath))) + { + return ToUtf8(std::wstring(wpath)); + } + else + { + // Try default environment variables + auto homedrive = GetEnvironmentVariable("HOMEDRIVE"); + auto homepath = GetEnvironmentVariable("HOMEPATH"); + if (!homedrive.empty() && !homepath.empty()) + { + return Path::Combine(homedrive, homepath); + } + else + { + return "C:\\"; + } + } + } + } + + } +#else + std::string GetFolderPath(SPECIAL_FOLDER folder) + { + switch (folder) + { + case SPECIAL_FOLDER::USER_CACHE: + { + auto path = GetEnvironmentPath("XDG_CACHE_HOME"); + if (path.empty()) + { + auto home = GetFolderPath(SPECIAL_FOLDER::USER_HOME); + path = Path::Combine(home, ".cache"); + } + return path; + } + case SPECIAL_FOLDER::USER_CONFIG: + { + auto path = GetEnvironmentPath("XDG_CONFIG_HOME"); + if (path.empty()) + { + auto home = GetFolderPath(SPECIAL_FOLDER::USER_HOME); + path = Path::Combine(home, ".config"); + } + return path; + } + case SPECIAL_FOLDER::USER_DATA: + { + auto path = GetEnvironmentPath("XDG_DATA_HOME"); + if (path.empty()) + { + auto home = GetFolderPath(SPECIAL_FOLDER::USER_HOME); + path = Path::Combine(home, ".local/share"); + } + return path; + } + case SPECIAL_FOLDER::USER_HOME: + return getpwuid(getuid())->pw_dir; + } + } +#endif + + std::string GetInstallPath() + { + utf8 path[260]; + platform_resolve_openrct_data_path(); + platform_get_openrct_data_path(path, sizeof(path)); + return path; + } } diff --git a/src/openrct2/platform/Platform2.h b/src/openrct2/platform/Platform2.h index fce41f87b9..5e0dc72319 100644 --- a/src/openrct2/platform/Platform2.h +++ b/src/openrct2/platform/Platform2.h @@ -18,11 +18,23 @@ #ifdef __cplusplus +#include #include "../common.h" +enum class SPECIAL_FOLDER +{ + USER_CACHE, + USER_CONFIG, + USER_DATA, + USER_HOME, +}; + namespace Platform { uint32 GetTicks(); + std::string GetEnvironmentVariable(const std::string &name); + std::string GetFolderPath(SPECIAL_FOLDER folder); + std::string GetInstallPath(); } #endif