diff --git a/openrct2.vcxproj b/openrct2.vcxproj
index 4a8b885800..092fca3988 100644
--- a/openrct2.vcxproj
+++ b/openrct2.vcxproj
@@ -30,6 +30,7 @@
+
@@ -203,6 +204,7 @@
+
@@ -377,4 +379,4 @@
-
+
\ No newline at end of file
diff --git a/openrct2.vcxproj.filters b/openrct2.vcxproj.filters
index fcebcbbe58..2ad680ca98 100644
--- a/openrct2.vcxproj.filters
+++ b/openrct2.vcxproj.filters
@@ -576,6 +576,9 @@
Source\Core
+
+ Source\Core
+
@@ -875,5 +878,8 @@
Source\Core
+
+ Source\Core
+
\ No newline at end of file
diff --git a/src/cmdline/RootCommands.cpp b/src/cmdline/RootCommands.cpp
index 9c9f24b851..5596d65b69 100644
--- a/src/cmdline/RootCommands.cpp
+++ b/src/cmdline/RootCommands.cpp
@@ -2,11 +2,13 @@
extern "C"
{
+ #include "../config.h"
#include "../openrct2.h"
}
#include "../core/Console.hpp"
#include "../core/Memory.hpp"
+#include "../core/Path.hpp"
#include "../core/String.hpp"
#include "../network/network.h"
#include "CommandLine.hpp"
@@ -52,6 +54,7 @@ static exitcode_t HandleCommandEdit(CommandLineArgEnumerator * enumerator);
static exitcode_t HandleCommandIntro(CommandLineArgEnumerator * enumerator);
static exitcode_t HandleCommandHost(CommandLineArgEnumerator * enumerator);
static exitcode_t HandleCommandJoin(CommandLineArgEnumerator * enumerator);
+static exitcode_t HandleCommandSetRCT2(CommandLineArgEnumerator * enumerator);
static void PrintAbout();
static void PrintVersion();
@@ -60,13 +63,14 @@ static void PrintLaunchInformation();
const CommandLineCommand CommandLine::RootCommands[]
{
// Main commands
- DefineCommand("", "", StandardOptions, HandleNoCommand ),
- DefineCommand("edit", "", StandardOptions, HandleCommandEdit ),
- DefineCommand("intro", "", StandardOptions, HandleCommandIntro),
-#ifndef DISABLE_NETWORK
- DefineCommand("host", "", StandardOptions, HandleCommandHost ),
- DefineCommand("join", "", StandardOptions, HandleCommandJoin ),
+ DefineCommand("", "", StandardOptions, HandleNoCommand ),
+ DefineCommand("edit", "", StandardOptions, HandleCommandEdit ),
+ DefineCommand("intro", "", StandardOptions, HandleCommandIntro ),
+#ifndef DISABLE_NETWORK
+ DefineCommand("host", "", StandardOptions, HandleCommandHost ),
+ DefineCommand("join", "", StandardOptions, HandleCommandJoin ),
#endif
+ DefineCommand("set-rct2", "", StandardOptions, HandleCommandSetRCT2),
// Sub-commands
DefineSubCommand("screenshot", CommandLine::ScreenshotCommands),
@@ -232,6 +236,84 @@ exitcode_t HandleCommandJoin(CommandLineArgEnumerator * enumerator)
#endif // DISABLE_NETWORK
+static exitcode_t HandleCommandSetRCT2(CommandLineArgEnumerator * enumerator)
+{
+ exitcode_t result = CommandLine::HandleCommandDefault();
+ if (result != EXITCODE_CONTINUE)
+ {
+ return result;
+ }
+
+ // Get the path that was passed
+ const utf8 * rawPath;
+ if (!enumerator->TryPopString(&rawPath))
+ {
+ Console::Error::WriteLine("Expected a path.");
+ return EXITCODE_FAIL;
+ }
+
+ utf8 path[MAX_PATH];
+ Path::GetAbsolute(path, sizeof(path), rawPath);
+
+ // Check if path exists
+ Console::WriteLine("Checking path...");
+ if (!platform_directory_exists(path))
+ {
+ Console::Error::WriteFormat("The path '%s' does not exist", path);
+ Console::Error::WriteLine();
+ return EXITCODE_FAIL;
+ }
+
+ // Check if g1.dat exists (naive but good check)
+ Console::WriteLine("Checking g1.dat...");
+
+ utf8 pathG1Check[MAX_PATH];
+ String::Set(pathG1Check, sizeof(pathG1Check), path);
+ Path::Append(pathG1Check, sizeof(pathG1Check), "Data");
+ Path::Append(pathG1Check, sizeof(pathG1Check), "g1.dat");
+ if (!platform_file_exists(pathG1Check))
+ {
+ Console::Error::WriteLine("RCT2 path not valid.");
+ Console::Error::WriteFormat("Unable to find %s.", pathG1Check);
+ Console::Error::WriteLine();
+ return EXITCODE_FAIL;
+ }
+
+ // Check user path that will contain the config
+ utf8 userPath[MAX_PATH];
+ platform_resolve_user_data_path();
+ platform_get_user_directory(userPath, NULL);
+ if (!platform_ensure_directory_exists(userPath)) {
+ Console::Error::WriteFormat("Unable to access or create directory '%s'.", userPath);
+ Console::Error::WriteLine();
+ return EXITCODE_FAIL;
+ }
+
+ // Update RCT2 path in config
+
+ // TODO remove this when we get rid of config_apply_to_old_addresses
+ if (!openrct2_setup_rct2_segment()) {
+ Console::Error::WriteLine("Unable to load RCT2 data sector");
+ return EXITCODE_FAIL;
+ }
+
+ config_set_defaults();
+ config_open_default();
+ String::DiscardDuplicate(&gConfigGeneral.game_path, path);
+ if (config_save_default())
+ {
+ Console::WriteFormat("Updating RCT2 path to '%s'.", path);
+ Console::WriteLine();
+ Console::WriteLine("Updated config.ini");
+ return EXITCODE_OK;
+ }
+ else
+ {
+ Console::Error::WriteLine("Unable to update config.ini");
+ return EXITCODE_FAIL;
+ }
+}
+
static void PrintAbout()
{
PrintVersion();
diff --git a/src/core/Path.cpp b/src/core/Path.cpp
new file mode 100644
index 0000000000..8b4344d4da
--- /dev/null
+++ b/src/core/Path.cpp
@@ -0,0 +1,52 @@
+extern "C"
+{
+ #include "../platform/platform.h"
+ #include "../localisation/localisation.h"
+ #include "../util/util.h"
+}
+
+#include "Memory.hpp"
+#include "Path.hpp"
+#include "String.hpp"
+#include "Util.hpp"
+
+namespace Path
+{
+ utf8 * Append(utf8 * buffer, size_t bufferSize, const utf8 * src)
+ {
+ return safe_strcat_path(buffer, src, bufferSize);
+ }
+
+ utf8 * GetAbsolute(utf8 *buffer, size_t bufferSize, const utf8 * relativePath)
+ {
+#if _WIN32
+ wchar_t * relativePathW = utf8_to_widechar(relativePath);
+ wchar_t absolutePathW[MAX_PATH];
+ DWORD length = GetFullPathNameW(relativePathW, Util::CountOf(absolutePathW), absolutePathW, NULL);
+ Memory::Free(relativePathW);
+ if (length == 0)
+ {
+ return String::Set(buffer, bufferSize, relativePath);
+ }
+ else
+ {
+ utf8 * absolutePath = widechar_to_utf8(absolutePathW);
+ String::Set(buffer, bufferSize, absolutePath);
+ Memory::Free(absolutePath);
+ return buffer;
+ }
+#else
+ utf8 * absolutePath = realpath(relativePath, NULL);
+ if (absolutePath == nullptr)
+ {
+ return String::Set(buffer, bufferSize, relativePath);
+ }
+ else
+ {
+ String::Set(buffer, bufferSize, absolutePath);
+ Memory::Free(absolutePath);
+ return buffer;
+ }
+#endif
+ }
+}
diff --git a/src/core/Path.hpp b/src/core/Path.hpp
new file mode 100644
index 0000000000..cdc1bab230
--- /dev/null
+++ b/src/core/Path.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+extern "C"
+{
+ #include "../common.h"
+}
+
+namespace Path
+{
+ utf8 * Append(utf8 * buffer, size_t bufferSize, const utf8 * src);
+ utf8 * GetAbsolute(utf8 *buffer, size_t bufferSize, const utf8 * relativePath);
+}
diff --git a/src/core/String.cpp b/src/core/String.cpp
index 9c02161a35..543a892cfd 100644
--- a/src/core/String.cpp
+++ b/src/core/String.cpp
@@ -129,4 +129,16 @@ namespace String
size_t srcSize = SizeOf(src);
return Memory::DuplicateArray(src, srcSize + 1);
}
+
+ utf8 * DiscardUse(utf8 * * ptr, utf8 * replacement)
+ {
+ Memory::Free(*ptr);
+ *ptr = replacement;
+ return replacement;
+ }
+
+ utf8 * DiscardDuplicate(utf8 * * ptr, utf8 * replacement)
+ {
+ return DiscardUse(ptr, String::Duplicate(replacement));
+ }
}
diff --git a/src/core/String.hpp b/src/core/String.hpp
index 98313ec1ce..b1dc24c720 100644
--- a/src/core/String.hpp
+++ b/src/core/String.hpp
@@ -17,4 +17,14 @@ namespace String
utf8 * Format(utf8 * buffer, size_t bufferSize, const utf8 * format, ...);
utf8 * AppendFormat(utf8 * buffer, size_t bufferSize, const utf8 * format, ...);
utf8 * Duplicate(const utf8 * src);
+
+ /**
+ * Helper method to free the string a string pointer points to and set it to a replacement string.
+ */
+ utf8 * DiscardUse(utf8 * * ptr, utf8 * replacement);
+
+ /**
+ * Helper method to free the string a string pointer points to and set it to a copy of a replacement string.
+ */
+ utf8 * DiscardDuplicate(utf8 * * ptr, utf8 * replacement);
}
diff --git a/src/openrct2.c b/src/openrct2.c
index 908e57f927..b54cd5a088 100644
--- a/src/openrct2.c
+++ b/src/openrct2.c
@@ -68,7 +68,6 @@ int _finished;
static struct { sint16 x, y, z; } _spritelocations1[MAX_SPRITES], _spritelocations2[MAX_SPRITES];
static void openrct2_loop();
-static bool openrct2_setup_rct2_segment();
static void openrct2_setup_rct2_hooks();
void openrct2_write_full_version_info(utf8 *buffer, size_t bufferSize)
@@ -475,7 +474,7 @@ void openrct2_reset_object_tween_locations()
* Loads RCT2's data model and remaps the addresses.
* @returns true if the data integrity check succeeded, otherwise false.
*/
-static bool openrct2_setup_rct2_segment()
+bool openrct2_setup_rct2_segment()
{
// OpenRCT2 on Linux and OS X is wired to have the original Windows PE sections loaded
// necessary. Windows does not need to do this as OpenRCT2 runs as a DLL loaded from the Windows PE.
diff --git a/src/openrct2.h b/src/openrct2.h
index e044e848f9..82eb77452a 100644
--- a/src/openrct2.h
+++ b/src/openrct2.h
@@ -54,6 +54,7 @@ void openrct2_launch();
void openrct2_dispose();
void openrct2_finish();
void openrct2_reset_object_tween_locations();
+bool openrct2_setup_rct2_segment();
int cmdline_run(const char **argv, int argc);