mirror of https://github.com/OpenRCT2/OpenRCT2.git
Merge pull request #2196 from IntelOrca/custom-user-data-path
Allow user data path to be specified by command line
This commit is contained in:
commit
7366c87e2c
|
@ -82,6 +82,7 @@
|
|||
<ClCompile Include="..\src\openrct2.c" />
|
||||
<ClCompile Include="..\src\peep\peep.c" />
|
||||
<ClCompile Include="..\src\peep\staff.c" />
|
||||
<ClCompile Include="..\src\platform\linux.c" />
|
||||
<ClCompile Include="..\src\platform\shared.c" />
|
||||
<ClCompile Include="..\src\platform\unix.c" />
|
||||
<ClCompile Include="..\src\platform\osx.c" />
|
||||
|
|
|
@ -543,6 +543,9 @@
|
|||
<ClCompile Include="..\src\ride\track_paint.c">
|
||||
<Filter>Source\Ride</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\platform\linux.c">
|
||||
<Filter>Source\Platform</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\src\management\award.h">
|
||||
|
|
|
@ -73,6 +73,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;
|
||||
|
||||
argparse_option_t options[] = {
|
||||
OPT_HELP(),
|
||||
|
@ -82,6 +83,7 @@ int cmdline_run(const char **argv, int argc)
|
|||
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_STRING(0, "user-data-path", &userDataPath, "path to the user data directory (containing config.ini)"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
@ -100,6 +102,10 @@ int cmdline_run(const char **argv, int argc)
|
|||
if (verbose)
|
||||
_log_levels[DIAGNOSTIC_LEVEL_VERBOSE] = 1;
|
||||
|
||||
if (userDataPath != NULL) {
|
||||
safe_strncpy(gCustomUserDataPath, userDataPath, sizeof(gCustomUserDataPath));
|
||||
}
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
if (port != 0) {
|
||||
gNetworkStart = NETWORK_MODE_SERVER;
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
int gOpenRCT2StartupAction = STARTUP_ACTION_TITLE;
|
||||
utf8 gOpenRCT2StartupActionPath[512] = { 0 };
|
||||
utf8 gExePath[MAX_PATH];
|
||||
utf8 gCustomUserDataPath[MAX_PATH] = { 0 };
|
||||
|
||||
// This should probably be changed later and allow a custom selection of things to initialise like SDL_INIT
|
||||
bool gOpenRCT2Headless = false;
|
||||
|
@ -187,6 +188,7 @@ bool openrct2_initialise()
|
|||
{
|
||||
utf8 userPath[MAX_PATH];
|
||||
|
||||
platform_resolve_user_data_path();
|
||||
platform_get_user_directory(userPath, NULL);
|
||||
if (!platform_ensure_directory_exists(userPath)) {
|
||||
log_fatal("Could not create user directory (do you have write access to your documents folder?)");
|
||||
|
|
|
@ -31,8 +31,9 @@ enum {
|
|||
};
|
||||
|
||||
extern int gOpenRCT2StartupAction;
|
||||
extern char gOpenRCT2StartupActionPath[512];
|
||||
extern char gExePath[MAX_PATH];
|
||||
extern utf8 gOpenRCT2StartupActionPath[512];
|
||||
extern utf8 gExePath[MAX_PATH];
|
||||
extern utf8 gCustomUserDataPath[MAX_PATH];
|
||||
extern bool gOpenRCT2Headless;
|
||||
extern bool gOpenRCT2ShowChangelog;
|
||||
|
||||
|
|
|
@ -39,6 +39,8 @@
|
|||
// The name of the mutex used to prevent multiple instances of the game from running
|
||||
#define SINGLE_INSTANCE_MUTEX_NAME "RollerCoaster Tycoon 2_GSKMUTEX"
|
||||
|
||||
utf8 _userDataDirectoryPath[MAX_PATH] = { 0 };
|
||||
|
||||
/**
|
||||
* The function that is called directly from the host application (rct2.exe)'s WinMain. This will be removed when OpenRCT2 can
|
||||
* be built as a stand alone application.
|
||||
|
@ -554,8 +556,27 @@ wchar_t *regular_to_wchar(const char* src)
|
|||
return w_buffer;
|
||||
}
|
||||
|
||||
void platform_get_user_directory(utf8 *outPath, const utf8 *subDirectory)
|
||||
/**
|
||||
* Default directory fallback is:
|
||||
* - (command line argument)
|
||||
* - $XDG_CONFIG_HOME/.config/OpenRCT2
|
||||
* - /home/[uid]/.config/OpenRCT2
|
||||
*/
|
||||
void platform_resolve_user_data_path()
|
||||
{
|
||||
const char separator[2] = { platform_get_path_separator(), 0 };
|
||||
|
||||
if (gCustomUserDataPath[0] != 0) {
|
||||
realpath(gCustomUserDataPath, _userDataDirectoryPath);
|
||||
|
||||
// Ensure path ends with separator
|
||||
int len = strlen(_userDataDirectoryPath);
|
||||
if (_userDataDirectoryPath[len - 1] != separator[0]) {
|
||||
strcat(_userDataDirectoryPath, separator);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[MAX_PATH];
|
||||
buffer[0] = '\0';
|
||||
log_verbose("buffer = '%s'", buffer);
|
||||
|
@ -567,16 +588,37 @@ void platform_get_user_directory(utf8 *outPath, const utf8 *subDirectory)
|
|||
log_verbose("homedir was null, used getuid, now is = '%s'", homedir);
|
||||
if (homedir == NULL)
|
||||
{
|
||||
log_error("Couldn't find user home directory");
|
||||
log_fatal("Couldn't find user data directory");
|
||||
exit(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
strncat(buffer, homedir, MAX_PATH);
|
||||
strncat(buffer, separator, MAX_PATH);
|
||||
strncat(buffer, ".config", MAX_PATH);
|
||||
}
|
||||
else
|
||||
{
|
||||
strncat(buffer, homedir, MAX_PATH);
|
||||
}
|
||||
char separator[2] = { platform_get_path_separator(), 0 };
|
||||
strncat(buffer, homedir, MAX_PATH);
|
||||
strncat(buffer, separator, MAX_PATH);
|
||||
strncat(buffer, "OpenRCT2", MAX_PATH);
|
||||
strncat(buffer, separator, MAX_PATH);
|
||||
log_verbose("outPath + OpenRCT2 = '%s'", buffer);
|
||||
log_verbose("OpenRCT2 user data directory = '%s'", buffer);
|
||||
int len = strnlen(buffer, MAX_PATH);
|
||||
wchar_t *w_buffer = regular_to_wchar(buffer);
|
||||
w_buffer[len] = '\0';
|
||||
utf8 *path = widechar_to_utf8(w_buffer);
|
||||
free(w_buffer);
|
||||
safe_strncpy(_userDataDirectoryPath, path, MAX_PATH);
|
||||
free(path);
|
||||
}
|
||||
|
||||
void platform_get_user_directory(utf8 *outPath, const utf8 *subDirectory)
|
||||
{
|
||||
const char separator[2] = { platform_get_path_separator(), 0 };
|
||||
char buffer[MAX_PATH];
|
||||
safe_strncpy(buffer, _userDataDirectoryPath, sizeof(buffer));
|
||||
if (subDirectory != NULL && subDirectory[0] != 0) {
|
||||
log_verbose("adding subDirectory '%s'", subDirectory);
|
||||
strcat(buffer, subDirectory);
|
||||
|
|
|
@ -138,6 +138,7 @@ void platform_show_cursor();
|
|||
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_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);
|
||||
|
|
|
@ -37,7 +37,9 @@
|
|||
// The name of the mutex used to prevent multiple instances of the game from running
|
||||
#define SINGLE_INSTANCE_MUTEX_NAME "RollerCoaster Tycoon 2_GSKMUTEX"
|
||||
|
||||
LPSTR *CommandLineToArgvA(LPSTR lpCmdLine, int *argc);
|
||||
utf8 _userDataDirectoryPath[MAX_PATH] = { 0 };
|
||||
|
||||
utf8 **windows_get_command_line_args(int *outNumArgs);
|
||||
|
||||
/**
|
||||
* Windows entry point to OpenRCT2 without a console window.
|
||||
|
@ -75,18 +77,43 @@ __declspec(dllexport) int StartOpenRCT(HINSTANCE hInstance, HINSTANCE hPrevInsta
|
|||
RCT2_GLOBAL(RCT2_ADDRESS_HINSTANCE, HINSTANCE) = hInstance;
|
||||
RCT2_GLOBAL(RCT2_ADDRESS_CMDLINE, LPSTR) = lpCmdLine;
|
||||
|
||||
// Get command line arguments in standard form
|
||||
argv = CommandLineToArgvA(lpCmdLine, &argc);
|
||||
// argv = CommandLineToArgvA(lpCmdLine, &argc);
|
||||
argv = (char**)windows_get_command_line_args(&argc);
|
||||
runGame = cmdline_run((const char **)argv, argc);
|
||||
GlobalFree(argv);
|
||||
|
||||
if (runGame)
|
||||
// Free argv
|
||||
for (int i = 0; i < argc; i++) {
|
||||
free(argv[i]);
|
||||
}
|
||||
free(argv);
|
||||
|
||||
if (runGame) {
|
||||
openrct2_launch();
|
||||
}
|
||||
|
||||
exit(gExitCode);
|
||||
return gExitCode;
|
||||
}
|
||||
|
||||
utf8 **windows_get_command_line_args(int *outNumArgs)
|
||||
{
|
||||
int argc;
|
||||
|
||||
// Get command line arguments as widechar
|
||||
LPWSTR commandLine = GetCommandLineW();
|
||||
LPWSTR *argvW = CommandLineToArgvW(commandLine, &argc);
|
||||
|
||||
// Convert to UTF-8
|
||||
utf8 **argvUtf8 = (utf8**)malloc(argc * sizeof(utf8*));
|
||||
for (int i = 0; i < argc; i++) {
|
||||
argvUtf8[i] = widechar_to_utf8(argvW[i]);
|
||||
}
|
||||
LocalFree(argvW);
|
||||
|
||||
*outNumArgs = argc;
|
||||
return argvUtf8;
|
||||
}
|
||||
|
||||
void platform_get_date(rct2_date *out_date)
|
||||
{
|
||||
assert(out_date != NULL);
|
||||
|
@ -380,25 +407,57 @@ bool platform_file_delete(const utf8 *path)
|
|||
return success == TRUE;
|
||||
}
|
||||
|
||||
void platform_get_user_directory(utf8 *outPath, const utf8 *subDirectory)
|
||||
/**
|
||||
* Default directory fallback is:
|
||||
* - (command line argument)
|
||||
* - C:\Users\%USERNAME%\OpenRCT2 (as from SHGetFolderPathW)
|
||||
*/
|
||||
void platform_resolve_user_data_path()
|
||||
{
|
||||
wchar_t wOutPath[MAX_PATH];
|
||||
char separator[2] = { platform_get_path_separator(), 0 };
|
||||
const char separator[2] = { platform_get_path_separator(), 0 };
|
||||
|
||||
if (gCustomUserDataPath[0] != 0) {
|
||||
wchar_t *customUserDataPathW = utf8_to_widechar(gCustomUserDataPath);
|
||||
if (GetFullPathNameW(customUserDataPathW, countof(wOutPath), wOutPath, NULL) == 0) {
|
||||
log_fatal("Unable to resolve path '%s'.", gCustomUserDataPath);
|
||||
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;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, wOutPath))) {
|
||||
utf8 *outPathTemp = widechar_to_utf8(wOutPath);
|
||||
safe_strncpy(outPath, outPathTemp, MAX_PATH);
|
||||
safe_strncpy(_userDataDirectoryPath, outPathTemp, sizeof(_userDataDirectoryPath));
|
||||
free(outPathTemp);
|
||||
|
||||
strcat(outPath, separator);
|
||||
strcat(outPath, "OpenRCT2");
|
||||
strcat(outPath, separator);
|
||||
if (subDirectory != NULL && subDirectory[0] != 0) {
|
||||
strcat(outPath, subDirectory);
|
||||
strcat(outPath, separator);
|
||||
}
|
||||
strcat(_userDataDirectoryPath, separator);
|
||||
strcat(_userDataDirectoryPath, "OpenRCT2");
|
||||
strcat(_userDataDirectoryPath, separator);
|
||||
} else {
|
||||
outPath[0] = 0;
|
||||
log_fatal("Unable to resolve user data path.");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void platform_get_user_directory(utf8 *outPath, const utf8 *subDirectory)
|
||||
{
|
||||
const char separator[2] = { platform_get_path_separator(), 0 };
|
||||
|
||||
strcpy(outPath, _userDataDirectoryPath);
|
||||
if (subDirectory != NULL && subDirectory[0] != 0) {
|
||||
strcat(outPath, subDirectory);
|
||||
strcat(outPath, separator);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -604,91 +663,6 @@ HWND windows_get_window_handle()
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* http://alter.org.ua/en/docs/win/args/
|
||||
*/
|
||||
PCHAR *CommandLineToArgvA(PCHAR CmdLine, int *_argc)
|
||||
{
|
||||
PCHAR* argv;
|
||||
PCHAR _argv;
|
||||
ULONG len;
|
||||
ULONG argc;
|
||||
CHAR a;
|
||||
ULONG i, j;
|
||||
|
||||
BOOLEAN in_QM;
|
||||
BOOLEAN in_TEXT;
|
||||
BOOLEAN in_SPACE;
|
||||
|
||||
len = strlen(CmdLine);
|
||||
i = ((len + 2) / 2)*sizeof(PVOID) + sizeof(PVOID);
|
||||
|
||||
argv = (PCHAR*)GlobalAlloc(GMEM_FIXED,
|
||||
i + (len + 2)*sizeof(CHAR) + 1);
|
||||
|
||||
_argv = (PCHAR)(((PUCHAR)argv) + i);
|
||||
|
||||
// Add in virtual 1st command line argument, process path, for arg_parse's sake.
|
||||
argv[0] = 0;
|
||||
argc = 1;
|
||||
argv[argc] = _argv;
|
||||
in_QM = FALSE;
|
||||
in_TEXT = FALSE;
|
||||
in_SPACE = TRUE;
|
||||
i = 0;
|
||||
j = 0;
|
||||
|
||||
while (a = CmdLine[i]) {
|
||||
if (in_QM) {
|
||||
if (a == '\"') {
|
||||
in_QM = FALSE;
|
||||
} else {
|
||||
_argv[j] = a;
|
||||
j++;
|
||||
}
|
||||
} else {
|
||||
switch (a) {
|
||||
case '\"':
|
||||
in_QM = TRUE;
|
||||
in_TEXT = TRUE;
|
||||
if (in_SPACE) {
|
||||
argv[argc] = _argv + j;
|
||||
argc++;
|
||||
}
|
||||
in_SPACE = FALSE;
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\r':
|
||||
if (in_TEXT) {
|
||||
_argv[j] = '\0';
|
||||
j++;
|
||||
}
|
||||
in_TEXT = FALSE;
|
||||
in_SPACE = TRUE;
|
||||
break;
|
||||
default:
|
||||
in_TEXT = TRUE;
|
||||
if (in_SPACE) {
|
||||
argv[argc] = _argv + j;
|
||||
argc++;
|
||||
}
|
||||
_argv[j] = a;
|
||||
j++;
|
||||
in_SPACE = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
_argv[j] = '\0';
|
||||
argv[argc] = NULL;
|
||||
|
||||
(*_argc) = argc;
|
||||
return argv;
|
||||
}
|
||||
|
||||
uint16 platform_get_locale_language()
|
||||
{
|
||||
CHAR langCode[4];
|
||||
|
|
|
@ -183,6 +183,9 @@ int strcicmp(char const *a, char const *b)
|
|||
|
||||
char *safe_strncpy(char * destination, const char * source, size_t size)
|
||||
{
|
||||
assert(destination != NULL);
|
||||
assert(source != NULL);
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
return destination;
|
||||
|
|
Loading…
Reference in New Issue