From 9d98147b75af9b38b6f005b2b1ea962c6e757c4a Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Sun, 10 Jan 2016 17:54:46 +0000 Subject: [PATCH] new-argparse: implement argument and option parsing --- openrct2.vcxproj | 3 +- openrct2.vcxproj.filters | 3 +- openrct2.vcxproj.user | 1 + src/cmdline/CommandLine.cpp | 274 +++++++++++++++++++++++-- src/cmdline/CommandLine.hpp | 19 +- src/cmdline/CommandLineDefinitions.cpp | 104 ---------- src/cmdline/RootCommands.cpp | 157 ++++++++++++++ src/core/Console.cpp | 58 ++++++ src/core/Console.hpp | 31 +-- src/core/String.hpp | 27 +++ src/platform/windows.c | 2 +- 11 files changed, 530 insertions(+), 149 deletions(-) delete mode 100644 src/cmdline/CommandLineDefinitions.cpp create mode 100644 src/cmdline/RootCommands.cpp create mode 100644 src/core/Console.cpp diff --git a/openrct2.vcxproj b/openrct2.vcxproj index 33e2ee8ed9..1b663f40b6 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -26,9 +26,10 @@ - + + diff --git a/openrct2.vcxproj.filters b/openrct2.vcxproj.filters index 468197b324..a27804dbe2 100644 --- a/openrct2.vcxproj.filters +++ b/openrct2.vcxproj.filters @@ -564,8 +564,9 @@ Source\argparse - + + diff --git a/openrct2.vcxproj.user b/openrct2.vcxproj.user index fb13b2fbe1..6e6c19d6fb 100644 --- a/openrct2.vcxproj.user +++ b/openrct2.vcxproj.user @@ -7,6 +7,7 @@ $(TargetDir)\openrct2.exe WindowsLocalDebugger $(TargetDir) + --port=233 $(TargetDir) diff --git a/src/cmdline/CommandLine.cpp b/src/cmdline/CommandLine.cpp index cee374d595..fdac6a6d7a 100644 --- a/src/cmdline/CommandLine.cpp +++ b/src/cmdline/CommandLine.cpp @@ -8,6 +8,8 @@ extern "C" #include "../core/String.hpp" #include "CommandLine.hpp" +#pragma region CommandLineArgEnumerator + CommandLineArgEnumerator::CommandLineArgEnumerator(const char * const * arguments, int count) { _arguments = arguments; @@ -20,6 +22,19 @@ void CommandLineArgEnumerator::Reset() _index = 0; } +bool CommandLineArgEnumerator::Backtrack() +{ + if (_index > 0) + { + _index--; + return true; + } + else + { + return false; + } +} + bool CommandLineArgEnumerator::TryPop() { if (_index < _count) @@ -71,6 +86,8 @@ bool CommandLineArgEnumerator::TryPopString(const char * * result) } } +#pragma endregion + namespace CommandLine { constexpr const char * HelpText = "openrct2 -ha shows help for all commands. " @@ -80,6 +97,13 @@ namespace CommandLine static void PrintExamples(const CommandLineExample *examples); static utf8 * GetOptionCaption(utf8 * buffer, size_t bufferSize, const CommandLineOptionDefinition *option); + static const CommandLineOptionDefinition * FindOption(const CommandLineOptionDefinition * options, char shortName); + static const CommandLineOptionDefinition * FindOption(const CommandLineOptionDefinition * options, const char * longName); + + static bool ParseShortOption(const CommandLineOptionDefinition * options, CommandLineArgEnumerator *argEnumerator, const char *argument); + static bool ParseLongOption(const CommandLineOptionDefinition * options, CommandLineArgEnumerator * argEnumerator, const char * argument); + static bool ParseOptionValue(const CommandLineOptionDefinition * option, const char * valueString); + void PrintHelp() { const CommandLineCommand * command; @@ -215,19 +239,23 @@ namespace CommandLine return buffer; } - const CommandLineCommand * FindCommandFor(const CommandLineCommand * commands, const char * const * arguments, int count) + void PrintUsageFor(const char * command) { - // Check if there are any arguments - if (count == 0) - { - return nullptr; - } - // Check if options have started - const char * firstArgument = arguments[0]; + } + + const CommandLineCommand * FindCommandFor(const CommandLineCommand * commands, CommandLineArgEnumerator *argEnumerator) + { + // Check if end of arguments or options have started + const char * firstArgument; + if (!argEnumerator->TryPopString(&firstArgument)) + { + return commands; + } if (firstArgument[0] == '-') { - return nullptr; + argEnumerator->Backtrack(); + return commands; } // Search through defined commands for one that matches @@ -250,25 +278,241 @@ namespace CommandLine else { // Recurse for the sub command table - return FindCommandFor(command->SubCommands, &arguments[1], count - 1); + return FindCommandFor(command->SubCommands, argEnumerator); } } } return fallback; } -} -void CommandLineDisplayUsageFor(const char * command) -{ + static bool ParseOptions(const CommandLineOptionDefinition * options, CommandLineArgEnumerator *argEnumerator) + { + const char * argument; + if (argEnumerator->TryPopString(&argument)) + { + if (argument[0] == '-') + { + if (argument[1] == '-') + { + if (!ParseLongOption(options, argEnumerator, &argument[2])) + { + return false; + } + } + else + { + if (!ParseShortOption(options, argEnumerator, argument)) + { + return false; + } + } + } + else + { + Console::WriteLineError("All options must be passed at the end of the command line."); + return false; + } + } + return true; + } + + static bool ParseLongOption(const CommandLineOptionDefinition * options, + CommandLineArgEnumerator *argEnumerator, + const char *argument) + { + // Get just the option name + char optionName[64]; + const char * equalsCh = strchr(argument, '='); + if (equalsCh != nullptr) + { + String::Set(optionName, sizeof(optionName), argument, equalsCh - argument); + } + else + { + String::Set(optionName, sizeof(optionName), argument); + } + + // Find a matching option definition + const CommandLineOptionDefinition * option = FindOption(options, optionName); + if (option == nullptr) + { + Console::WriteError("Unknown option: --"); + Console::WriteLineError(optionName); + return false; + } + + if (equalsCh == nullptr) + { + if (option->Type == CMDLINE_TYPE_SWITCH) + { + ParseOptionValue(option, nullptr); + } + else + { + const char * valueString = nullptr; + if (!argEnumerator->TryPopString(&valueString)) + { + Console::WriteError("Expected value for option: "); + Console::WriteLineError(optionName); + return false; + } + + if (!ParseOptionValue(option, valueString)) + { + return false; + } + } + } + else + { + if (option->Type == CMDLINE_TYPE_SWITCH) + { + Console::WriteError("Option is a switch: "); + Console::WriteLineError(optionName); + return false; + } + else + { + if (!ParseOptionValue(option, equalsCh + 1)) + { + return false; + } + } + } + + return true; + } + + static bool ParseShortOption(const CommandLineOptionDefinition * options, + CommandLineArgEnumerator *argEnumerator, + const char *argument) + { + const CommandLineOptionDefinition * option = nullptr; + + const char * shortOption = &argument[1]; + for (; *shortOption != '\0'; shortOption++) + { + option = FindOption(options, shortOption[0]); + if (option == nullptr) + { + Console::WriteError("Unknown option: -"); + Console::WriteError(shortOption[0]); + Console::WriteLineError(); + return false; + } + if (option->Type == CMDLINE_TYPE_SWITCH) + { + if (!ParseOptionValue(option, nullptr)) + { + return false; + } + } + else if (shortOption[1] != '\0') + { + if (!ParseOptionValue(option, &shortOption[1])) + { + return false; + } + return true; + } + } + + if (option != nullptr && option->Type != CMDLINE_TYPE_SWITCH) + { + const char * valueString = nullptr; + if (!argEnumerator->TryPopString(&valueString)) + { + Console::WriteError("Expected value for option: "); + Console::WriteError(option->ShortName); + Console::WriteLineError(); + return false; + } + + if (!ParseOptionValue(option, valueString)) + { + return false; + } + } + + return true; + } + + static bool ParseOptionValue(const CommandLineOptionDefinition * option, const char * valueString) + { + if (option->OutAddress == nullptr) return true; + + switch (option->Type) { + case CMDLINE_TYPE_SWITCH: + *((bool *)option->OutAddress) = true; + return true; + case CMDLINE_TYPE_INTEGER: + *((sint32 *)option->OutAddress) = (sint32)atol(valueString); + return true; + case CMDLINE_TYPE_REAL: + *((float *)option->OutAddress) = (float)atof(valueString); + return true; + case CMDLINE_TYPE_STRING: + *((utf8 * *)option->OutAddress) = String::Duplicate(valueString); + return true; + default: + Console::WriteError("Unknown CMDLINE_TYPE for: "); + Console::WriteLineError(option->LongName); + return false; + } + } + + const CommandLineOptionDefinition * FindOption(const CommandLineOptionDefinition * options, char shortName) + { + for (const CommandLineOptionDefinition * option = options; option->Type != 255; option++) + { + if (option->ShortName == shortName) + { + return option; + } + } + return nullptr; + } + + const CommandLineOptionDefinition * FindOption(const CommandLineOptionDefinition * options, const char * longName) + { + for (const CommandLineOptionDefinition * option = options; option->Type != 255; option++) + { + if (String::Equals(option->LongName, longName)) + { + return option; + } + } + return nullptr; + } } extern "C" { int cmdline_run(const char * * argv, int argc) { - CommandLine::PrintHelp(); - return 0; + auto argEnumerator = CommandLineArgEnumerator(argv, argc); + + // Pop process path + argEnumerator.TryPop(); + + const CommandLineCommand * command = CommandLine::FindCommandFor(CommandLine::RootCommands, &argEnumerator); + if (command->Options != nullptr) + { + auto argEnumeratorForOptions = CommandLineArgEnumerator(argEnumerator); + if (!CommandLine::ParseOptions(command->Options, &argEnumeratorForOptions)) + { + return EXITCODE_FAIL; + } + } + if (command == CommandLine::RootCommands || command->Func == nullptr) + { + return CommandLine::HandleCommandDefault(); + } + else + { + return command->Func(&argEnumerator); + } } } diff --git a/src/cmdline/CommandLine.hpp b/src/cmdline/CommandLine.hpp index 234eb9ceb6..8a1c340c5b 100644 --- a/src/cmdline/CommandLine.hpp +++ b/src/cmdline/CommandLine.hpp @@ -22,6 +22,7 @@ public: CommandLineArgEnumerator(const char * const * arguments, int count); void Reset(); + bool Backtrack(); bool TryPop(); bool TryPopInteger(sint32 * result); bool TryPopReal(float * result); @@ -31,6 +32,13 @@ public: typedef int exitcode_t; typedef exitcode_t (*CommandLineFunc)(CommandLineArgEnumerator *); +enum +{ + EXITCODE_FAIL = -1, + EXITCODE_OK = 0, + EXITCODE_CONTINUE = 1, +}; + struct CommandLineExample { const char * Arguments; @@ -72,12 +80,13 @@ constexpr char NAC = '\0'; #define DefineCommand(name, params, options, func) { name, params, options, NULL, func } #define DefineSubCommand(name, subcommandtable) { name, "", NULL, subcommandtable, NULL } -void CommandLineDisplayUsageFor(const char * command); - -extern const CommandLineCommand RootCommands[]; -extern const CommandLineExample RootExamples[]; - namespace CommandLine { + extern const CommandLineCommand RootCommands[]; + extern const CommandLineExample RootExamples[]; + void PrintHelp(); + void PrintUsageFor(const char * command); + + exitcode_t HandleCommandDefault(); } diff --git a/src/cmdline/CommandLineDefinitions.cpp b/src/cmdline/CommandLineDefinitions.cpp deleted file mode 100644 index 68a0a9cc80..0000000000 --- a/src/cmdline/CommandLineDefinitions.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "CommandLine.hpp" - -const CommandLineCommand * ScreenshotCommandTable; -const CommandLineCommand * SpriteCommandTable; - -static bool _help; -static bool _version; -static bool _noInstall; - -const CommandLineOptionDefinition StandardOptions[] -{ - { CMDLINE_TYPE_SWITCH, &_help, 'h', "help", "show this help message and exit" }, - { CMDLINE_TYPE_SWITCH, &_version, 'v', "version", "show version information and exit" }, - { CMDLINE_TYPE_SWITCH, &_noInstall, 'n', "no-install", "do not install scenario if passed" }, - { CMDLINE_TYPE_SWITCH, nullptr, NAC, "no-install", "show information about " OPENRCT2_NAME }, - { CMDLINE_TYPE_SWITCH, nullptr, NAC, "verbose", "log verbose messages" }, - { CMDLINE_TYPE_SWITCH, nullptr, NAC, "headless", "run " OPENRCT2_NAME " headless" }, - { CMDLINE_TYPE_INTEGER, nullptr, NAC, "port", "port to use for hosting or joining a server" }, - { CMDLINE_TYPE_STRING, nullptr, NAC, "user-data-path", "path to the user data directory (containing config.ini)" }, - { CMDLINE_TYPE_STRING, nullptr, NAC, "openrct-data-path", "path to the OpenRCT2 data directory (containing languages)" }, - OptionTableEnd -}; - -const CommandLineCommand RootCommands[] -{ - // Main commands - DefineCommand("", "", StandardOptions, nullptr), - DefineCommand("intro", "", StandardOptions, nullptr), - DefineCommand("host", "", StandardOptions, nullptr), - DefineCommand("join", "", StandardOptions, nullptr), - - // Sub-commands - DefineSubCommand("screenshot", ScreenshotCommandTable), - DefineSubCommand("sprite", SpriteCommandTable ), - - CommandTableEnd -}; - -const CommandLineExample RootExamples[] -{ - { "./my_park.sv6", "open a saved park" }, - { "./SnowyPark.sc6", "install and open a scenario" }, - { "./ShuttleLoop.td6", "install a track" }, - { "http:/openrct2.website/files/SnowyPark.sv6", "download and open a saved park" }, - { "host ./my_park.sv6 --port 11753 --headless", "run a headless server for a saved park" }, - ExampleTableEnd -}; - -void HandleNoCommand(CommandLineArgEnumerator * enumerator) -{ - -} - -void HandleCommandIntro(CommandLineArgEnumerator * enumerator) -{ - -} - -void HandleCommandHost(CommandLineArgEnumerator * enumerator) -{ - const char * parkUri; - - if (!enumerator->TryPopString(&parkUri)) - { - fprintf(stderr, "Expected path or URL to a saved park."); - CommandLineDisplayUsageFor("host"); - } -} - -void HandleCommandJoin(CommandLineArgEnumerator * enumerator) -{ - const char * hostname; - - if (!enumerator->TryPopString(&hostname)) - { - fprintf(stderr, "Expected a hostname or IP address to the server to connect to."); - CommandLineDisplayUsageFor("join"); - } -} - - - -const CommandLineCommand ScreenshotCommands[] -{ - // Main commands - DefineCommand("", " [ ]", nullptr, nullptr), - DefineCommand("", " giant ", nullptr, nullptr), - CommandTableEnd -}; - -const CommandLineOptionDefinition SpriteOptions[] -{ - { CMDLINE_TYPE_STRING, nullptr, 'm', "mode", "the type of sprite conversion " }, - OptionTableEnd -}; - -const CommandLineCommand SpriteCommands[] -{ - // Main commands - DefineCommand("details", " [idx]", SpriteOptions, nullptr), - DefineCommand("export", " ", SpriteOptions, nullptr), - DefineCommand("build", " [silent]", SpriteOptions, nullptr), - CommandTableEnd -}; diff --git a/src/cmdline/RootCommands.cpp b/src/cmdline/RootCommands.cpp new file mode 100644 index 0000000000..eb6aa3351c --- /dev/null +++ b/src/cmdline/RootCommands.cpp @@ -0,0 +1,157 @@ +#include "../core/Console.hpp" +#include "CommandLine.hpp" + +const CommandLineCommand * ScreenshotCommandTable; +const CommandLineCommand * SpriteCommandTable; + +static bool _help = false; +static bool _version = false; +static bool _noInstall = false; +static bool _about = false; +static bool _verbose = false; +static bool _headless = false; +static uint32 _port = 0; +static utf8 * _userDataPath = nullptr; +static utf8 * _openrctDataPath = nullptr; + +static const CommandLineOptionDefinition StandardOptions[] +{ + { CMDLINE_TYPE_SWITCH, &_help, 'h', "help", "show this help message and exit" }, + { CMDLINE_TYPE_SWITCH, &_version, 'v', "version", "show version information and exit" }, + { CMDLINE_TYPE_SWITCH, &_noInstall, 'n', "no-install", "do not install scenario if passed" }, + { CMDLINE_TYPE_SWITCH, &_about, NAC, "about" , "show information about " OPENRCT2_NAME }, + { CMDLINE_TYPE_SWITCH, &_verbose, NAC, "verbose", "log verbose messages" }, + { CMDLINE_TYPE_SWITCH, &_headless, NAC, "headless", "run " OPENRCT2_NAME " headless" }, + { CMDLINE_TYPE_INTEGER, &_port, NAC, "port", "port to use for hosting or joining a server" }, + { CMDLINE_TYPE_STRING, &_userDataPath, NAC, "user-data-path", "path to the user data directory (containing config.ini)" }, + { CMDLINE_TYPE_STRING, &_openrctDataPath, NAC, "openrct-data-path", "path to the OpenRCT2 data directory (containing languages)" }, + OptionTableEnd +}; + +static exitcode_t HandleNoCommand(CommandLineArgEnumerator * enumerator); +static exitcode_t HandleCommandIntro(CommandLineArgEnumerator * enumerator); +static exitcode_t HandleCommandHost(CommandLineArgEnumerator * enumerator); +static exitcode_t HandleCommandJoin(CommandLineArgEnumerator * enumerator); + +const CommandLineCommand CommandLine::RootCommands[] +{ + // Main commands + DefineCommand("", "", StandardOptions, HandleNoCommand ), + DefineCommand("intro", "", StandardOptions, HandleCommandIntro), + DefineCommand("host", "", StandardOptions, HandleCommandHost ), + DefineCommand("join", "", StandardOptions, HandleCommandJoin ), + + // Sub-commands + DefineSubCommand("screenshot", ScreenshotCommandTable), + DefineSubCommand("sprite", SpriteCommandTable ), + + CommandTableEnd +}; + +static const CommandLineExample CommandLine::RootExamples[] +{ + { "./my_park.sv6", "open a saved park" }, + { "./SnowyPark.sc6", "install and open a scenario" }, + { "./ShuttleLoop.td6", "install a track" }, + { "http:/openrct2.website/files/SnowyPark.sv6", "download and open a saved park" }, + { "host ./my_park.sv6 --port 11753 --headless", "run a headless server for a saved park" }, + ExampleTableEnd +}; + +exitcode_t CommandLine::HandleCommandDefault() +{ + if (_help) + { + CommandLine::PrintHelp(); + return EXITCODE_OK; + } + + return EXITCODE_CONTINUE; +} + +exitcode_t HandleNoCommand(CommandLineArgEnumerator * enumerator) +{ + exitcode_t result = CommandLine::HandleCommandDefault(); + if (result != EXITCODE_CONTINUE) + { + return result; + } + + // Process URI + + return EXITCODE_CONTINUE; +} + +exitcode_t HandleCommandIntro(CommandLineArgEnumerator * enumerator) +{ + exitcode_t result = CommandLine::HandleCommandDefault(); + if (result != EXITCODE_CONTINUE) + { + return result; + } + + return EXITCODE_CONTINUE; +} + +exitcode_t HandleCommandHost(CommandLineArgEnumerator * enumerator) +{ + const char * parkUri; + + exitcode_t result = CommandLine::HandleCommandDefault(); + if (result != EXITCODE_CONTINUE) + { + return result; + } + + if (!enumerator->TryPopString(&parkUri)) + { + Console::WriteLineError("Expected path or URL to a saved park."); + CommandLine::PrintUsageFor("host"); + return EXITCODE_FAIL; + } + + return EXITCODE_CONTINUE; +} + +exitcode_t HandleCommandJoin(CommandLineArgEnumerator * enumerator) +{ + const char * hostname; + + exitcode_t result = CommandLine::HandleCommandDefault(); + if (result != EXITCODE_CONTINUE) + { + return result; + } + + if (!enumerator->TryPopString(&hostname)) + { + Console::WriteLineError("Expected a hostname or IP address to the server to connect to."); + CommandLine::PrintUsageFor("join"); + return EXITCODE_FAIL; + } + + return EXITCODE_CONTINUE; +} + +const CommandLineCommand ScreenshotCommands[] +{ + // Main commands + DefineCommand("", " [ ]", nullptr, nullptr), + DefineCommand("", " giant ", nullptr, nullptr), + CommandTableEnd +}; + +const CommandLineOptionDefinition SpriteOptions[] +{ + { CMDLINE_TYPE_STRING, nullptr, 'm', "mode", "the type of sprite conversion " }, + OptionTableEnd +}; + +const CommandLineCommand SpriteCommands[] +{ + // Main commands + DefineCommand("details", " [idx]", SpriteOptions, nullptr), + DefineCommand("export", " ", SpriteOptions, nullptr), + DefineCommand("build", " [silent]", SpriteOptions, nullptr), + CommandTableEnd +}; diff --git a/src/core/Console.cpp b/src/core/Console.cpp new file mode 100644 index 0000000000..e8e939239b --- /dev/null +++ b/src/core/Console.cpp @@ -0,0 +1,58 @@ +extern "C" +{ + #include "../platform/platform.h" +} + +#include "Console.hpp" + +namespace Console +{ + void Write(char c) + { + fputc(c, stdout); + } + + void Write(const utf8 * str) + { + fputs(str, stdout); + } + + void WriteSpace(size_t count) + { + for (size_t i = 0; i < count; i++) + { + Write(' '); + } + } + + void WriteLine() + { + puts(""); + } + + void WriteLine(const utf8 * str) + { + puts(str); + } + + void WriteError(char c) + { + fputc(c, stderr); + } + + void WriteError(const utf8 * str) + { + fputs(str, stderr); + } + + void WriteLineError() + { + fputs(platform_get_new_line(), stderr); + } + + void WriteLineError(const utf8 * str) + { + fputs(str, stderr); + WriteLineError(); + } +} \ No newline at end of file diff --git a/src/core/Console.hpp b/src/core/Console.hpp index ae7ee378aa..f1322f31ec 100644 --- a/src/core/Console.hpp +++ b/src/core/Console.hpp @@ -7,26 +7,13 @@ extern "C" namespace Console { - void Write(const utf8 * str) - { - fputs(str, stdout); - } - - void WriteSpace(size_t count) - { - for (size_t i = 0; i < count; i++) - { - fputc(' ', stdout); - } - } - - void WriteLine() - { - puts(""); - } - - void WriteLine(const utf8 * str) - { - puts(str); - } + void Write(char c); + void Write(const utf8 * str); + void WriteSpace(size_t count); + void WriteLine(); + void WriteLine(const utf8 * str); + void WriteError(char c); + void WriteError(const utf8 * str); + void WriteLineError(); + void WriteLineError(const utf8 * str); } diff --git a/src/core/String.hpp b/src/core/String.hpp index ae0219c28f..5856263bef 100644 --- a/src/core/String.hpp +++ b/src/core/String.hpp @@ -7,6 +7,8 @@ extern "C" #include "../util/util.h" } +#include "Memory.hpp" + namespace String { bool Equals(const utf8 * a, const utf8 * b, bool ignoreCase = false) @@ -34,6 +36,25 @@ namespace String return strlen(str); } + utf8 * Set(utf8 * buffer, size_t bufferSize, const utf8 * src) + { + return safe_strncpy(buffer, src, bufferSize); + } + + utf8 * Set(utf8 * buffer, size_t bufferSize, const utf8 * src, size_t srcSize) + { + utf8 * dst = buffer; + size_t minSize = Math::Min(bufferSize - 1, srcSize); + for (size_t i = 0; i < minSize; i++) + { + *dst++ = *src; + if (*src == '\0') break; + *src++; + } + *dst = '\0'; + return buffer; + } + utf8 * Append(utf8 * buffer, size_t bufferSize, const utf8 * src) { return safe_strcat(buffer, src, bufferSize); @@ -77,4 +98,10 @@ namespace String return buffer; } + + utf8 * Duplicate(const utf8 * src) + { + size_t srcSize = SizeOf(src); + return Memory::DuplicateArray(src, srcSize + 1); + } } diff --git a/src/platform/windows.c b/src/platform/windows.c index 9709de3e46..eb8e83f3f4 100644 --- a/src/platform/windows.c +++ b/src/platform/windows.c @@ -94,7 +94,7 @@ __declspec(dllexport) int StartOpenRCT(HINSTANCE hInstance, HINSTANCE hPrevInsta } free(argv); - if (runGame) { + if (runGame == 1) { openrct2_launch(); }