2017-06-01 21:55:10 +02:00
|
|
|
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
|
2015-11-27 18:01:22 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
|
|
|
*
|
2016-05-04 19:24:41 +02:00
|
|
|
* 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
|
2015-11-27 18:01:22 +01:00
|
|
|
*
|
|
|
|
* 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.
|
2016-05-04 19:24:41 +02:00
|
|
|
*
|
|
|
|
* A full copy of the GNU General Public License can be found in licence.txt
|
2015-11-27 18:01:22 +01:00
|
|
|
*****************************************************************************/
|
2016-05-04 19:24:41 +02:00
|
|
|
#pragma endregion
|
2015-11-27 18:01:22 +01:00
|
|
|
|
2016-12-28 14:18:04 +01:00
|
|
|
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD__)
|
2015-11-27 18:01:22 +01:00
|
|
|
|
2017-06-12 00:43:28 +02:00
|
|
|
#include <ctype.h>
|
2016-01-10 22:30:42 +01:00
|
|
|
#include <dirent.h>
|
|
|
|
#include <errno.h>
|
2017-09-03 14:29:23 +02:00
|
|
|
#include <fcntl.h>
|
2016-01-10 22:30:42 +01:00
|
|
|
#include <fnmatch.h>
|
2017-09-04 23:18:16 +02:00
|
|
|
#ifndef __EMSCRIPTEN__
|
|
|
|
#include <fts.h>
|
|
|
|
#endif
|
2015-11-27 18:01:22 +01:00
|
|
|
#include <libgen.h>
|
2016-01-10 22:30:42 +01:00
|
|
|
#include <locale.h>
|
2017-06-12 00:43:28 +02:00
|
|
|
#include <pwd.h>
|
2017-10-24 18:36:40 +02:00
|
|
|
#include <stdlib.h>
|
2017-06-12 00:43:28 +02:00
|
|
|
#include <sys/file.h>
|
2015-11-27 18:01:22 +01:00
|
|
|
#include <sys/stat.h>
|
2016-01-10 22:30:42 +01:00
|
|
|
#include <sys/time.h>
|
|
|
|
#include <time.h>
|
2017-07-29 16:21:33 +02:00
|
|
|
#include <unistd.h>
|
2017-02-18 16:45:10 +01:00
|
|
|
#include "../config/Config.h"
|
2018-01-06 18:32:25 +01:00
|
|
|
#include "../localisation/Date.h"
|
|
|
|
#include "../localisation/Language.h"
|
2016-12-11 01:12:29 +01:00
|
|
|
#include "../OpenRCT2.h"
|
2017-12-13 13:02:24 +01:00
|
|
|
#include "../util/Util.h"
|
2016-01-10 22:30:42 +01:00
|
|
|
#include "platform.h"
|
2015-11-27 18:01:22 +01:00
|
|
|
// The name of the mutex used to prevent multiple instances of the game from running
|
2016-01-06 11:56:57 +01:00
|
|
|
#define SINGLE_INSTANCE_MUTEX_NAME "openrct2.lock"
|
2015-11-27 18:01:22 +01:00
|
|
|
|
2016-01-05 11:20:52 +01:00
|
|
|
#define FILE_BUFFER_SIZE 4096
|
|
|
|
|
2017-10-09 17:13:14 +02:00
|
|
|
static utf8 _userDataDirectoryPath[MAX_PATH] = { 0 };
|
|
|
|
static utf8 _openrctDataDirectoryPath[MAX_PATH] = { 0 };
|
2015-11-27 18:01:22 +01:00
|
|
|
|
2016-07-23 23:25:13 +02:00
|
|
|
void platform_get_date_utc(rct2_date *out_date)
|
2015-11-27 18:01:22 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
assert(out_date != NULL);
|
|
|
|
time_t rawtime;
|
|
|
|
struct tm * timeinfo;
|
|
|
|
time(&rawtime);
|
|
|
|
timeinfo = gmtime(&rawtime);
|
|
|
|
out_date->day = timeinfo->tm_mday;
|
|
|
|
out_date->month = timeinfo->tm_mon + 1;
|
|
|
|
out_date->year = timeinfo->tm_year + 1900;
|
|
|
|
out_date->day_of_week = timeinfo->tm_wday;
|
2015-11-27 18:01:22 +01:00
|
|
|
}
|
|
|
|
|
2016-07-23 23:25:13 +02:00
|
|
|
void platform_get_time_utc(rct2_time *out_time)
|
2015-11-27 18:01:22 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
assert(out_time != NULL);
|
|
|
|
time_t rawtime;
|
|
|
|
struct tm * timeinfo;
|
|
|
|
time(&rawtime);
|
|
|
|
timeinfo = gmtime(&rawtime);
|
|
|
|
out_time->second = timeinfo->tm_sec;
|
|
|
|
out_time->minute = timeinfo->tm_min;
|
|
|
|
out_time->hour = timeinfo->tm_hour;
|
2015-11-27 18:01:22 +01:00
|
|
|
}
|
|
|
|
|
2016-07-23 23:25:13 +02:00
|
|
|
void platform_get_date_local(rct2_date *out_date)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
assert(out_date != NULL);
|
|
|
|
time_t rawtime;
|
|
|
|
struct tm * timeinfo;
|
|
|
|
time(&rawtime);
|
|
|
|
timeinfo = localtime(&rawtime);
|
|
|
|
out_date->day = timeinfo->tm_mday;
|
|
|
|
out_date->month = timeinfo->tm_mon + 1;
|
|
|
|
out_date->year = timeinfo->tm_year + 1900;
|
|
|
|
out_date->day_of_week = timeinfo->tm_wday;
|
2016-07-23 23:25:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void platform_get_time_local(rct2_time *out_time)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
assert(out_time != NULL);
|
|
|
|
time_t rawtime;
|
|
|
|
struct tm * timeinfo;
|
|
|
|
time(&rawtime);
|
|
|
|
timeinfo = localtime(&rawtime);
|
|
|
|
out_time->second = timeinfo->tm_sec;
|
|
|
|
out_time->minute = timeinfo->tm_min;
|
|
|
|
out_time->hour = timeinfo->tm_hour;
|
2016-07-23 23:25:13 +02:00
|
|
|
}
|
|
|
|
|
2016-10-10 15:13:51 +02:00
|
|
|
static size_t platform_utf8_to_multibyte(const utf8 *path, char *buffer, size_t buffer_size)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
wchar_t *wpath = utf8_to_widechar(path);
|
|
|
|
setlocale(LC_CTYPE, "UTF-8");
|
|
|
|
size_t len = wcstombs(NULL, wpath, 0);
|
|
|
|
bool truncated = false;
|
|
|
|
if (len > buffer_size - 1) {
|
|
|
|
truncated = true;
|
|
|
|
len = buffer_size - 1;
|
|
|
|
}
|
|
|
|
wcstombs(buffer, wpath, len);
|
|
|
|
buffer[len] = '\0';
|
|
|
|
if (truncated)
|
|
|
|
log_warning("truncated string %s", buffer);
|
|
|
|
free(wpath);
|
|
|
|
return len;
|
2016-10-10 15:13:51 +02:00
|
|
|
}
|
|
|
|
|
2015-11-27 18:01:22 +01:00
|
|
|
bool platform_file_exists(const utf8 *path)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
char buffer[MAX_PATH];
|
|
|
|
platform_utf8_to_multibyte(path, buffer, MAX_PATH);
|
|
|
|
bool exists = access(buffer, F_OK) != -1;
|
|
|
|
log_verbose("file '%s' exists = %i", buffer, exists);
|
|
|
|
return exists;
|
2015-11-27 18:01:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool platform_directory_exists(const utf8 *path)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
char buffer[MAX_PATH];
|
|
|
|
platform_utf8_to_multibyte(path, buffer, MAX_PATH);
|
|
|
|
struct stat dirinfo;
|
|
|
|
sint32 result = stat(buffer, &dirinfo);
|
|
|
|
log_verbose("checking dir %s, result = %d, is_dir = %d", buffer, result, S_ISDIR(dirinfo.st_mode));
|
|
|
|
if ((result != 0) || !S_ISDIR(dirinfo.st_mode))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2015-11-27 18:01:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool platform_original_game_data_exists(const utf8 *path)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
char buffer[MAX_PATH];
|
|
|
|
platform_utf8_to_multibyte(path, buffer, MAX_PATH);
|
|
|
|
char checkPath[MAX_PATH];
|
|
|
|
safe_strcpy(checkPath, buffer, MAX_PATH);
|
|
|
|
safe_strcat_path(checkPath, "Data", MAX_PATH);
|
|
|
|
safe_strcat_path(checkPath, "g1.dat", MAX_PATH);
|
|
|
|
return platform_file_exists(checkPath);
|
2015-11-27 18:01:22 +01:00
|
|
|
}
|
|
|
|
|
2016-07-13 00:46:51 +02:00
|
|
|
static mode_t getumask()
|
2015-11-27 18:01:22 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
mode_t mask = umask(0);
|
|
|
|
umask(mask);
|
|
|
|
return 0777 & ~mask; // Keep in mind 0777 is octal
|
2015-11-27 18:01:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool platform_ensure_directory_exists(const utf8 *path)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
mode_t mask = getumask();
|
|
|
|
char buffer[MAX_PATH];
|
|
|
|
platform_utf8_to_multibyte(path, buffer, MAX_PATH);
|
|
|
|
|
|
|
|
log_verbose("Create directory: %s", buffer);
|
|
|
|
for (char *p = buffer + 1; *p != '\0'; p++) {
|
|
|
|
if (*p == '/') {
|
|
|
|
// Temporarily truncate
|
|
|
|
*p = '\0';
|
|
|
|
|
|
|
|
log_verbose("mkdir(%s)", buffer);
|
|
|
|
if (mkdir(buffer, mask) != 0) {
|
|
|
|
if (errno != EEXIST) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Restore truncation
|
|
|
|
*p = '/';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
log_verbose("mkdir(%s)", buffer);
|
|
|
|
if (mkdir(buffer, mask) != 0) {
|
|
|
|
if (errno != EEXIST) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2015-11-27 18:01:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool platform_directory_delete(const utf8 *path)
|
|
|
|
{
|
2017-09-04 23:18:16 +02:00
|
|
|
#ifdef _FTS_H
|
2017-06-06 23:24:18 +02:00
|
|
|
log_verbose("Recursively deleting directory %s", path);
|
|
|
|
|
|
|
|
FTS *ftsp;
|
|
|
|
FTSENT *p, *chp;
|
|
|
|
|
|
|
|
// fts_open only accepts non const paths, so we have to take a copy
|
|
|
|
char* ourPath = _strdup(path);
|
|
|
|
|
|
|
|
utf8* const patharray[2] = {ourPath, NULL};
|
|
|
|
if ((ftsp = fts_open(patharray, FTS_COMFOLLOW | FTS_LOGICAL | FTS_NOCHDIR, NULL)) == NULL) {
|
|
|
|
log_error("fts_open returned NULL");
|
|
|
|
free(ourPath);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
chp = fts_children(ftsp, 0);
|
|
|
|
if (chp == NULL) {
|
|
|
|
log_verbose("No files to traverse, deleting directory %s", path);
|
|
|
|
if (remove(path) != 0)
|
|
|
|
{
|
|
|
|
log_error("Failed to remove %s, errno = %d", path, errno);
|
|
|
|
}
|
|
|
|
free(ourPath);
|
|
|
|
return true; // No files to traverse
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((p = fts_read(ftsp)) != NULL) {
|
|
|
|
switch (p->fts_info) {
|
|
|
|
case FTS_DP: // Directory postorder, which means
|
|
|
|
// the directory is empty
|
|
|
|
|
|
|
|
case FTS_F: // File
|
|
|
|
if(remove(p->fts_path)) {
|
|
|
|
log_error("Could not remove %s", p->fts_path);
|
|
|
|
fts_close(ftsp);
|
|
|
|
free(ourPath);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FTS_ERR:
|
|
|
|
log_error("Error traversing %s", path);
|
|
|
|
fts_close(ftsp);
|
|
|
|
free(ourPath);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(ourPath);
|
|
|
|
fts_close(ftsp);
|
|
|
|
|
2017-09-04 23:18:16 +02:00
|
|
|
#else
|
|
|
|
log_warning("OpenRCT2 was compiled without fts.h, deleting '%s' not done.", path);
|
|
|
|
#endif // _FTS_H
|
2017-06-06 23:24:18 +02:00
|
|
|
return true;
|
2015-11-27 18:01:22 +01:00
|
|
|
}
|
|
|
|
|
2017-05-07 20:17:11 +02:00
|
|
|
utf8 * platform_get_absolute_path(const utf8 * relative_path, const utf8 * base_path)
|
2017-01-14 13:39:38 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
utf8 path[MAX_PATH];
|
|
|
|
|
|
|
|
if (base_path != NULL)
|
|
|
|
{
|
|
|
|
snprintf(path, MAX_PATH, "%s/%s", base_path, relative_path);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
safe_strcpy(path, base_path, MAX_PATH);
|
|
|
|
}
|
|
|
|
return realpath(path,NULL);
|
2017-01-14 13:39:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-11-27 18:01:22 +01:00
|
|
|
bool platform_lock_single_instance()
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
char pidFilePath[MAX_PATH];
|
|
|
|
|
|
|
|
safe_strcpy(pidFilePath, _userDataDirectoryPath, sizeof(pidFilePath));
|
|
|
|
safe_strcat_path(pidFilePath, SINGLE_INSTANCE_MUTEX_NAME, sizeof(pidFilePath));
|
|
|
|
|
|
|
|
// We will never close this file manually. The operating system will
|
|
|
|
// take care of that, because flock keeps the lock as long as the
|
|
|
|
// file is open and closes it automatically on file close.
|
|
|
|
// This is intentional.
|
|
|
|
sint32 pidFile = open(pidFilePath, O_CREAT | O_RDWR, 0666);
|
|
|
|
|
|
|
|
if (pidFile == -1) {
|
|
|
|
log_warning("Cannot open lock file for writing.");
|
|
|
|
return false;
|
|
|
|
}
|
2017-11-06 23:53:59 +01:00
|
|
|
|
2017-11-06 23:58:06 +01:00
|
|
|
struct flock lock;
|
2017-11-06 23:53:59 +01:00
|
|
|
|
2017-11-06 23:58:06 +01:00
|
|
|
lock.l_start = 0;
|
|
|
|
lock.l_len = 0;
|
|
|
|
lock.l_type = F_WRLCK;
|
|
|
|
lock.l_whence = SEEK_SET;
|
2017-11-06 23:53:59 +01:00
|
|
|
|
2017-11-07 09:15:22 +01:00
|
|
|
if (fcntl(pidFile, F_SETLK, &lock) == -1)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
if (errno == EWOULDBLOCK) {
|
|
|
|
log_warning("Another OpenRCT2 session has been found running.");
|
2017-11-07 09:15:22 +01:00
|
|
|
return false;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2017-11-06 23:58:06 +01:00
|
|
|
log_error("flock returned an uncatched errno: %d", errno);
|
|
|
|
return false;
|
2017-11-07 04:49:02 +01:00
|
|
|
}
|
2017-11-07 05:19:43 +01:00
|
|
|
return true;
|
|
|
|
}
|
2015-11-27 18:01:22 +01:00
|
|
|
|
2018-01-06 20:09:04 +01:00
|
|
|
sint32 platform_get_drives() {
|
2017-06-06 23:24:18 +02:00
|
|
|
// POSIX systems do not know drives. Return 0.
|
|
|
|
return 0;
|
2015-11-27 18:01:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool platform_file_copy(const utf8 *srcPath, const utf8 *dstPath, bool overwrite)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
log_verbose("Copying %s to %s", srcPath, dstPath);
|
|
|
|
|
|
|
|
FILE *dstFile;
|
|
|
|
|
|
|
|
if (overwrite) {
|
|
|
|
dstFile = fopen(dstPath, "wb");
|
|
|
|
} else {
|
|
|
|
// Portability note: check your libc's support for "wbx"
|
|
|
|
dstFile = fopen(dstPath, "wbx");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dstFile == NULL) {
|
|
|
|
if (errno == EEXIST) {
|
|
|
|
log_warning("platform_file_copy: Not overwriting %s, because overwrite flag == false", dstPath);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_error("Could not open destination file %s for copying", dstPath);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open both files and check whether they are opened correctly
|
|
|
|
FILE *srcFile = fopen(srcPath, "rb");
|
|
|
|
if (srcFile == NULL) {
|
|
|
|
fclose(dstFile);
|
|
|
|
log_error("Could not open source file %s for copying", srcPath);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t amount_read = 0;
|
|
|
|
size_t file_offset = 0;
|
|
|
|
|
|
|
|
// Copy file in FILE_BUFFER_SIZE-d chunks
|
|
|
|
char* buffer = (char*) malloc(FILE_BUFFER_SIZE);
|
|
|
|
while ((amount_read = fread(buffer, FILE_BUFFER_SIZE, 1, srcFile))) {
|
|
|
|
fwrite(buffer, amount_read, 1, dstFile);
|
|
|
|
file_offset += amount_read;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finish the left-over data from file, which may not be a full
|
|
|
|
// FILE_BUFFER_SIZE-d chunk.
|
|
|
|
fseek(srcFile, file_offset, SEEK_SET);
|
|
|
|
amount_read = fread(buffer, 1, FILE_BUFFER_SIZE, srcFile);
|
|
|
|
fwrite(buffer, amount_read, 1, dstFile);
|
|
|
|
|
|
|
|
fclose(srcFile);
|
|
|
|
fclose(dstFile);
|
|
|
|
free(buffer);
|
|
|
|
|
|
|
|
return true;
|
2015-11-27 18:01:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool platform_file_move(const utf8 *srcPath, const utf8 *dstPath)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return rename(srcPath, dstPath) == 0;
|
2015-11-27 18:01:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool platform_file_delete(const utf8 *path)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
sint32 ret = unlink(path);
|
|
|
|
return ret == 0;
|
2015-11-27 18:01:22 +01:00
|
|
|
}
|
|
|
|
|
2016-07-13 00:46:51 +02:00
|
|
|
static wchar_t *regular_to_wchar(const char* src)
|
2015-11-27 18:01:22 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
sint32 len = strnlen(src, MAX_PATH);
|
2018-01-18 15:04:31 +01:00
|
|
|
wchar_t * w_buffer = (wchar_t *)malloc((len + 1) * sizeof(wchar_t));
|
2017-06-06 23:24:18 +02:00
|
|
|
mbtowc (NULL, NULL, 0); /* reset mbtowc */
|
|
|
|
|
|
|
|
sint32 max = len;
|
|
|
|
sint32 i = 0;
|
|
|
|
while (max > 0)
|
|
|
|
{
|
|
|
|
sint32 length;
|
|
|
|
length = mbtowc(&w_buffer[i], &src[i], max);
|
|
|
|
if (length < 1)
|
|
|
|
{
|
|
|
|
w_buffer[i + 1] = '\0';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i += length;
|
|
|
|
max -= length;
|
|
|
|
}
|
|
|
|
return w_buffer;
|
2015-11-27 18:01:22 +01:00
|
|
|
}
|
|
|
|
|
2016-09-26 04:24:29 +02:00
|
|
|
void platform_get_openrct_data_path(utf8 *outPath, size_t outSize)
|
2015-12-17 22:49:39 +01:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
safe_strcpy(outPath, _openrctDataDirectoryPath, outSize);
|
2015-12-17 22:49:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Default directory fallback is:
|
|
|
|
* - (command line argument)
|
2015-12-18 22:59:41 +01:00
|
|
|
* - <exePath>/data
|
2015-12-17 22:49:39 +01:00
|
|
|
* - <platform dependent>
|
|
|
|
*/
|
|
|
|
void platform_resolve_openrct_data_path()
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
if (gCustomOpenrctDataPath[0] != 0) {
|
|
|
|
// NOTE: second argument to `realpath` is meant to either be NULL or `PATH_MAX`-sized buffer,
|
|
|
|
// since our `MAX_PATH` macro is set to some other value, pass NULL to have `realpath` return
|
|
|
|
// a `malloc`ed buffer.
|
|
|
|
char *resolved_path = realpath(gCustomOpenrctDataPath, NULL);
|
|
|
|
if (resolved_path == NULL) {
|
|
|
|
log_error("Could not resolve path \"%s\", errno = %d", gCustomOpenrctDataPath, errno);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
safe_strcpy(_openrctDataDirectoryPath, resolved_path, MAX_PATH);
|
|
|
|
free(resolved_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
path_end_with_separator(_openrctDataDirectoryPath, MAX_PATH);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
char buffer[MAX_PATH];
|
|
|
|
platform_get_exe_path(buffer, sizeof(buffer));
|
|
|
|
|
|
|
|
safe_strcat_path(buffer, "data", MAX_PATH);
|
|
|
|
log_verbose("Looking for OpenRCT2 data in %s", buffer);
|
|
|
|
if (platform_directory_exists(buffer))
|
|
|
|
{
|
|
|
|
_openrctDataDirectoryPath[0] = '\0';
|
|
|
|
safe_strcpy(_openrctDataDirectoryPath, buffer, MAX_PATH);
|
|
|
|
log_verbose("Found OpenRCT2 data in %s", _openrctDataDirectoryPath);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
platform_posix_sub_resolve_openrct_data_path(_openrctDataDirectoryPath, sizeof(_openrctDataDirectoryPath));
|
|
|
|
log_verbose("Trying to use OpenRCT2 data in %s", _openrctDataDirectoryPath);
|
2015-12-17 22:49:39 +01:00
|
|
|
}
|
|
|
|
|
2015-11-27 18:01:22 +01:00
|
|
|
time_t platform_file_get_modified_time(const utf8* path){
|
2017-06-06 23:24:18 +02:00
|
|
|
struct stat buf;
|
|
|
|
if (stat(path, &buf) == 0) {
|
|
|
|
return buf.st_mtime;
|
|
|
|
}
|
|
|
|
return 100;
|
2015-11-27 18:01:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8 platform_get_locale_temperature_format(){
|
2017-05-23 09:30:13 +02:00
|
|
|
// LC_MEASUREMENT is GNU specific.
|
|
|
|
#ifdef LC_MEASUREMENT
|
2017-06-06 23:24:18 +02:00
|
|
|
const char *langstring = setlocale(LC_MEASUREMENT, "");
|
2017-05-23 09:30:13 +02:00
|
|
|
#else
|
2017-06-06 23:24:18 +02:00
|
|
|
const char *langstring = setlocale(LC_ALL, "");
|
2017-05-23 09:30:13 +02:00
|
|
|
#endif
|
2015-11-20 17:29:36 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
if(langstring != NULL){
|
|
|
|
if (!fnmatch("*_US*", langstring, 0) ||
|
|
|
|
!fnmatch("*_BS*", langstring, 0) ||
|
|
|
|
!fnmatch("*_BZ*", langstring, 0) ||
|
|
|
|
!fnmatch("*_PW*", langstring, 0))
|
|
|
|
{
|
|
|
|
return TEMPERATURE_FORMAT_F;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TEMPERATURE_FORMAT_C;
|
2015-11-27 18:01:22 +01:00
|
|
|
}
|
|
|
|
|
2017-04-09 04:50:34 +02:00
|
|
|
uint8 platform_get_locale_date_format()
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
return DATE_FORMAT_DAY_MONTH_YEAR;
|
2017-04-09 04:50:34 +02:00
|
|
|
}
|
|
|
|
|
2016-01-03 02:48:52 +01:00
|
|
|
datetime64 platform_get_datetime_now_utc()
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
const datetime64 epochAsTicks = 621355968000000000;
|
2016-01-03 02:48:52 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
struct timeval tv;
|
|
|
|
gettimeofday(&tv, NULL);
|
2016-01-03 02:48:52 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
// Epoch starts from: 1970-01-01T00:00:00Z
|
|
|
|
// Convert to ticks from 0001-01-01T00:00:00Z
|
|
|
|
uint64 utcEpochTicks = (uint64)tv.tv_sec * 10000000ULL + tv.tv_usec * 10;
|
|
|
|
datetime64 utcNow = epochAsTicks + utcEpochTicks;
|
|
|
|
return utcNow;
|
2016-01-03 02:48:52 +01:00
|
|
|
}
|
|
|
|
|
2016-02-10 21:49:10 +01:00
|
|
|
utf8* platform_get_username() {
|
2017-06-06 23:24:18 +02:00
|
|
|
struct passwd* pw = getpwuid(getuid());
|
2016-02-10 21:49:10 +01:00
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
if (pw) {
|
|
|
|
return pw->pw_name;
|
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-02-10 21:49:10 +01:00
|
|
|
}
|
|
|
|
|
2017-07-29 16:21:33 +02:00
|
|
|
bool platform_process_is_elevated()
|
|
|
|
{
|
2017-09-04 23:19:11 +02:00
|
|
|
#ifndef __EMSCRIPTEN__
|
2017-07-29 16:21:33 +02:00
|
|
|
return (geteuid() == 0);
|
2017-09-04 23:19:11 +02:00
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif // __EMSCRIPTEN__
|
2017-07-29 16:21:33 +02:00
|
|
|
}
|
|
|
|
|
2015-11-27 18:01:22 +01:00
|
|
|
#endif
|