/***************************************************************************** * Copyright (c) 2014-2020 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 * * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ #if defined(__unix__) && !defined(__ANDROID__) && !defined(__APPLE__) # include # include # include # include # include # if defined(__FreeBSD__) || defined(__NetBSD__) # include # include # include # endif // __FreeBSD__ || __NetBSD__ # if defined(__linux__) // for PATH_MAX # include # endif // __linux__ # include "../OpenRCT2.h" # include "../core/Path.hpp" # include "../localisation/Language.h" # include "Platform2.h" # include "platform.h" namespace Platform { std::string GetFolderPath(SPECIAL_FOLDER folder) { switch (folder) { case SPECIAL_FOLDER::USER_CACHE: case SPECIAL_FOLDER::USER_CONFIG: case SPECIAL_FOLDER::USER_DATA: { 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_HOME: return GetHomePath(); default: return std::string(); } } std::string GetDocsPath() { static const utf8* searchLocations[] = { "./doc", "/usr/share/doc/openrct2", }; for (auto searchLocation : searchLocations) { log_verbose("Looking for OpenRCT2 doc path at %s", searchLocation); if (Path::DirectoryExists(searchLocation)) { return searchLocation; } } return std::string(); } static std::string GetCurrentWorkingDirectory() { char cwdPath[PATH_MAX]; if (getcwd(cwdPath, sizeof(cwdPath)) != nullptr) { return cwdPath; } return std::string(); } std::string GetInstallPath() { // 1. Try command line argument auto path = std::string(gCustomOpenRCT2DataPath); if (!path.empty()) { return Path::GetAbsolute(path); } // 2. Try {${exeDir},${cwd},/}/{data,standard system app directories} // exeDir should come first to allow installing into build dir std::vector prefixes; auto exePath = Platform::GetCurrentExecutablePath(); prefixes.push_back(Path::GetDirectory(exePath)); prefixes.push_back(GetCurrentWorkingDirectory()); prefixes.push_back("/"); static const char* SearchLocations[] = { "/data", "../share/openrct2", # ifdef ORCT2_RESOURCE_DIR // defined in CMakeLists.txt ORCT2_RESOURCE_DIR, # endif // ORCT2_RESOURCE_DIR "/usr/local/share/openrct2", "/var/lib/openrct2", "/usr/share/openrct2", }; for (const auto& prefix : prefixes) { for (auto searchLocation : SearchLocations) { auto prefixedPath = Path::Combine(prefix, searchLocation); log_verbose("Looking for OpenRCT2 data in %s", prefixedPath.c_str()); if (Path::DirectoryExists(prefixedPath)) { return prefixedPath; } } } return "/"; } std::string GetCurrentExecutablePath() { char exePath[PATH_MAX] = { 0 }; # ifdef __linux__ auto bytesRead = readlink("/proc/self/exe", exePath, sizeof(exePath)); if (bytesRead == -1) { log_fatal("failed to read /proc/self/exe"); } # elif defined(__FreeBSD__) || defined(__NetBSD__) # if defined(__FreeBSD__) const int32_t mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1, }; # else const int32_t mib[] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME, }; # endif auto exeLen = sizeof(exePath); if (sysctl(mib, 4, exePath, &exeLen, nullptr, 0) == -1) { log_fatal("failed to get process path"); } # elif defined(__OpenBSD__) // There is no way to get the path name of a running executable. // If you are not using the port or package, you may have to change this line! strlcpy(exePath, "/usr/local/bin/", sizeof(exePath)); # else # error "Platform does not support full path exe retrieval" # endif return exePath; } utf8* StrDecompToPrecomp(utf8* input) { return input; } bool HandleSpecialCommandLineArgument(const char* argument) { return false; } uint16_t GetLocaleLanguage() { const char* langString = setlocale(LC_MESSAGES, ""); if (langString != nullptr) { // The locale has the following form: // language[_territory[.codeset]][@modifier] // (see https://www.gnu.org/software/libc/manual/html_node/Locale-Names.html) // longest on my system is 29 with codeset and modifier, so 32 for the pattern should be more than enough char pattern[32]; // strip the codeset and modifier part int32_t length = strlen(langString); { for (int32_t i = 0; i < length; ++i) { if (langString[i] == '.' || langString[i] == '@') { length = i; break; } } } // end strip std::memcpy(pattern, langString, length); // copy all until first '.' or '@' pattern[length] = '\0'; // find _ if present const char* strip = strchr(pattern, '_'); if (strip != nullptr) { // could also use '-', but '?' is more flexible. Maybe LanguagesDescriptors will change. // pattern is now "language?territory" pattern[strip - pattern] = '?'; } // Iterate through all available languages for (int32_t i = 1; i < LANGUAGE_COUNT; ++i) { if (!fnmatch(pattern, LanguagesDescriptors[i].locale, 0)) { return i; } } // special case if (fnmatch(pattern, "en_CA", 0) == 0) { return LANGUAGE_ENGLISH_US; } // no exact match found trying only language part if (strip != nullptr) { pattern[strip - pattern] = '*'; pattern[strip - pattern + 1] = '\0'; // pattern is now "language*" for (int32_t i = 1; i < LANGUAGE_COUNT; ++i) { if (!fnmatch(pattern, LanguagesDescriptors[i].locale, 0)) { return i; } } } } return LANGUAGE_ENGLISH_UK; } CurrencyType GetLocaleCurrency() { char* langstring = setlocale(LC_MONETARY, ""); if (langstring == nullptr) { return Platform::GetCurrencyValue(NULL); } struct lconv* lc = localeconv(); return Platform::GetCurrencyValue(lc->int_curr_symbol); } } // namespace Platform #endif