diff --git a/openrct2.sln b/openrct2.sln
index b3adc8b5f0..559ca44fee 100644
--- a/openrct2.sln
+++ b/openrct2.sln
@@ -5,8 +5,6 @@ VisualStudioVersion = 14.0.24720.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openrct2", "openrct2.vcxproj", "{D24D94F6-2A74-480C-B512-629C306CE92F}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libs", "libs.vcxproj", "{074DC930-05C6-4B7F-B5DD-DD237E6E44DB}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -23,14 +21,6 @@ Global
{D24D94F6-2A74-480C-B512-629C306CE92F}.Release XP|Win32.Build.0 = Release XP|Win32
{D24D94F6-2A74-480C-B512-629C306CE92F}.Release|Win32.ActiveCfg = Release|Win32
{D24D94F6-2A74-480C-B512-629C306CE92F}.Release|Win32.Build.0 = Release|Win32
- {074DC930-05C6-4B7F-B5DD-DD237E6E44DB}.Debug|Win32.ActiveCfg = Debug|Win32
- {074DC930-05C6-4B7F-B5DD-DD237E6E44DB}.Debug|Win32.Build.0 = Debug|Win32
- {074DC930-05C6-4B7F-B5DD-DD237E6E44DB}.Release with Tests|Win32.ActiveCfg = Release with Tests|Win32
- {074DC930-05C6-4B7F-B5DD-DD237E6E44DB}.Release with Tests|Win32.Build.0 = Release with Tests|Win32
- {074DC930-05C6-4B7F-B5DD-DD237E6E44DB}.Release XP|Win32.ActiveCfg = Release XP|Win32
- {074DC930-05C6-4B7F-B5DD-DD237E6E44DB}.Release XP|Win32.Build.0 = Release XP|Win32
- {074DC930-05C6-4B7F-B5DD-DD237E6E44DB}.Release|Win32.ActiveCfg = Release|Win32
- {074DC930-05C6-4B7F-B5DD-DD237E6E44DB}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/openrct2.vcxproj b/openrct2.vcxproj
index 0427c85709..eb4fb080d2 100644
--- a/openrct2.vcxproj
+++ b/openrct2.vcxproj
@@ -28,6 +28,7 @@
+
@@ -116,9 +117,6 @@
-
-
-
@@ -292,11 +290,6 @@
-
-
- {074dc930-05c6-4b7f-b5dd-dd237e6e44db}
-
-
{D24D94F6-2A74-480C-B512-629C306CE92F}
openrct2
@@ -348,14 +341,15 @@
- $(SolutionDir)lib;$(SolutionDir)lib\libspeex;$(SolutionDir)lib\sdl\include;$(SolutionDir)lib\libcurl\include;$(SolutionDir)lib\jansson;$(SolutionDir)lib\cutest;$(SolutionDir)lib\SDL2_ttf\include;$(IncludePath)
- $(SolutionDir)lib\sdl\lib\x86;$(SolutionDir)lib\libcurl\lib;$(LibraryPath)
+ $(SolutionDir)lib\include;$(SolutionDir)lib\include\libspeex;$(SolutionDir)lib\include\sdl;$(SolutionDir)lib\include\jansson;$(SolutionDir)lib\include\sdl_ttf;$(SolutionDir)lib\include\libpng;$(IncludePath)
+ $(SolutionDir)lib;$(LibraryPath)
$(SolutionDir)bin\
$(SolutionDir)obj\$(ProjectName)\$(Configuration)\
+
- $(SolutionDir)lib;$(SolutionDir)lib\libspeex;$(SolutionDir)lib\sdl\include;$(SolutionDir)lib\libcurl\include;$(SolutionDir)lib\jansson;$(SolutionDir)lib\cutest;$(SolutionDir)lib\SDL2_ttf\include;$(IncludePath)
- $(SolutionDir)lib\sdl\lib\x86;$(SolutionDir)lib\libcurl\lib;$(LibraryPath)
+ $(SolutionDir)lib\include;$(SolutionDir)lib\include\libspeex;$(SolutionDir)lib\include\sdl;$(SolutionDir)lib\include\jansson;$(SolutionDir)lib\include\sdl_ttf;$(SolutionDir)lib\include\libpng;$(IncludePath)
+ $(SolutionDir)lib;$(LibraryPath)
$(SolutionDir)bin\
$(SolutionDir)obj\$(ProjectName)\$(Configuration)\
@@ -377,7 +371,7 @@
Disabled
true
1Byte
- $(OpenRCT2_DEFINES);DEBUG;_CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;_USE_MATH_DEFINES;CURL_STATICLIB;SDL_MAIN_HANDLED;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)
+ $(OpenRCT2_DEFINES);DEBUG;USE_LIBPNG;_CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;_USE_MATH_DEFINES;CURL_STATICLIB;SDL_MAIN_HANDLED;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)
MultiThreaded
true
$(IntDir)\%(RelativeDir)
@@ -386,7 +380,9 @@
true
- winmm.lib;sdl2.lib;%(AdditionalDependencies)
+ openrct2-libs-vs2015.lib;imm32.lib;version.lib;winmm.lib;%(AdditionalDependencies)
+ UseFastLinkTimeCodeGeneration
+ /OPT:NOLBR %(AdditionalOptions)
@@ -403,7 +399,7 @@
false
- $(OpenRCT2_DEFINES);_CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;_USE_MATH_DEFINES;CURL_STATICLIB;SDL_MAIN_HANDLED;%(PreprocessorDefinitions)
+ $(OpenRCT2_DEFINES);USE_LIBPNG;_CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;_USE_MATH_DEFINES;CURL_STATICLIB;SDL_MAIN_HANDLED;%(PreprocessorDefinitions)
$(IntDir)fake\%(RelativeDir)
true
Speed
@@ -412,7 +408,7 @@
true
true
true
- winmm.lib;sdl2.lib;%(AdditionalDependencies)
+ openrct2-libs-vs2015.lib;imm32.lib;version.lib;winmm.lib;%(AdditionalDependencies)
/ignore:4099 %(AdditionalOptions)
diff --git a/openrct2.vcxproj.filters b/openrct2.vcxproj.filters
index 9c8b9cd76b..e14e10b1c1 100644
--- a/openrct2.vcxproj.filters
+++ b/openrct2.vcxproj.filters
@@ -459,15 +459,6 @@
Source\Windows
-
- Test
-
-
- Test\Management
-
-
- Test\Ride
-
Source\Windows
@@ -564,6 +555,7 @@
Source\Core
+
diff --git a/src/argparse/argparse.c b/src/argparse/argparse.c
new file mode 100644
index 0000000000..3ecd4d1f2e
--- /dev/null
+++ b/src/argparse/argparse.c
@@ -0,0 +1,323 @@
+#include "argparse.h"
+
+#define OPT_UNSET 1
+
+static const char *
+prefix_skip(const char *str, const char *prefix)
+{
+ size_t len = strlen(prefix);
+ return strncmp(str, prefix, len) ? NULL : str + len;
+}
+
+int
+prefix_cmp(const char *str, const char *prefix)
+{
+ for (;; str++, prefix++)
+ if (!*prefix)
+ return 0;
+ else if (*str != *prefix)
+ return (unsigned char)*prefix - (unsigned char)*str;
+}
+
+static void
+argparse_error(struct argparse *this, const struct argparse_option *opt,
+ const char *reason)
+{
+ if (!strncmp(this->argv[0], "--", 2)) {
+ fprintf(stderr, "error: option `%s` %s\n", opt->long_name, reason);
+ exit(1);
+ } else {
+ fprintf(stderr, "error: option `%c` %s\n", opt->short_name, reason);
+ exit(1);
+ }
+}
+
+static int
+argparse_getvalue(struct argparse *this, const struct argparse_option *opt,
+ int flags)
+{
+ const char *s = NULL;
+ if (!opt->value)
+ goto skipped;
+ switch (opt->type) {
+ case ARGPARSE_OPT_BOOLEAN:
+ if (flags & OPT_UNSET) {
+ *(int *)opt->value = *(int *)opt->value - 1;
+ } else {
+ *(int *)opt->value = *(int *)opt->value + 1;
+ }
+ if (*(int *)opt->value < 0) {
+ *(int *)opt->value = 0;
+ }
+ break;
+ case ARGPARSE_OPT_BIT:
+ if (flags & OPT_UNSET) {
+ *(int *)opt->value &= ~opt->data;
+ } else {
+ *(int *)opt->value |= opt->data;
+ }
+ break;
+ case ARGPARSE_OPT_STRING:
+ if (this->optvalue) {
+ *(const char **)opt->value = this->optvalue;
+ this->optvalue = NULL;
+ } else if (this->argc > 1) {
+ this->argc--;
+ *(const char **)opt->value = *++this->argv;
+ } else {
+ argparse_error(this, opt, "requires a value");
+ }
+ break;
+ case ARGPARSE_OPT_INTEGER:
+ if (this->optvalue) {
+ *(int *)opt->value = strtol(this->optvalue, (char **)&s, 0);
+ this->optvalue = NULL;
+ } else if (this->argc > 1) {
+ this->argc--;
+ *(int *)opt->value = strtol(*++this->argv, (char **)&s, 0);
+ } else {
+ argparse_error(this, opt, "requires a value");
+ }
+ if (s[0] != '\0')
+ argparse_error(this, opt, "expects a numerical value");
+ break;
+ default:
+ assert(0);
+ }
+
+skipped:
+ if (opt->callback) {
+ return opt->callback(this, opt);
+ }
+
+ return 0;
+}
+
+static void
+argparse_options_check(const struct argparse_option *options)
+{
+ for (; options->type != ARGPARSE_OPT_END; options++) {
+ switch (options->type) {
+ case ARGPARSE_OPT_END:
+ case ARGPARSE_OPT_BOOLEAN:
+ case ARGPARSE_OPT_BIT:
+ case ARGPARSE_OPT_INTEGER:
+ case ARGPARSE_OPT_STRING:
+ case ARGPARSE_OPT_GROUP:
+ continue;
+ default:
+ fprintf(stderr, "wrong option type: %d", options->type);
+ break;
+ }
+ }
+}
+
+static int
+argparse_short_opt(struct argparse *this, const struct argparse_option *options)
+{
+ for (; options->type != ARGPARSE_OPT_END; options++) {
+ if (options->short_name == *this->optvalue) {
+ this->optvalue = this->optvalue[1] ? this->optvalue + 1 : NULL;
+ return argparse_getvalue(this, options, 0);
+ }
+ }
+ return -2;
+}
+
+static int
+argparse_long_opt(struct argparse *this, const struct argparse_option *options)
+{
+ for (; options->type != ARGPARSE_OPT_END; options++) {
+ const char *rest;
+ int opt_flags = 0;
+ if (!options->long_name)
+ continue;
+
+ rest = prefix_skip(this->argv[0] + 2, options->long_name);
+ if (!rest) {
+ // Negation allowed?
+ if (options->flags & OPT_NONEG) {
+ continue;
+ }
+ // Only boolean/bit allow negation.
+ if (options->type != ARGPARSE_OPT_BOOLEAN && options->type != ARGPARSE_OPT_BIT) {
+ continue;
+ }
+
+ if (!prefix_cmp(this->argv[0] + 2, "no-")) {
+ rest = prefix_skip(this->argv[0] + 2 + 3, options->long_name);
+ if (!rest)
+ continue;
+ opt_flags |= OPT_UNSET;
+ } else {
+ continue;
+ }
+ }
+ if (*rest) {
+ if (*rest != '=')
+ continue;
+ this->optvalue = rest + 1;
+ }
+ return argparse_getvalue(this, options, opt_flags);
+ }
+ return -2;
+}
+
+int
+argparse_init(struct argparse *this, struct argparse_option *options,
+ const char *const *usage, int flags)
+{
+ memset(this, 0, sizeof(*this));
+ this->options = options;
+ this->usage = usage;
+ this->flags = flags;
+ return 0;
+}
+
+int
+argparse_parse(struct argparse *this, int argc, const char **argv)
+{
+ this->argc = argc - 1;
+ this->argv = argv + 1;
+ this->out = argv;
+
+ argparse_options_check(this->options);
+
+ for (; this->argc; this->argc--, this->argv++) {
+ const char *arg = this->argv[0];
+ if (arg[0] != '-' || !arg[1]) {
+ if (this->flags & ARGPARSE_STOP_AT_NON_OPTION) {
+ goto end;
+ }
+ // if it's not option or is a single char '-', copy verbatimly
+ this->out[this->cpidx++] = this->argv[0];
+ continue;
+ }
+ // short option
+ if (arg[1] != '-') {
+ this->optvalue = arg + 1;
+ switch (argparse_short_opt(this, this->options)) {
+ case -1:
+ break;
+ case -2:
+ goto unknown;
+ }
+ while (this->optvalue) {
+ switch (argparse_short_opt(this, this->options)) {
+ case -1:
+ break;
+ case -2:
+ goto unknown;
+ }
+ }
+ continue;
+ }
+ // if '--' presents
+ if (!arg[2]) {
+ this->argc--;
+ this->argv++;
+ break;
+ }
+ // long option
+ switch (argparse_long_opt(this, this->options)) {
+ case -1:
+ break;
+ case -2:
+ goto unknown;
+ }
+ continue;
+
+unknown:
+ fprintf(stderr, "error: unknown option `%s`\n", this->argv[0]);
+ argparse_usage(this);
+ exit(1);
+ }
+
+end:
+ memmove(this->out + this->cpidx, this->argv,
+ this->argc * sizeof(*this->out));
+ this->out[this->cpidx + this->argc] = NULL;
+
+ return this->cpidx + this->argc;
+}
+
+void
+argparse_usage(struct argparse *this)
+{
+ fprintf(stdout, "Usage: %s\n", *this->usage++);
+ while (*this->usage && **this->usage)
+ fprintf(stdout, " or: %s\n", *this->usage++);
+ fputc('\n', stdout);
+
+ const struct argparse_option *options;
+
+ // figure out best width
+ size_t usage_opts_width = 0;
+ size_t len;
+ options = this->options;
+ for (; options->type != ARGPARSE_OPT_END; options++) {
+ len = 0;
+ if ((options)->short_name) {
+ len += 2;
+ }
+ if ((options)->short_name && (options)->long_name) {
+ len += 2; // separator ", "
+ }
+ if ((options)->long_name) {
+ len += strlen((options)->long_name) + 2;
+ }
+ if (options->type == ARGPARSE_OPT_INTEGER) {
+ len += strlen("=");
+ } else if (options->type == ARGPARSE_OPT_STRING) {
+ len += strlen("=");
+ }
+ len = ceil((float)len / 4) * 4;
+ if (usage_opts_width < len) {
+ usage_opts_width = len;
+ }
+ }
+ usage_opts_width += 4; // 4 spaces prefix
+
+ options = this->options;
+ for (; options->type != ARGPARSE_OPT_END; options++) {
+ size_t pos = 0;
+ int pad = 0;
+ if (options->type == ARGPARSE_OPT_GROUP) {
+ fputc('\n', stdout);
+ pos += fprintf(stdout, "%s", options->help);
+ fputc('\n', stdout);
+ continue;
+ }
+ pos = fprintf(stdout, " ");
+ if (options->short_name) {
+ pos += fprintf(stdout, "-%c", options->short_name);
+ }
+ if (options->long_name && options->short_name) {
+ pos += fprintf(stdout, ", ");
+ }
+ if (options->long_name) {
+ pos += fprintf(stdout, "--%s", options->long_name);
+ }
+ if (options->type == ARGPARSE_OPT_INTEGER) {
+ pos += fprintf(stdout, "=");
+ } else if (options->type == ARGPARSE_OPT_STRING) {
+ pos += fprintf(stdout, "=");
+ }
+ if (pos <= usage_opts_width) {
+ pad = usage_opts_width - pos;
+ } else {
+ fputc('\n', stdout);
+ pad = usage_opts_width;
+ }
+ fprintf(stdout, "%*s%s\n", pad + 2, "", options->help);
+ }
+}
+
+int
+argparse_help_cb(struct argparse *this, const struct argparse_option *option)
+{
+ (void)option;
+ argparse_usage(this);
+ exit(0);
+ return 0;
+}
diff --git a/src/argparse/argparse.h b/src/argparse/argparse.h
new file mode 100644
index 0000000000..350ee58df4
--- /dev/null
+++ b/src/argparse/argparse.h
@@ -0,0 +1,132 @@
+#ifndef ARGPARSE_H
+#define ARGPARSE_H
+/**
+ * Command-line arguments parsing library.
+ *
+ * This module is inspired by parse-options.c (git) and python's argparse
+ * module.
+ *
+ * Arguments parsing is common task in cli program, but traditional `getopt`
+ * libraries are not easy to use. This library provides high-level arguments
+ * parsing solutions.
+ *
+ * The program defines what arguments it requires, and `argparse` will figure
+ * out how to parse those out of `argc` and `argv`, it also automatically
+ * generates help and usage messages and issues errors when users give the
+ * program invalid arguments.
+ *
+ * Reserved namespaces:
+ * argparse
+ * OPT
+ * Author: Yecheng Fu
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+struct argparse;
+struct argparse_option;
+
+typedef int argparse_callback(struct argparse *this,
+ const struct argparse_option *option);
+
+enum argparse_flag {
+ ARGPARSE_STOP_AT_NON_OPTION = 1,
+};
+
+enum argparse_option_type {
+ /* special */
+ ARGPARSE_OPT_END,
+ ARGPARSE_OPT_GROUP,
+ /* options with no arguments */
+ ARGPARSE_OPT_BOOLEAN,
+ ARGPARSE_OPT_BIT,
+ /* options with arguments (optional or required) */
+ ARGPARSE_OPT_INTEGER,
+ ARGPARSE_OPT_STRING,
+};
+
+enum argparse_option_flags {
+ OPT_NONEG = 1, /* Negation disabled. */
+};
+
+/*
+ * Argparse option struct.
+ *
+ * `type`:
+ * holds the type of the option, you must have an ARGPARSE_OPT_END last in your
+ * array.
+ *
+ * `short_name`:
+ * the character to use as a short option name, '\0' if none.
+ *
+ * `long_name`:
+ * the long option name, without the leading dash, NULL if none.
+ *
+ * `value`:
+ * stores pointer to the value to be filled.
+ *
+ * `help`:
+ * the short help message associated to what the option does.
+ * Must never be NULL (except for ARGPARSE_OPT_END).
+ *
+ * `callback`:
+ * function is called when corresponding argument is parsed.
+ *
+ * `data`:
+ * associated data. Callbacks can use it like they want.
+ *
+ * `flags`:
+ * option flags.
+ *
+ */
+struct argparse_option {
+ enum argparse_option_type type;
+ const char short_name;
+ const char *long_name;
+ void *value;
+ const char *help;
+ argparse_callback *callback;
+ intptr_t data;
+ int flags;
+};
+
+/*
+ * argpparse
+ */
+struct argparse {
+ // user supplied
+ const struct argparse_option *options;
+ const char *const *usage;
+ int flags;
+ // internal context
+ int argc;
+ const char **argv;
+ const char **out;
+ int cpidx;
+ const char *optvalue; // current option value
+};
+
+// builtin callbacks
+int argparse_help_cb(struct argparse *this,
+ const struct argparse_option *option);
+
+// builtin option macros
+#define OPT_END() { ARGPARSE_OPT_END }
+#define OPT_BOOLEAN(...) { ARGPARSE_OPT_BOOLEAN, __VA_ARGS__ }
+#define OPT_BIT(...) { ARGPARSE_OPT_BIT, __VA_ARGS__ }
+#define OPT_INTEGER(...) { ARGPARSE_OPT_INTEGER, __VA_ARGS__ }
+#define OPT_STRING(...) { ARGPARSE_OPT_STRING, __VA_ARGS__ }
+#define OPT_GROUP(h) { ARGPARSE_OPT_GROUP, 0, NULL, NULL, h, NULL }
+#define OPT_HELP() OPT_BOOLEAN('h', "help", NULL, "show this help message and exit", argparse_help_cb)
+
+int argparse_init(struct argparse *this, struct argparse_option *options,
+ const char *const *usage, int flags);
+int argparse_parse(struct argparse *this, int argc, const char **argv);
+void argparse_usage(struct argparse *this);
+
+#endif
diff --git a/src/cmdline.c b/src/cmdline.c
index 003f6faba4..c4fb495947 100644
--- a/src/cmdline.c
+++ b/src/cmdline.c
@@ -19,7 +19,7 @@
*****************************************************************************/
#include
-#include
+#include "argparse/argparse.h"
#include "addresses.h"
#include "cmdline.h"
#include "interface/screenshot.h"