mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-02-01 03:05:24 +01:00
Merge pull request #4790 from IntelOrca/add/uri-protocol-handler
Add uri protocol handler
This commit is contained in:
@@ -240,6 +240,7 @@
|
||||
C6E96E321E04072F0076A04F /* TitleSequencePlayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C6E96E2D1E04072F0076A04F /* TitleSequencePlayer.cpp */; };
|
||||
C6E96E361E0408B40076A04F /* libzip.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = C6E96E351E0408B40076A04F /* libzip.dylib */; };
|
||||
C6E96E371E040E040076A04F /* libzip.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C6E96E351E0408B40076A04F /* libzip.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
C6EABCC41E719691008C09AB /* UriHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C6EABCC31E719691008C09AB /* UriHandler.cpp */; };
|
||||
D41B73EF1C2101890080A7B9 /* libcurl.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D41B73EE1C2101890080A7B9 /* libcurl.tbd */; };
|
||||
D41B741D1C210A7A0080A7B9 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D41B741C1C210A7A0080A7B9 /* libiconv.tbd */; };
|
||||
D41B74731C2125E50080A7B9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D41B74721C2125E50080A7B9 /* Assets.xcassets */; };
|
||||
@@ -716,6 +717,7 @@
|
||||
C6E96E331E0408A80076A04F /* zip.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = zip.h; sourceTree = "<group>"; };
|
||||
C6E96E341E0408A80076A04F /* zipconf.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = zipconf.h; sourceTree = "<group>"; };
|
||||
C6E96E351E0408B40076A04F /* libzip.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libzip.dylib; sourceTree = "<group>"; };
|
||||
C6EABCC31E719691008C09AB /* UriHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UriHandler.cpp; sourceTree = "<group>"; };
|
||||
C6FF1BAD1DBCE1A10078DCB5 /* junior_roller_coaster.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = junior_roller_coaster.h; sourceTree = "<group>"; };
|
||||
D41B73EE1C2101890080A7B9 /* libcurl.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libcurl.tbd; path = usr/lib/libcurl.tbd; sourceTree = SDKROOT; };
|
||||
D41B741C1C210A7A0080A7B9 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; };
|
||||
@@ -1686,6 +1688,7 @@
|
||||
D44270D91CC81B3200D84D28 /* RootCommands.cpp */,
|
||||
D44270DA1CC81B3200D84D28 /* ScreenshotCommands.cpp */,
|
||||
D44270DB1CC81B3200D84D28 /* SpriteCommands.cpp */,
|
||||
C6EABCC31E719691008C09AB /* UriHandler.cpp */,
|
||||
);
|
||||
path = cmdline;
|
||||
sourceTree = "<group>";
|
||||
@@ -2866,6 +2869,7 @@
|
||||
C686F8B91CDBC37E009F9BFC /* supports.c in Sources */,
|
||||
D442726E1CC81B3200D84D28 /* maze_construction.c in Sources */,
|
||||
D44272751CC81B3200D84D28 /* news_options.c in Sources */,
|
||||
C6EABCC41E719691008C09AB /* UriHandler.cpp in Sources */,
|
||||
D44272551CC81B3200D84D28 /* changelog.c in Sources */,
|
||||
C686F9471CDBC3B7009F9BFC /* top_spin.c in Sources */,
|
||||
D442720E1CC81B3200D84D28 /* rect.c in Sources */,
|
||||
|
||||
@@ -14,16 +14,18 @@
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "core/Console.hpp"
|
||||
#include "core/Guard.hpp"
|
||||
#include "core/File.h"
|
||||
#include "core/FileStream.hpp"
|
||||
#include "core/Guard.hpp"
|
||||
#include "core/String.hpp"
|
||||
#include "FileClassifier.h"
|
||||
#include "network/network.h"
|
||||
#include "object/ObjectRepository.h"
|
||||
#include "OpenRCT2.h"
|
||||
#include "ParkImporter.h"
|
||||
#include "platform/crash.h"
|
||||
#include "PlatformEnvironment.h"
|
||||
#include "ride/TrackDesignRepository.h"
|
||||
@@ -518,50 +520,59 @@ namespace OpenRCT2
|
||||
ClassifiedFile info;
|
||||
if (TryClassifyFile(path, &info))
|
||||
{
|
||||
if (info.Type == FILE_TYPE::SAVED_GAME)
|
||||
if (info.Type == FILE_TYPE::SAVED_GAME ||
|
||||
info.Type == FILE_TYPE::SCENARIO)
|
||||
{
|
||||
std::unique_ptr<IParkImporter> parkImporter;
|
||||
if (info.Version <= 2)
|
||||
{
|
||||
if (rct1_load_saved_game(path))
|
||||
parkImporter.reset(ParkImporter::CreateS4());
|
||||
}
|
||||
else
|
||||
{
|
||||
parkImporter.reset(ParkImporter::CreateS6());
|
||||
}
|
||||
|
||||
if (info.Type == FILE_TYPE::SAVED_GAME)
|
||||
{
|
||||
try
|
||||
{
|
||||
parkImporter->LoadSavedGame(path);
|
||||
parkImporter->Import();
|
||||
game_fix_save_vars();
|
||||
sprite_position_tween_reset();
|
||||
gScreenAge = 0;
|
||||
gLastAutoSaveUpdate = AUTOSAVE_PAUSE;
|
||||
game_load_init();
|
||||
return true;
|
||||
}
|
||||
catch (const Exception &)
|
||||
{
|
||||
Console::Error::WriteLine("Error loading saved game.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (game_load_save(path))
|
||||
{
|
||||
gFirstTimeSave = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Console::Error::WriteLine("Error loading saved game.");
|
||||
}
|
||||
else if (info.Type == FILE_TYPE::SCENARIO)
|
||||
{
|
||||
if (info.Version <= 2)
|
||||
{
|
||||
|
||||
if (rct1_load_scenario(path))
|
||||
try
|
||||
{
|
||||
parkImporter->LoadScenario(path);
|
||||
parkImporter->Import();
|
||||
game_fix_save_vars();
|
||||
sprite_position_tween_reset();
|
||||
gScreenAge = 0;
|
||||
gLastAutoSaveUpdate = AUTOSAVE_PAUSE;
|
||||
scenario_begin();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (scenario_load_and_play_from_path(path))
|
||||
catch (const Exception &)
|
||||
{
|
||||
return true;
|
||||
Console::Error::WriteLine("Error loading scenario.");
|
||||
}
|
||||
}
|
||||
Console::Error::WriteLine("Error loading scenario.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console::Error::WriteLine("Invalid file type.");
|
||||
Console::Error::WriteLine("Invalid file type.");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -110,4 +110,5 @@ namespace CommandLine
|
||||
exitcode_t HandleCommandDefault();
|
||||
|
||||
exitcode_t HandleCommandConvert(CommandLineArgEnumerator * enumerator);
|
||||
exitcode_t HandleCommandUri(CommandLineArgEnumerator * enumerator);
|
||||
}
|
||||
|
||||
@@ -126,6 +126,7 @@ const CommandLineCommand CommandLine::RootCommands[]
|
||||
DefineCommand("set-rct2", "<path>", StandardOptions, HandleCommandSetRCT2),
|
||||
DefineCommand("convert", "<source> <destination>", StandardOptions, CommandLine::HandleCommandConvert),
|
||||
DefineCommand("scan-objects", "<path>", StandardOptions, HandleCommandScanObjects),
|
||||
DefineCommand("handle-uri", "openrct2://.../", StandardOptions, CommandLine::HandleCommandUri),
|
||||
|
||||
#if defined(__WINDOWS__) && !defined(__MINGW32__)
|
||||
DefineCommand("register-shell", "", RegisterShellOptions, HandleCommandRegisterShell),
|
||||
|
||||
108
src/openrct2/cmdline/UriHandler.cpp
Normal file
108
src/openrct2/cmdline/UriHandler.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#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 "../OpenRCT2.h"
|
||||
#include "CommandLine.hpp"
|
||||
|
||||
static exitcode_t HandleUri(const std::string &uri);
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
static exitcode_t HandleUriJoin(const std::vector<std::string> &args);
|
||||
static bool TryParseHostnamePort(const std::string &hostnamePort, std::string * outHostname, sint32 * outPort, sint32 defaultPort);
|
||||
#endif
|
||||
|
||||
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 std::string &uri)
|
||||
{
|
||||
exitcode_t result = EXITCODE_CONTINUE;
|
||||
auto args = String::Split(uri, "/");
|
||||
if (args.size() > 0)
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
std::string arg = args[0];
|
||||
if (arg == "join")
|
||||
{
|
||||
result = HandleUriJoin(args);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
|
||||
static exitcode_t HandleUriJoin(const std::vector<std::string> &args)
|
||||
{
|
||||
std::string hostname;
|
||||
sint32 port;
|
||||
if (args.size() > 1 && TryParseHostnamePort(args[1], &hostname, &port, NETWORK_DEFAULT_PORT))
|
||||
{
|
||||
// Set the network start configuration
|
||||
gNetworkStart = NETWORK_MODE_CLIENT;
|
||||
String::Set(gNetworkStartHost, sizeof(gNetworkStartHost), hostname.c_str());
|
||||
gNetworkStartPort = port;
|
||||
return EXITCODE_CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console::Error::WriteLine("Expected hostname:port after join");
|
||||
return EXITCODE_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool TryParseHostnamePort(const std::string &hostnamePort, std::string * outHostname, sint32 * outPort, sint32 defaultPort)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Argument is in hostname:port format, so we need to split
|
||||
std::string hostname = hostnamePort;
|
||||
sint32 port = defaultPort;
|
||||
size_t colonIndex = hostnamePort.find_first_of(':');
|
||||
if (colonIndex != std::string::npos)
|
||||
{
|
||||
hostname = hostnamePort.substr(0, colonIndex);
|
||||
port = std::stoi(hostnamePort.substr(colonIndex + 1));
|
||||
}
|
||||
*outPort = port;
|
||||
*outHostname = hostname;
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DISABLE_NETWORK
|
||||
@@ -113,6 +113,9 @@ typedef uint8 colour_t;
|
||||
#endif // __GNUC__
|
||||
#endif // __cplusplus
|
||||
|
||||
// Gets the name of a symbol as a C string
|
||||
#define nameof(symbol) #symbol
|
||||
|
||||
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
||||
#include <unistd.h>
|
||||
#define STUB() log_warning("Function %s at %s:%d is a stub.\n", __PRETTY_FUNCTION__, __FILE__, __LINE__)
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#pragma endregion
|
||||
|
||||
#include <cwctype>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@@ -116,6 +118,19 @@ namespace String
|
||||
}
|
||||
}
|
||||
|
||||
size_t IndexOf(const utf8 * str, utf8 match, size_t startIndex)
|
||||
{
|
||||
const utf8 * ch = str + startIndex;
|
||||
for (; *ch != '\0'; ch++)
|
||||
{
|
||||
if (*ch == match)
|
||||
{
|
||||
return (size_t)(ch - str);
|
||||
}
|
||||
}
|
||||
return SIZE_MAX;
|
||||
}
|
||||
|
||||
size_t LastIndexOf(const utf8 * str, utf8 match)
|
||||
{
|
||||
const utf8 * lastOccurance = nullptr;
|
||||
@@ -300,6 +315,78 @@ namespace String
|
||||
return DiscardUse(ptr, String::Duplicate(replacement));
|
||||
}
|
||||
|
||||
utf8 * Substring(const utf8 * buffer, size_t index)
|
||||
{
|
||||
size_t bufferSize = String::SizeOf(buffer);
|
||||
bool goodSubstring = index <= bufferSize;
|
||||
Guard::Assert(goodSubstring, "Substring past end of input string.");
|
||||
|
||||
// If assertion continues, return empty string to avoid crash
|
||||
if (!goodSubstring)
|
||||
{
|
||||
return String::Duplicate("");
|
||||
}
|
||||
|
||||
return String::Duplicate(buffer + index);
|
||||
}
|
||||
|
||||
utf8 * Substring(const utf8 * buffer, size_t index, size_t size)
|
||||
{
|
||||
size_t bufferSize = String::SizeOf(buffer);
|
||||
bool goodSubstring = index + size <= bufferSize;
|
||||
Guard::Assert(goodSubstring, "Substring past end of input string.");
|
||||
|
||||
// If assertion continues, cap the substring to avoid crash
|
||||
if (!goodSubstring)
|
||||
{
|
||||
if (index >= bufferSize)
|
||||
{
|
||||
size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
size = bufferSize - index;
|
||||
}
|
||||
}
|
||||
|
||||
utf8 * result = Memory::Allocate<utf8>(size + 1);
|
||||
Memory::Copy(result, buffer + index, size);
|
||||
result[size] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> Split(const std::string &s, const std::string &delimiter)
|
||||
{
|
||||
if (delimiter.empty())
|
||||
{
|
||||
throw std::invalid_argument(nameof(delimiter) " can not be empty.");
|
||||
}
|
||||
|
||||
std::vector<std::string> results;
|
||||
if (!s.empty())
|
||||
{
|
||||
size_t index = 0;
|
||||
size_t nextIndex;
|
||||
do
|
||||
{
|
||||
nextIndex = s.find(delimiter, index);
|
||||
std::string value;
|
||||
if (nextIndex == std::string::npos)
|
||||
{
|
||||
value = s.substr(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = s.substr(index, nextIndex - index);
|
||||
}
|
||||
results.push_back(value);
|
||||
index = nextIndex + delimiter.size();
|
||||
}
|
||||
while (nextIndex != SIZE_MAX);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
utf8 * SkipBOM(utf8 * buffer)
|
||||
{
|
||||
return (utf8*)SkipBOM((const utf8 *)buffer);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../common.h"
|
||||
|
||||
namespace String
|
||||
@@ -32,6 +33,7 @@ namespace String
|
||||
bool Equals(const std::string &a, const std::string &b, bool ignoreCase = false);
|
||||
bool Equals(const utf8 * a, const utf8 * b, bool ignoreCase = false);
|
||||
bool StartsWith(const utf8 * str, const utf8 * match, bool ignoreCase = false);
|
||||
size_t IndexOf(const utf8 * str, utf8 match, size_t startIndex = 0);
|
||||
size_t LastIndexOf(const utf8 * str, utf8 match);
|
||||
|
||||
/**
|
||||
@@ -64,6 +66,22 @@ namespace String
|
||||
*/
|
||||
utf8 * DiscardDuplicate(utf8 * * ptr, const utf8 * replacement);
|
||||
|
||||
/**
|
||||
* Creates a new string containing the characters between index and and end of the input string.
|
||||
*/
|
||||
utf8 * Substring(const utf8 * buffer, size_t index);
|
||||
|
||||
/**
|
||||
* Creates a new string containing the characters between index and index + size of the input string.
|
||||
*/
|
||||
utf8 * Substring(const utf8 * buffer, size_t index, size_t size);
|
||||
|
||||
/**
|
||||
* Splits the given string by a delimiter and returns the values as a new string array.
|
||||
* @returns the number of values.
|
||||
*/
|
||||
std::vector<std::string> Split(const std::string &s, const std::string &delimiter);
|
||||
|
||||
utf8 * SkipBOM(utf8 * buffer);
|
||||
const utf8 * SkipBOM(const utf8 * buffer);
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
<ClCompile Include="audio\FileAudioSource.cpp" />
|
||||
<ClCompile Include="audio\MemoryAudioSource.cpp" />
|
||||
<ClCompile Include="audio\NullAudioSource.cpp" />
|
||||
<ClCompile Include="cmdline\UriHandler.cpp" />
|
||||
<ClCompile Include="config\Config.cpp" />
|
||||
<ClCompile Include="config\IniReader.cpp" />
|
||||
<ClCompile Include="config\IniWriter.cpp" />
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,57 @@ 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);
|
||||
#ifdef __MINGW32__
|
||||
// mingw-w64 defines RegSetKeyValueW's signature incorrectly
|
||||
// A fix has already been submitted upstream, this can be be removed after their next release:
|
||||
// https://sourceforge.net/p/mingw-w64/mingw-w64/ci/da9341980a4b70be3563ac09b5927539e7da21f7/
|
||||
RegSetKeyValueW(hMuiCacheKey, NULL, (LPCSTR)buffer, REG_SZ, (LPCSTR)L"OpenRCT2", sizeof(L"OpenRCT2") + 1);
|
||||
#else
|
||||
RegSetKeyValueW(hMuiCacheKey, NULL, buffer, REG_SZ, L"OpenRCT2", sizeof(L"OpenRCT2") + 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
log_verbose("URI protocol setup successful");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_verbose("URI protocol setup failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif
|
||||
|
||||
25
test/tests/AssertHelpers.hpp
Normal file
25
test/tests/AssertHelpers.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <initializer_list>
|
||||
#include <vector>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
template<typename T, typename TExpected>
|
||||
void AssertVector(std::vector<T> actual, TExpected expected)
|
||||
{
|
||||
ASSERT_EQ(actual.size(), expected.size()) <<
|
||||
"Expected vector of size " << expected.size() << ", but was " << actual.size();
|
||||
size_t i = 0;
|
||||
for (auto item : expected)
|
||||
{
|
||||
EXPECT_EQ(actual[i], item) <<
|
||||
"Element at index " << i << " did not match";
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void AssertVector(std::vector<T> actual, std::initializer_list<T> expected)
|
||||
{
|
||||
AssertVector<T, std::initializer_list<T>>(actual, expected);
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <tuple>
|
||||
#include <gtest/gtest.h>
|
||||
#include <openrct2/core/String.hpp>
|
||||
#include "AssertHelpers.hpp"
|
||||
|
||||
using TCase = std::tuple<std::string, std::string>;
|
||||
|
||||
@@ -28,3 +29,23 @@ TEST_P(StringTest, Trim)
|
||||
std::string actual = String::Trim(input);
|
||||
ASSERT_EQ(expected, actual);
|
||||
}
|
||||
|
||||
TEST_F(StringTest, Split_ByComma)
|
||||
{
|
||||
auto actual = String::Split("a,bb,ccc,dd", ",");
|
||||
AssertVector<std::string>(actual, { "a", "bb", "ccc", "dd" });
|
||||
}
|
||||
TEST_F(StringTest, Split_ByColonColon)
|
||||
{
|
||||
auto actual = String::Split("a::bb:ccc:::::dd", "::");
|
||||
AssertVector<std::string>(actual, { "a", "bb:ccc", "", ":dd" });
|
||||
}
|
||||
TEST_F(StringTest, Split_Empty)
|
||||
{
|
||||
auto actual = String::Split("", ".");
|
||||
AssertVector<std::string>(actual, { });
|
||||
}
|
||||
TEST_F(StringTest, Split_ByEmpty)
|
||||
{
|
||||
EXPECT_THROW(String::Split("string", ""), std::invalid_argument);
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<!-- Files -->
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AssertHelpers.hpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="LanguagePackTest.cpp" />
|
||||
|
||||
Reference in New Issue
Block a user