diff --git a/src/cmdline.c b/src/cmdline.c index d0164f7cc9..2d141c2c92 100644 --- a/src/cmdline.c +++ b/src/cmdline.c @@ -76,6 +76,7 @@ int cmdline_run(const char **argv, int argc) int version = 0, headless = 0, verbose = 0, width = 0, height = 0, port = 0; char *server = NULL; char *userDataPath = NULL; + char *openrctDataPath = NULL; argparse_option_t options[] = { OPT_HELP(), @@ -84,8 +85,9 @@ int cmdline_run(const char **argv, int argc) OPT_BOOLEAN(0, "verbose", &verbose, "log verbose messages"), OPT_INTEGER('m', "mode", &sprite_mode, "the type of sprite conversion. 0 = default, 1 = simple closest pixel match, 2 = dithering"), OPT_STRING(0, "server", &server, "server to connect to"), - OPT_INTEGER(0, "port", &port, "port"), + OPT_INTEGER(0, "port", &port, "Port to use. If used with --server, it will connect to specified server at this port, otherwise it will start the server"), OPT_STRING(0, "user-data-path", &userDataPath, "path to the user data directory (containing config.ini)"), + OPT_STRING(0, "openrct-data-path", &openrctDataPath, "path to the OpenRCT2 data directory (containing languages)"), OPT_END() }; @@ -117,6 +119,10 @@ int cmdline_run(const char **argv, int argc) safe_strncpy(gCustomUserDataPath, userDataPath, sizeof(gCustomUserDataPath)); } + if (openrctDataPath != NULL) { + safe_strncpy(gCustomOpenrctDataPath, openrctDataPath, sizeof(gCustomOpenrctDataPath)); + } + #ifndef DISABLE_NETWORK if (port != 0) { gNetworkStart = NETWORK_MODE_SERVER; diff --git a/src/config.c b/src/config.c index ee8a140678..3db493267e 100644 --- a/src/config.c +++ b/src/config.c @@ -1360,6 +1360,7 @@ static void title_sequence_open(const char *path, const char *customName); void title_sequences_set_default() { char path[MAX_PATH]; + char dataPath[MAX_PATH]; char sep = platform_get_path_separator(); platform_get_user_directory(path, "title sequences"); @@ -1368,12 +1369,14 @@ void title_sequences_set_default() gConfigTitleSequences.presets = malloc(0); gConfigTitleSequences.num_presets = 0; + platform_get_openrct_data_path(dataPath); + // Load OpenRCT2 title sequence - sprintf(path, "%s%c%s%c%s%c%s%c", gExePath, sep, "data", sep, "title", sep, "rct2", sep); + sprintf(path, "%s%c%s%c%s%c", dataPath, sep, "title", sep, "rct2", sep); title_sequence_open(path, language_get_string(5308)); // Load OpenRCT2 title sequence - sprintf(path, "%s%c%s%c%s%c%s%c", gExePath, sep, "data", sep, "title", sep, "openrct2", sep); + sprintf(path, "%s%c%s%c%s%c", dataPath, sep, "title", sep, "openrct2", sep); title_sequence_open(path, language_get_string(5309)); } diff --git a/src/drawing/sprite.c b/src/drawing/sprite.c index aaa09f1cd2..7fc3f5c585 100644 --- a/src/drawing/sprite.c +++ b/src/drawing/sprite.c @@ -82,7 +82,10 @@ int gfx_load_g2() unsigned int i; char path[MAX_PATH]; - sprintf(path, "%s%cdata%cg2.dat", gExePath, platform_get_path_separator(), platform_get_path_separator()); + char dataPath[MAX_PATH]; + + platform_get_openrct_data_path(dataPath); + sprintf(path, "%s%cg2.dat", dataPath, platform_get_path_separator()); file = SDL_RWFromFile(path, "rb"); if (file != NULL) { if (SDL_RWread(file, &g2.header, 8, 1) == 1) { diff --git a/src/localisation/language.cpp b/src/localisation/language.cpp index a14f9a70fe..33adeb6e83 100644 --- a/src/localisation/language.cpp +++ b/src/localisation/language.cpp @@ -146,19 +146,21 @@ const char *language_get_string(rct_string_id id) int language_open(int id) { - static const char *languagePath = "%s/data/language/%s.txt"; + static const char *languagePath = "%s/language/%s.txt"; char filename[MAX_PATH]; + char dataPath[MAX_PATH]; language_close_all(); if (id == LANGUAGE_UNDEFINED) return 1; + platform_get_openrct_data_path(dataPath); if (id != LANGUAGE_ENGLISH_UK) { - sprintf(filename, languagePath, gExePath, LanguagesDescriptors[LANGUAGE_ENGLISH_UK].path); + sprintf(filename, languagePath, dataPath, LanguagesDescriptors[LANGUAGE_ENGLISH_UK].path); _languageFallback = LanguagePack::FromFile(LANGUAGE_ENGLISH_UK, filename); } - sprintf(filename, languagePath, gExePath, LanguagesDescriptors[id].path); + sprintf(filename, languagePath, dataPath, LanguagesDescriptors[id].path); _languageCurrent = LanguagePack::FromFile(id, filename); if (_languageCurrent != nullptr) { gCurrentLanguage = id; diff --git a/src/openrct2.c b/src/openrct2.c index 6b1919d082..9af3275cfc 100644 --- a/src/openrct2.c +++ b/src/openrct2.c @@ -53,6 +53,7 @@ int gOpenRCT2StartupAction = STARTUP_ACTION_TITLE; utf8 gOpenRCT2StartupActionPath[512] = { 0 }; utf8 gExePath[MAX_PATH]; utf8 gCustomUserDataPath[MAX_PATH] = { 0 }; +utf8 gCustomOpenrctDataPath[MAX_PATH] = { 0 }; // This should probably be changed later and allow a custom selection of things to initialise like SDL_INIT bool gOpenRCT2Headless = false; @@ -148,7 +149,6 @@ static void openrct2_copy_files_over(const utf8 *originalDirectory, const utf8 * platform_enumerate_directories_end(fileEnumHandle); } -// TODO move to platform static void openrct2_set_exe_path() { platform_get_exe_path(gExePath); @@ -173,6 +173,7 @@ bool openrct2_initialise() { utf8 userPath[MAX_PATH]; + platform_resolve_openrct_data_path(); platform_resolve_user_data_path(); platform_get_user_directory(userPath, NULL); if (!platform_ensure_directory_exists(userPath)) { diff --git a/src/openrct2.h b/src/openrct2.h index 6d9853ad95..52cbc4d19e 100644 --- a/src/openrct2.h +++ b/src/openrct2.h @@ -34,6 +34,7 @@ extern int gOpenRCT2StartupAction; extern utf8 gOpenRCT2StartupActionPath[512]; extern utf8 gExePath[MAX_PATH]; extern utf8 gCustomUserDataPath[MAX_PATH]; +extern utf8 gCustomOpenrctDataPath[MAX_PATH]; extern bool gOpenRCT2Headless; extern bool gOpenRCT2ShowChangelog; diff --git a/src/platform/linux.c b/src/platform/linux.c index 8070466dfa..5bbb9a24b1 100644 --- a/src/platform/linux.c +++ b/src/platform/linux.c @@ -112,6 +112,29 @@ void platform_posix_sub_user_data_path(char *buffer, const char *homedir, const strncat(buffer, separator, MAX_PATH - strnlen(buffer, MAX_PATH) - 1); } +/** + * Default directory fallback is: + * - (command line argument) + * - /data + * - /var/lib/openrct2 + * - /usr/share/openrct2 + */ +void platform_posix_sub_resolve_openrct_data_path(utf8 *out) { + static const utf8 *searchLocations[] = { + "/var/lib/openrct2", + "/usr/share/openrct2", + }; + for (size_t i = 0; i < countof(searchLocations); i++) + { + if (platform_directory_exists(searchLocations[i])) + { + out[0] = '\0'; + safe_strncpy(out, searchLocations[i], MAX_PATH); + return; + } + } +} + utf8 *platform_open_directory_browser(utf8 *title) { STUB(); diff --git a/src/platform/osx.m b/src/platform/osx.m index 60b76132d8..1052cad4b8 100644 --- a/src/platform/osx.m +++ b/src/platform/osx.m @@ -21,6 +21,7 @@ #if defined(__APPLE__) && defined(__MACH__) @import AppKit; +@import Foundation; #include #include "platform.h" #include "../util/util.h" @@ -75,6 +76,29 @@ void platform_posix_sub_user_data_path(char *buffer, const char *homedir, const strncat(buffer, separator, MAX_PATH - strnlen(buffer, MAX_PATH) - 1); } +/** + * Default directory fallback is: + * - (command line argument) + * - /data + * - + */ +void platform_posix_sub_resolve_openrct_data_path(utf8 *out) { + @autoreleasepool + { + NSBundle *bundle = [NSBundle mainBundle]; + if (bundle) + { + const utf8 *resources = bundle.resourcePath.UTF8String; + if (platform_directory_exists(resources)) + { + out[0] = '\0'; + safe_strncpy(out, resources, MAX_PATH); + return; + } + } + } +} + void platform_show_messagebox(char *message) { @autoreleasepool diff --git a/src/platform/platform.h b/src/platform/platform.h index f0956231b3..0aeac6d853 100644 --- a/src/platform/platform.h +++ b/src/platform/platform.h @@ -147,6 +147,8 @@ void platform_get_cursor_position(int *x, int *y); void platform_set_cursor_position(int x, int y); unsigned int platform_get_ticks(); void platform_resolve_user_data_path(); +void platform_resolve_openrct_data_path(); +void platform_get_openrct_data_path(utf8 *outPath); void platform_get_user_directory(utf8 *outPath, const utf8 *subDirectory); void platform_show_messagebox(utf8 *message); int platform_open_common_file_dialog(int type, utf8 *title, utf8 *filename, utf8 *filterPattern, utf8 *filterName); diff --git a/src/platform/posix.c b/src/platform/posix.c index c1271e529b..522c9fb1c3 100644 --- a/src/platform/posix.c +++ b/src/platform/posix.c @@ -42,6 +42,7 @@ #define SINGLE_INSTANCE_MUTEX_NAME "RollerCoaster Tycoon 2_GSKMUTEX" utf8 _userDataDirectoryPath[MAX_PATH] = { 0 }; +utf8 _openrctDataDirectoryPath[MAX_PATH] = { 0 }; /** * The function that is called directly from the host application (rct2.exe)'s WinMain. This will be removed when OpenRCT2 can @@ -568,6 +569,49 @@ void platform_resolve_user_data_path() free(path); } +void platform_get_openrct_data_path(utf8 *outPath) +{ + safe_strncpy(outPath, _openrctDataDirectoryPath, sizeof(_openrctDataDirectoryPath)); +} + +void platform_posix_sub_resolve_openrct_data_path(utf8 *out); + +/** + * Default directory fallback is: + * - (command line argument) + * - /data + * - + */ +void platform_resolve_openrct_data_path() +{ + const char separator[2] = { platform_get_path_separator(), 0 }; + + if (gCustomOpenrctDataPath[0] != 0) { + realpath(gCustomOpenrctDataPath, _openrctDataDirectoryPath); + + // Ensure path ends with separator + int len = strlen(_openrctDataDirectoryPath); + if (_openrctDataDirectoryPath[len - 1] != separator[0]) { + strncat(_openrctDataDirectoryPath, separator, MAX_PATH - 1); + } + return; + } + + char buffer[MAX_PATH] = { 0 }; + platform_get_exe_path(buffer); + + strncat(buffer, separator, MAX_PATH - strnlen(buffer, MAX_PATH) - 1); + strncat(buffer, "data", MAX_PATH - strnlen(buffer, MAX_PATH) - 1); + if (platform_directory_exists(buffer)) + { + _openrctDataDirectoryPath[0] = '\0'; + safe_strncpy(_openrctDataDirectoryPath, buffer, MAX_PATH); + return; + } + + platform_posix_sub_resolve_openrct_data_path(_openrctDataDirectoryPath); +} + void platform_get_user_directory(utf8 *outPath, const utf8 *subDirectory) { const char separator[2] = { platform_get_path_separator(), 0 }; diff --git a/src/platform/windows.c b/src/platform/windows.c index d31e058236..d07f765fcd 100644 --- a/src/platform/windows.c +++ b/src/platform/windows.c @@ -38,6 +38,7 @@ #define SINGLE_INSTANCE_MUTEX_NAME "RollerCoaster Tycoon 2_GSKMUTEX" utf8 _userDataDirectoryPath[MAX_PATH] = { 0 }; +utf8 _openrctDataDirectoryPath[MAX_PATH] = { 0 }; utf8 **windows_get_command_line_args(int *outNumArgs); @@ -407,6 +408,51 @@ bool platform_file_delete(const utf8 *path) return success == TRUE; } +void platform_resolve_openrct_data_path() +{ + wchar_t wOutPath[MAX_PATH]; + const char separator[2] = { platform_get_path_separator(), 0 }; + + if (gCustomOpenrctDataPath[0] != 0) { + wchar_t *customUserDataPathW = utf8_to_widechar(gCustomOpenrctDataPath); + if (GetFullPathNameW(customUserDataPathW, countof(wOutPath), wOutPath, NULL) == 0) { + log_fatal("Unable to resolve path '%s'.", gCustomOpenrctDataPath); + exit(-1); + } + utf8 *outPathTemp = widechar_to_utf8(wOutPath); + safe_strncpy(_userDataDirectoryPath, outPathTemp, sizeof(_userDataDirectoryPath)); + free(outPathTemp); + free(customUserDataPathW); + + // Ensure path ends with separator + int len = strlen(_userDataDirectoryPath); + if (_userDataDirectoryPath[len - 1] != separator[0]) { + strcat(_userDataDirectoryPath, separator); + } + return; + } + char buffer[MAX_PATH] = { 0 }; + platform_get_exe_path(buffer); + + strncat(buffer, separator, MAX_PATH - strnlen(buffer, MAX_PATH) - 1); + strncat(buffer, "data", MAX_PATH - strnlen(buffer, MAX_PATH) - 1); + + if (platform_directory_exists(buffer)) + { + _openrctDataDirectoryPath[0] = '\0'; + safe_strncpy(_openrctDataDirectoryPath, buffer, sizeof(_openrctDataDirectoryPath)); + return; + } else { + log_fatal("Unable to resolve openrct data path."); + exit(-1); + } +} + +void platform_get_openrct_data_path(utf8 *outPath) +{ + safe_strncpy(outPath, _openrctDataDirectoryPath, sizeof(_openrctDataDirectoryPath)); +} + /** * Default directory fallback is: * - (command line argument) diff --git a/src/title.c b/src/title.c index 032d4eb8e0..33d6007ffe 100644 --- a/src/title.c +++ b/src/title.c @@ -602,9 +602,11 @@ static uint8 *title_script_load() char parts[3 * 128], *token, *part1, *part2, *src; utf8 path[MAX_PATH]; - utf8 filePath[] = "data/title/script.txt"; + utf8 dataPath[MAX_PATH]; + utf8 filePath[] = "title/script.txt"; - sprintf(path, "%s%c%s", gExePath, platform_get_path_separator(), filePath); + platform_get_openrct_data_path(dataPath); + sprintf(path, "%s%c%s", dataPath, platform_get_path_separator(), filePath); log_verbose("loading title script, %s", path); file = SDL_RWFromFile(path, "r"); if (file == NULL) {