From 925d64249f50e78c1639df7bf476d33acf79e4b8 Mon Sep 17 00:00:00 2001 From: Ted John Date: Sat, 12 Nov 2016 02:21:42 +0000 Subject: [PATCH] Add openrct2:// uri scheme support This sets up a URI scheme for openrct2 for Windows by adding the necessary registry keys. This is done on startup every time to keep the binary location up to date. URI handling currently supports joining a server. --- src/openrct2/cmdline/CommandLine.hpp | 1 + src/openrct2/cmdline/RootCommands.cpp | 1 + src/openrct2/cmdline/UriHandler.cpp | 93 +++++++++++++++++++++++++++ src/openrct2/libopenrct2.vcxproj | 1 + src/openrct2/platform/platform.h | 1 + src/openrct2/platform/windows.c | 49 +++++++++++++- 6 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 src/openrct2/cmdline/UriHandler.cpp diff --git a/src/openrct2/cmdline/CommandLine.hpp b/src/openrct2/cmdline/CommandLine.hpp index 21463f9467..2014326da6 100644 --- a/src/openrct2/cmdline/CommandLine.hpp +++ b/src/openrct2/cmdline/CommandLine.hpp @@ -110,4 +110,5 @@ namespace CommandLine exitcode_t HandleCommandDefault(); exitcode_t HandleCommandConvert(CommandLineArgEnumerator * enumerator); + exitcode_t HandleCommandUri(CommandLineArgEnumerator * enumerator); } diff --git a/src/openrct2/cmdline/RootCommands.cpp b/src/openrct2/cmdline/RootCommands.cpp index b68ca8e7f1..1d64d017b8 100644 --- a/src/openrct2/cmdline/RootCommands.cpp +++ b/src/openrct2/cmdline/RootCommands.cpp @@ -126,6 +126,7 @@ const CommandLineCommand CommandLine::RootCommands[] DefineCommand("set-rct2", "", StandardOptions, HandleCommandSetRCT2), DefineCommand("convert", " ", StandardOptions, CommandLine::HandleCommandConvert), DefineCommand("scan-objects", "", StandardOptions, HandleCommandScanObjects), + DefineCommand("handle-uri", "openrct2://.../", StandardOptions, CommandLine::HandleCommandUri), #if defined(__WINDOWS__) && !defined(__MINGW32__) DefineCommand("register-shell", "", RegisterShellOptions, HandleCommandRegisterShell), diff --git a/src/openrct2/cmdline/UriHandler.cpp b/src/openrct2/cmdline/UriHandler.cpp new file mode 100644 index 0000000000..041f6c0bb9 --- /dev/null +++ b/src/openrct2/cmdline/UriHandler.cpp @@ -0,0 +1,93 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * 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 + * + * 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. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#include "../core/Console.hpp" +#include "../core/Memory.hpp" +#include "../core/String.hpp" +#include "../network/network.h" +#include "CommandLine.hpp" + +extern "C" +{ + #include "../openrct2.h" +} + +static exitcode_t HandleUri(const utf8 * uri); + +exitcode_t CommandLine::HandleCommandUri(CommandLineArgEnumerator * enumerator) +{ + const utf8 * uri; + if (enumerator->TryPopString(&uri)) + { + if (String::StartsWith(uri, "openrct2://")) + { + const utf8 * uriCommand = uri + 11; + return HandleUri(uriCommand); + } + } + + Console::Error::WriteLine("Invalid URI"); + return EXITCODE_FAIL; +} + +static exitcode_t HandleUri(const utf8 * uri) +{ + utf8 * * args; + size_t numArgs = String::Split(&args, uri, '/'); + if (numArgs > 0) + { + utf8 * arg = args[0]; + if (String::Equals(arg, "join")) + { + if (numArgs > 1) + { + utf8 * hostnamePort = args[1]; + + // Argument is in hostname:port format, so we need to split + utf8 * hostname = String::Duplicate(hostnamePort); + sint32 port = NETWORK_DEFAULT_PORT; + size_t colonIndex = String::IndexOf(hostnamePort, ':'); + if (colonIndex != SIZE_MAX) + { + Memory::Free(hostname); + hostname = String::Substring(hostnamePort, 0, colonIndex); + port = atoi(hostnamePort + colonIndex + 1); + } + + // Set the network start configuration + gNetworkStart = NETWORK_MODE_CLIENT; + String::Set(gNetworkStartHost, sizeof(gNetworkStartHost), hostname); + gNetworkStartPort = port; + + Memory::Free(hostname); + } + else + { + Console::Error::WriteLine("Expected hostname:port after join"); + return EXITCODE_FAIL; + } + } + } + + // Clean up + for (size_t i = 0; i < numArgs; i++) + { + Memory::Free(args[i]); + } + Memory::FreeArray(args, numArgs); + + return EXITCODE_CONTINUE; +} diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj index 8096eb5290..fe636745d1 100644 --- a/src/openrct2/libopenrct2.vcxproj +++ b/src/openrct2/libopenrct2.vcxproj @@ -72,6 +72,7 @@ + diff --git a/src/openrct2/platform/platform.h b/src/openrct2/platform/platform.h index df2181f2c5..535b33fedf 100644 --- a/src/openrct2/platform/platform.h +++ b/src/openrct2/platform/platform.h @@ -224,6 +224,7 @@ void core_init(); HWND windows_get_window_handle(); void platform_setup_file_associations(); void platform_remove_file_associations(); + bool platform_setup_uri_protocol(); // This function cannot be marked as 'static', even though it may seem to be, // as it requires external linkage, which 'static' prevents __declspec(dllexport) sint32 StartOpenRCT(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, sint32 nCmdShow); diff --git a/src/openrct2/platform/windows.c b/src/openrct2/platform/windows.c index fa568c323d..e46fbbd84b 100644 --- a/src/openrct2/platform/windows.c +++ b/src/openrct2/platform/windows.c @@ -1040,7 +1040,8 @@ utf8* platform_get_username() { // File association setup /////////////////////////////////////////////////////////////////////////////// -#define SOFTWARE_CLASSES L"Software\\Classes" +#define SOFTWARE_CLASSES L"Software\\Classes" +#define MUI_CACHE L"Local Settings\\Software\\Microsoft\\Windows\\Shell\\MuiCache" static void get_progIdName(wchar_t *dst, const utf8 *extension) { @@ -1183,4 +1184,50 @@ void platform_remove_file_associations() } /////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// URI protocol association setup +/////////////////////////////////////////////////////////////////////////////// + +bool platform_setup_uri_protocol() +{ + log_verbose("Setting up URI protocol..."); + + // [HKEY_CURRENT_USER\Software\Classes] + HKEY hRootKey; + if (RegOpenKeyW(HKEY_CURRENT_USER, SOFTWARE_CLASSES, &hRootKey) == ERROR_SUCCESS) { + // [hRootKey\openrct2] + HKEY hClassKey; + if (RegCreateKeyA(hRootKey, "openrct2", &hClassKey) == ERROR_SUCCESS) { + if (RegSetValueA(hClassKey, NULL, REG_SZ, "URL:openrct2", 0) == ERROR_SUCCESS) { + if (RegSetKeyValueA(hClassKey, NULL, "URL Protocol", REG_SZ, "", 0) == ERROR_SUCCESS) { + // [hRootKey\openrct2\shell\open\command] + wchar_t exePath[MAX_PATH]; + GetModuleFileNameW(NULL, exePath, MAX_PATH); + + wchar_t buffer[512]; + swprintf_s(buffer, sizeof(buffer), L"\"%s\" handle-uri \"%%1\"", exePath); + if (RegSetValueW(hClassKey, L"shell\\open\\command", REG_SZ, buffer, 0) == ERROR_SUCCESS) { + // Not compulsory, but gives the application a nicer name + // [HKEY_CURRENT_USER\SOFTWARE\Classes\Local Settings\Software\Microsoft\Windows\Shell\MuiCache] + HKEY hMuiCacheKey; + if (RegCreateKeyW(hRootKey, MUI_CACHE, &hMuiCacheKey) == ERROR_SUCCESS) { + swprintf_s(buffer, sizeof(buffer), L"%s.FriendlyAppName", exePath); + RegSetKeyValueW(hMuiCacheKey, NULL, buffer, REG_SZ, L"OpenRCT2", sizeof(L"OpenRCT2") + 1); + } + + log_verbose("URI protocol setup successful"); + return true; + } + } + } + } + } + + log_verbose("URI protocol setup failed"); + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + #endif