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);