diff --git a/external/cflags/.gitignore b/external/cflags/.gitignore
new file mode 100644
index 00000000..abef3588
--- /dev/null
+++ b/external/cflags/.gitignore
@@ -0,0 +1,4 @@
+*.vs
+*.vscode
+/[Bb]uild
+
diff --git a/external/cflags/CMakeLists.txt b/external/cflags/CMakeLists.txt
new file mode 100644
index 00000000..bf7cc8cf
--- /dev/null
+++ b/external/cflags/CMakeLists.txt
@@ -0,0 +1,121 @@
+cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
+
+project(
+ cflags
+ LANGUAGES C CXX
+ DESCRIPTION "Command line flag parsing library in C/C++"
+ VERSION 3.0.3
+)
+
+###
+### cflags
+###
+
+add_library(cflags INTERFACE)
+add_library(cflags::cflags ALIAS cflags)
+
+target_include_directories(
+ cflags INTERFACE
+ $
+ $
+)
+
+###
+### cppflags
+###
+
+add_library(cppflags INTERFACE)
+add_library(cflags::cppflags ALIAS cppflags)
+
+target_include_directories(
+ cppflags INTERFACE
+ $
+ $
+)
+
+set_target_properties(
+ cppflags
+ PROPERTIES
+ CXX_STANDARD 17
+ CXX_STANDARD_REQUIRED ON
+)
+
+###
+### Install
+###
+
+install(
+ TARGETS cflags cppflags
+ EXPORT cflagsTargets
+ INCLUDES DESTINATION include
+)
+
+include(CMakePackageConfigHelpers)
+
+write_basic_package_version_file(
+ ${CMAKE_CURRENT_BINARY_DIR}/cflagsConfigVersion.cmake
+ VERSION ${PROJECT_VERSION}
+ COMPATIBILITY AnyNewerVersion
+)
+
+install(
+ EXPORT cflagsTargets
+ NAMESPACE cflags::
+ DESTINATION lib/cmake/cflags
+)
+
+configure_package_config_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cflagsConfig.cmake.in
+ ${CMAKE_CURRENT_BINARY_DIR}/cflagsConfig.cmake
+ INSTALL_DESTINATION lib/cmake/cflags
+)
+
+install(
+ FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/cflagsConfigVersion.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/cflagsConfig.cmake
+ DESTINATION lib/cmake/cflags
+)
+
+include(FindPkgConfig)
+
+if(PKG_CONFIG_FOUND)
+ configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cflags.pc.in
+ ${CMAKE_CURRENT_BINARY_DIR}/cflags.pc
+ @ONLY
+ )
+
+ install(
+ FILES ${CMAKE_CURRENT_BINARY_DIR}/cflags.pc
+ DESTINATION share/pkgconfig
+ )
+endif()
+
+install(
+ DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/
+ DESTINATION include
+)
+
+###
+### Test executables
+###
+
+if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
+
+ add_executable(example examples/example.c)
+
+ target_link_libraries(example cflags)
+
+ add_executable(example-cpp examples/example.cpp)
+
+ target_link_libraries(example-cpp cppflags)
+
+ set_target_properties(
+ example-cpp
+ PROPERTIES
+ CXX_STANDARD 17
+ CXX_STANDARD_REQUIRED ON
+ )
+
+endif()
\ No newline at end of file
diff --git a/external/cflags/LICENSE b/external/cflags/LICENSE
new file mode 100644
index 00000000..9fa24d83
--- /dev/null
+++ b/external/cflags/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Stephen Lane-Walsh
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/external/cflags/README.md b/external/cflags/README.md
new file mode 100644
index 00000000..f547d5bf
--- /dev/null
+++ b/external/cflags/README.md
@@ -0,0 +1,310 @@
+
+# cflags
+
+Command line flag parsing library in C
+
+Heavily inspired by Go's `flag` package
+https://golang.org/pkg/flag/
+
+## Building
+
+cflags is a header only library. To use it, simply copy `cflags.h` or `cflags.hpp` to your project, or add it to your include path.
+
+You may also install it using cmake, like so:
+
+```
+cmake path/to/source
+sudo make install
+```
+
+This will install both CMake and pkg-config configuration files.
+
+## Argument Parsing Logic
+
+* The first argument is stored in `program`
+* The following arguments are parsed left to right
+* If an argument does not start with `-`, it is placed in the additional arguments list stored in `args/argv`
+* If the special `--` argument appears, all following arguments are treated as positional
+ e.g. `-c 4 -- --name hello` would parse the `-c`, but place `--name` and `hello` into `args/argv`
+* Arguments starting with `--` are long name flags, e.g. `--example`
+ * The list of flags is searched for one with `long_name` equal to the argument name (after the `--`), e.g. `long_name == example`
+ * If a flag is not found with that name, an error is printed and `parse()` returns false
+* Arguments starting with just `-` are short name flags, e.g. `-xvf`
+ * These can be grouped together, so they are searched one at a time from left to right, e.g. `x`, `v`, then `f`
+ * If any of these fail to match a flag, an error is printed and `parse()` returns false
+* Once a flag is found, it attempts to find a value
+ * Arguments with long names can also come in the forms `--name`, `--name=value`, or `--name value`
+ * Arguemnts with short names can come in the forms `-n`, or `-n value`
+ * Note: Only the last short flag of a group can have a value, e.g. `-xvf file` will work, but `-xfv file` will fail
+ * If the flag is of type `[c]string`, `int`, or `float` then a value is required, and if one is not found an error is printed and `parse()` returns false
+ * Arguments of type `bool` can have a value, e.g. `--debug=false`, but one is not required
+ * Each time a flag is encountered, the `count` member is incremented
+ * The value for a flag is overwritten each time the flag is processed, the last argument parsed wins, e.g. `-c 4 -c 10` will result in `-c` being 10
+ * If you want to capture each argument separately, use `add_*_callback` instead
+
+## Usage (C)
+
+```cpp
+#include
+
+void process_string(const char * str)
+{
+ printf("processing %s\n", str);
+}
+
+void process_bool(bool b)
+{
+ printf("processing %d\n", b);
+}
+
+void process_int(int i)
+{
+ printf("processing %d\n", i);
+}
+
+void process_float(float f)
+{
+ printf("processing %f\n", f);
+}
+
+int main(int argc, char** argv)
+{
+ // Create a cflags object
+ cflags_t * flags = cflags_init();
+
+ // Add a bool flag, which will be callable with -d or --debug
+ // The value will be true if it exists, and can bet set to false
+ // by saying -d false or --debug=false
+ bool debug = false;
+ cflags_add_bool(flags, 'd', "debug", &debug, "enable debug mode");
+
+ // Add a similar help flag, which will be callable with just --help
+ bool help = false;
+ cflags_add_bool(flags, '\0', "help", &help, "print this text and exit");
+
+ // Add a string flag
+ const char * string = NULL;
+ cflags_add_string(flags, 's', "string", &string, "enter a string");
+
+ // Add an int flag
+ int count = 0;
+ cflags_add_int(flags, 'c', "count", &count, "enter a number");
+
+ // Add a float flag
+ float amount = 0.f;
+ cflags_add_float(flags, 'a', "amount", &amount, "enter a float");
+
+ // Add a string callback flag. This will call the supplied function with the value
+ // when it is parsed
+ cflags_add_string_callback(flags, 'f', "file", &process_string, "process a file");
+
+ cflags_add_bool_callback(flags, 'q', "bool-flag", &process_bool, "process a bool");
+
+ cflags_add_int_callback(flags, 'w', "int-flag", &process_int, "process a int");
+
+ cflags_add_float_callback(flags, 'e', "float-flag", &process_float, "process a float");
+
+ // Add a flag that can be called multiple times
+ cflags_flag_t * verbose = cflags_add_bool(flags, 'v', "verbose", NULL, "enables verbose output, repeat up to 4 times for more verbosity");
+
+ // Parse the command arguments
+ if (!cflags_parse(flags, argc, argv) || help || flags->argc == 0) {
+ cflags_print_usage(flags,
+ "[OPTION]... [ARG]...",
+ "Tests the cflags library.",
+ "Additional information about this library can be found by at:\n"
+ " https://github.com/WhoBrokeTheBuild/cflags");
+ }
+
+ printf("debug: %d\n", debug);
+ printf("string: %s\n", string);
+ printf("count: %d\n", count);
+ printf("amount: %f\n", amount);
+
+ // Print the number of times verbose was added
+ printf("verbosity: %d\n", verbose->count);
+
+ // Print any additional arguments, in the order they were parsed
+ for (int i = 1; i < flags->argc; ++i) {
+ printf("positional arg %d: %s\n", i, flags->argv[i]);
+ }
+
+ // Cleanup
+ cflags_free(flags);
+
+ return 0;
+}
+```
+
+## Usage (C++)
+
+```cpp
+#include
+
+void process_string(std::string str)
+{
+ printf("processing %s\n", str.c_str());
+}
+
+void process_cstring(const char * str)
+{
+ printf("processing %s\n", str);
+}
+
+void process_bool(bool b)
+{
+ printf("processing %d\n", b);
+}
+
+void process_int(int i)
+{
+ printf("processing %d\n", i);
+}
+
+void process_float(float f)
+{
+ printf("processing %f\n", f);
+}
+
+int main(int argc, char * argv[])
+{
+ // Create a cflags object
+ cflags::cflags flags;
+
+ // Add a bool flag, which will be callable with -d or --debug
+ // The value will be true if it exists, and can bet set to false
+ // by saying -d false or --debug=false
+ bool debug = false;
+ flags.add_bool('d', "debug", &debug, "enable debug mode");
+
+ // Add a similar help flag, which will be callable with just --help
+ bool help = false;
+ flags.add_bool('\0', "help", &help, "print this text and exit");
+
+ // Add a string flag
+ std::string string;
+ flags.add_string('s', "string", &string, "enter a string");
+
+ // Add a cstring flag
+ const char * cstring = NULL;
+ flags.add_cstring('\0', "cstring", &cstring, "enter a string (cstring)");
+
+ // Add an int flag
+ int count = 0;
+ flags.add_int('c', "count", &count, "enter a number");
+
+ // Add a float flag
+ float amount = 0.f;
+ flags.add_float('a', "amount", &amount, "enter a float");
+
+ // Add a string callback flag. This will call the supplied function with the value
+ // when it is parsed
+ flags.add_string_callback('f', "file", &process_string, "process a file");
+
+ flags.add_cstring_callback('\0', "cfile", &process_cstring, "process a file (cstring)");
+
+ flags.add_bool_callback('q', "bool-flag", &process_bool, "process a bool");
+
+ flags.add_int_callback('w', "int-flag", &process_int, "process a int");
+
+ flags.add_float_callback('e', "float-flag", &process_float, "process a float");
+
+ // You can also use lambdas
+ flags.add_string_callback('l', "lambda",
+ [](std::string value) {
+ printf("Hello %s\n", value.c_str());
+ },
+ "use a lambda function"
+ );
+
+ // Add a flag that can be called multiple times
+ auto verbose = flags.add_bool('v', "verbose", NULL, "enables verbose output, repeat up to 4 times for more verbosity");
+
+ // Parse the command arguments
+ if (!flags.parse(argc, argv) || help || flags.argc == 0) {
+ flags.print_usage(
+ "[OPTION]... [ARG]...",
+ "Tests the cflags library.",
+ "Additional information about this library can be found by at:\n"
+ " https://github.com/WhoBrokeTheBuild/cflags");
+ }
+
+ printf("debug: %d\n", debug);
+ printf("string: %s\n", string.c_str());
+ printf("cstring: %s\n", cstring);
+ printf("count: %d\n", count);
+ printf("amount: %f\n", amount);
+
+ // Print the number of times verbose was added
+ printf("verbosity: %d\n", verbose->count);
+
+ // Print any additional arguments, in the order they were parsed
+ for (auto& arg : flags.args) {
+ printf("positional arg %s\n", arg.data());
+ }
+
+ // For backwards compatability, the additional arguments are also exposed in argc/argv
+ for (int i = 0; i < flags.argc; ++i) {
+ printf("positional arg %d: %s\n", i, flags.argv[i]);
+ }
+
+ return 0;
+}
+```
+
+## Quirks
+
+### 1. Only the last short-name argument in a group may have a value.
+
+For example:
+```cpp
+flags.add_string('f', "file", parse_filename, "parse a filename");
+flags.add_bool('d', "debug", &debug, "enable debug mode");
+
+// These will work
+test -df test.txt
+test -df test.txt -f test2.txt
+
+// And these will fail
+test -fd test.txt
+test -ff test.txt test2.txt
+```
+
+### 2. Any `char *` strings point to the original memory from `argv`
+
+Call the following code as `program -s hello`
+```cpp
+int main(int argc, char ** argv)
+{
+ for (int i = 1; i < argc; ++i) {
+ printf("argv[%d] @ %p: %s\n", i, argv[i], argv[i]);
+ }
+
+ cflags_t * flags = cflags_init();
+
+ const char * string = NULL;
+ cflags_add_string(flags, 's', "string", &string, "enter a string");
+
+ cflags_parse(flags, argc, argv);
+
+ // The address pointed to by `string` is the same memory pointed to by argv
+ printf("string @ %p: %s\n", string, string);
+
+ cflags_free(flags);
+
+ return 0;
+}
+```
+
+Example output
+```
+argv[1] @ 0000024F6BDD665F: -s
+argv[2] @ 0000024F6BDD6662: hello
+string @ 0000024F6BDD6662: hello
+```
+
+When parsing arguments in the form `--name=value`, the memory pointed to by `argv` is altered and the `=` is replaced by a `\0`.
+
+When using the C++ version, arguments as `std::string` do not point at `argv` as their memory gets copied.
+
+
diff --git a/external/cflags/cmake/cflags.pc.in b/external/cflags/cmake/cflags.pc.in
new file mode 100644
index 00000000..e41788c7
--- /dev/null
+++ b/external/cflags/cmake/cflags.pc.in
@@ -0,0 +1,9 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+includedir=include
+
+Name: cflags
+Description: Command Line Argument Parser in C
+Version: @VERSION@
+
+Requires:
+Cflags: -I${includedir}
diff --git a/external/cflags/cmake/cflagsConfig.cmake.in b/external/cflags/cmake/cflagsConfig.cmake.in
new file mode 100644
index 00000000..6e847e29
--- /dev/null
+++ b/external/cflags/cmake/cflagsConfig.cmake.in
@@ -0,0 +1,4 @@
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/cflagsTargets.cmake")
+check_required_components("@PROJECT_NAME@")
diff --git a/external/cflags/examples/example.c b/external/cflags/examples/example.c
new file mode 100644
index 00000000..1ed3fa1a
--- /dev/null
+++ b/external/cflags/examples/example.c
@@ -0,0 +1,56 @@
+#include "cflags.h"
+#include
+
+void parse_file(const char * filename)
+{
+ printf("parsing %s\n", filename);
+}
+
+int main(int argc, char** argv)
+{
+ cflags_t * flags = cflags_init();
+
+ bool help = false;
+ cflags_add_bool(flags, '\0', "help", &help, "display this help and exit");
+
+ bool debug = false;
+ cflags_add_bool(flags, 'd', "debug", &debug, "enable debug mode");
+
+ int count = 0;
+ cflags_add_int(flags, 'c', "count", &count, "enter a number");
+
+ float amount = 0.f;
+ cflags_add_float(flags, 'a', "amount", &amount, "enter a float");
+
+ cflags_add_bool(flags, 'q', "really-long-argument-name", NULL, "testing really long argument names");
+
+ cflags_add_string_callback(flags, 'f', "file", &parse_file, "process a file");
+
+ cflags_flag_t * verbose = cflags_add_bool(flags, 'v', "verbose", NULL, "enables verbose output, repeat up to 4 times for more verbosity");
+
+ // Parse the command arguments
+ if (!cflags_parse(flags, argc, argv) || help || argc == 1) {
+ cflags_print_usage(flags,
+ "[OPTION]... [ARG]...",
+ "Tests the cflags library.",
+ "Additional information about this library can be found by at:\n"
+ " https://github.com/WhoBrokeTheBuild/cflags");
+ }
+
+ printf("help: %d\n", help);
+ printf("debug: %d\n", debug);
+
+ printf("count: %d\n", count);
+ printf("amount: %f\n", amount);
+
+ printf("verbosity: %d\n", verbose->count);
+
+ printf("argc/argv:\n");
+ for (int i = 0; i < flags->argc; ++i) {
+ printf("positional %d: %s\n", i, flags->argv[i]);
+ }
+
+ cflags_free(flags);
+
+ return 0;
+}
diff --git a/external/cflags/examples/example.cpp b/external/cflags/examples/example.cpp
new file mode 100644
index 00000000..0c17bbbf
--- /dev/null
+++ b/external/cflags/examples/example.cpp
@@ -0,0 +1,64 @@
+#include "cflags.hpp"
+
+void parse_file(const char * filename)
+{
+ printf("parsing %s\n", filename);
+}
+
+int main(int argc, char * argv[])
+{
+ cflags::cflags flags;
+
+ bool help = false;
+ flags.add_bool('\0', "help", &help, "display this help and exit");
+
+ bool debug = false;
+ flags.add_bool('d', "debug", &debug, "enable debug mode");
+
+ int count = 0;
+ flags.add_int('c', "count", &count, "enter a number");
+
+ float amount = 0.f;
+ flags.add_float('a', "amount", &amount, "enter a float");
+
+ flags.add_bool('q', "really-long-argument-name", nullptr, "testing really long argument names");
+
+ flags.add_cstring_callback('f', "file", &parse_file, "process a file");
+
+ flags.add_string_callback('n', "name",
+ [](std::string name) {
+ printf("Hello %s\n", name.c_str());
+ },
+ "say hello to name");
+
+ auto verbose = flags.add_bool('v', "verbose", NULL, "enables verbose output, repeat up to 4 times for more verbosity");
+
+ if (!flags.parse(argc, argv) || help || argc == 1) {
+ flags.print_usage(
+ "[OPTION]... [ARG]...",
+ "Tests the cflags library.",
+ "Additional information about this library can be found by contacting:\n"
+ " sdl.slane@gmail.com");
+ return 1;
+ }
+
+ printf("help: %d\n", help);
+ printf("debug: %d\n", debug);
+
+ printf("count: %d\n", count);
+ printf("amount: %f\n", amount);
+
+ printf("verbosity: %d\n", verbose->count);
+
+ printf("args:\n");
+ for (auto& arg : flags.args) {
+ printf("Positional %s\n", arg.data());
+ }
+
+ printf("argc/argv:\n");
+ for (int i = 0; i < flags.argc; ++i) {
+ printf("positional %d: %s\n", i, flags.argv[i]);
+ }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/external/cflags/include/cflags.h b/external/cflags/include/cflags.h
new file mode 100644
index 00000000..d6a29c1f
--- /dev/null
+++ b/external/cflags/include/cflags.h
@@ -0,0 +1,505 @@
+//
+// cflags version 3.0.3
+//
+// MIT License
+//
+// Copyright (c) 2022 Stephen Lane-Walsh
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+#ifndef CFLAGS_H
+#define CFLAGS_H
+
+#include
+#include
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+#define CFLAGS_ERROR_OOM "cflags: out of memory"
+
+enum cflags_type
+{
+ CFLAGS_TYPE_UNDEFINED = -1,
+ CFLAGS_TYPE_STRING,
+ CFLAGS_TYPE_BOOL,
+ CFLAGS_TYPE_INT,
+ CFLAGS_TYPE_FLOAT,
+ CFLAGS_TYPE_STRING_CALLBACK,
+ CFLAGS_TYPE_BOOL_CALLBACK,
+ CFLAGS_TYPE_INT_CALLBACK,
+ CFLAGS_TYPE_FLOAT_CALLBACK,
+};
+
+typedef enum cflags_type cflags_type_t;
+
+struct cflags_flag
+{
+ char short_name;
+ const char * long_name;
+ const char * description;
+
+ cflags_type_t type;
+ unsigned count;
+
+ struct cflags_flag * next;
+
+ union {
+ const char ** string_ptr;
+ bool * bool_ptr;
+ int * int_ptr;
+ float * float_ptr;
+ };
+
+ void (*string_callback)(const char *);
+ void (*bool_callback)(bool);
+ void (*int_callback)(int);
+ void (*float_callback)(float);
+};
+
+typedef struct cflags_flag cflags_flag_t;
+
+struct cflags
+{
+ const char * program;
+
+ int argc;
+ char ** argv;
+
+ cflags_flag_t * first_flag;
+};
+
+typedef struct cflags cflags_t;
+
+static cflags_t * cflags_init()
+{
+ cflags_t * flags = (cflags_t *)malloc(sizeof(cflags_t));
+ if (!flags) {
+ fprintf(stderr, CFLAGS_ERROR_OOM);
+ return NULL;
+ }
+
+ flags->program = NULL;
+ flags->argc = 0;
+ flags->argv = NULL;
+ flags->first_flag = NULL;
+ return flags;
+}
+
+cflags_flag_t * _cflags_add_flag(cflags_t * flags)
+{
+ cflags_flag_t ** next_flag = &flags->first_flag;
+
+ while (*next_flag) {
+ next_flag = &(*next_flag)->next;
+ }
+
+ *next_flag = (cflags_flag_t *)malloc(sizeof(cflags_flag_t));
+ if (!*next_flag) {
+ fprintf(stderr, CFLAGS_ERROR_OOM);
+ return NULL;
+ }
+
+ (*next_flag)->short_name = '\0';
+ (*next_flag)->long_name = NULL;
+ (*next_flag)->type = CFLAGS_TYPE_UNDEFINED;
+ (*next_flag)->count = 0;
+ (*next_flag)->description = NULL;
+ (*next_flag)->next = NULL;
+
+ return *next_flag;
+}
+
+static cflags_flag_t * cflags_add_string(cflags_t * flags, char short_name, const char * long_name, const char ** value, const char * description)
+{
+ cflags_flag_t * flag = _cflags_add_flag(flags);
+ if (!flag) {
+ return NULL;
+ }
+
+ flag->short_name = short_name;
+ flag->long_name = long_name;
+ flag->type = CFLAGS_TYPE_STRING;
+ flag->string_ptr = value;
+ flag->description = description;
+ return flag;
+}
+
+static cflags_flag_t * cflags_add_bool(cflags_t * flags, char short_name, const char * long_name, bool * value, const char * description)
+{
+ cflags_flag_t * flag = _cflags_add_flag(flags);
+ if (!flag) {
+ return NULL;
+ }
+
+ flag->short_name = short_name;
+ flag->long_name = long_name;
+ flag->type = CFLAGS_TYPE_BOOL;
+ flag->bool_ptr = value;
+ flag->description = description;
+ return flag;
+}
+
+static cflags_flag_t * cflags_add_int(cflags_t * flags, char short_name, const char * long_name, int * value, const char * description)
+{
+ cflags_flag_t * flag = _cflags_add_flag(flags);
+ if (!flag) {
+ return NULL;
+ }
+
+ flag->short_name = short_name;
+ flag->long_name = long_name;
+ flag->type = CFLAGS_TYPE_INT;
+ flag->int_ptr = value;
+ flag->description = description;
+ return flag;
+}
+
+static cflags_flag_t * cflags_add_float(cflags_t * flags, char short_name, const char * long_name, float * value, const char * description)
+{
+ cflags_flag_t * flag = _cflags_add_flag(flags);
+ if (!flag) {
+ return NULL;
+ }
+
+ flag->short_name = short_name;
+ flag->long_name = long_name;
+ flag->type = CFLAGS_TYPE_FLOAT;
+ flag->float_ptr = value;
+ flag->description = description;
+ return flag;
+}
+
+static cflags_flag_t * cflags_add_string_callback(cflags_t * flags, char short_name, const char * long_name, void (*func)(const char *), const char * description)
+{
+ cflags_flag_t * flag = _cflags_add_flag(flags);
+ if (!flag) {
+ return NULL;
+ }
+
+ flag->short_name = short_name;
+ flag->long_name = long_name;
+ flag->type = CFLAGS_TYPE_STRING_CALLBACK;
+ flag->string_callback = func;
+ flag->description = description;
+ return flag;
+}
+
+static cflags_flag_t * cflags_add_bool_callback(cflags_t * flags, char short_name, const char * long_name, void (*func)(bool), const char * description)
+{
+ cflags_flag_t * flag = _cflags_add_flag(flags);
+ if (!flag) {
+ return NULL;
+ }
+
+ flag->short_name = short_name;
+ flag->long_name = long_name;
+ flag->type = CFLAGS_TYPE_BOOL_CALLBACK;
+ flag->bool_callback = func;
+ flag->description = description;
+ return flag;
+}
+
+static cflags_flag_t * cflags_add_int_callback(cflags_t * flags, char short_name, const char * long_name, void (*func)(int), const char * description)
+{
+ cflags_flag_t * flag = _cflags_add_flag(flags);
+ if (!flag) {
+ return NULL;
+ }
+
+ flag->short_name = short_name;
+ flag->long_name = long_name;
+ flag->type = CFLAGS_TYPE_INT_CALLBACK;
+ flag->int_callback = func;
+ flag->description = description;
+ return flag;
+}
+
+static cflags_flag_t * cflags_add_float_callback(cflags_t * flags, char short_name, const char * long_name, void (*func)(float), const char * description)
+{
+ cflags_flag_t * flag = _cflags_add_flag(flags);
+ if (!flag) {
+ return NULL;
+ }
+
+ flag->short_name = short_name;
+ flag->long_name = long_name;
+ flag->type = CFLAGS_TYPE_FLOAT_CALLBACK;
+ flag->float_callback = func;
+ flag->description = description;
+ return flag;
+}
+
+static bool _cflags_parse_bool(const char * str)
+{
+ return !(strcmp(str, "false") == 0 ||
+ strcmp(str, "FALSE") == 0 ||
+ strcmp(str, "0") == 0);
+}
+
+static void _cflags_process_flag(cflags_flag_t * flag, const char * value)
+{
+ ++flag->count;
+
+ switch (flag->type) {
+ case CFLAGS_TYPE_STRING:
+ if (flag->string_ptr) {
+ *flag->string_ptr = value;
+ }
+ break;
+ case CFLAGS_TYPE_STRING_CALLBACK:
+ if (flag->string_callback) {
+ flag->string_callback(value);
+ }
+ break;
+ case CFLAGS_TYPE_BOOL:
+ if (flag->bool_ptr) {
+ if (value) {
+ *flag->bool_ptr = _cflags_parse_bool(value);
+ }
+ else {
+ *flag->bool_ptr = true;
+ }
+ }
+ break;
+ case CFLAGS_TYPE_BOOL_CALLBACK:
+ if (flag->bool_callback) {
+ if (value) {
+ flag->bool_callback(_cflags_parse_bool(value));
+ }
+ else {
+ flag->bool_callback(true);
+ }
+ }
+ break;
+ case CFLAGS_TYPE_INT:
+ if (flag->int_ptr) {
+ if (value) {
+ *flag->int_ptr = strtol(value, NULL, 10);
+ }
+ }
+ break;
+ case CFLAGS_TYPE_INT_CALLBACK:
+ if (flag->int_callback) {
+ if (value) {
+ flag->int_callback(strtol(value, NULL, 10));
+ }
+ }
+ break;
+ case CFLAGS_TYPE_FLOAT:
+ if (flag->float_ptr) {
+ if (value) {
+ *flag->float_ptr = strtof(value, NULL);
+ }
+ }
+ break;
+ case CFLAGS_TYPE_FLOAT_CALLBACK:
+ if (flag->float_callback) {
+ if (value) {
+ flag->float_callback(strtof(value, NULL));
+ }
+ }
+ break;
+ default: ;
+ }
+}
+
+static bool cflags_parse(cflags_t * flags, int argc, char ** argv)
+{
+ flags->argc = 1;
+ flags->argv = (char **)malloc(flags->argc * sizeof(char *));
+ if (!flags->argv) {
+ fprintf(stderr, CFLAGS_ERROR_OOM);
+ return false;
+ }
+ flags->argv[0] = argv[0];
+ flags->program = flags->argv[0];
+
+ bool passthrough = false;
+ for (int i = 1; i < argc; ++i) {
+ char * pch = argv[i];
+ if (!passthrough && *pch == '-') {
+ ++pch;
+ if (*pch == '-') {
+ ++pch;
+
+ if (*pch == '\0') {
+ // All following flags are not to be processed
+ passthrough = true;
+ continue;
+ }
+
+ // Long
+ char * key = pch;
+ char * value = NULL;
+
+ char * divider = strchr(pch, '=');
+ if (divider) {
+ *divider = '\0';
+ value = divider + 1;
+ }
+
+ bool next_arg_is_value = (i + 1 < argc && argv[i + 1][0] != '-');
+
+ bool found = false;
+ cflags_flag_t * flag = flags->first_flag;
+ while (flag) {
+ if (strcmp(flag->long_name, key) == 0) {
+ found = true;
+
+ if (value) {
+ _cflags_process_flag(flag, value);
+ }
+ else if (next_arg_is_value) {
+ _cflags_process_flag(flag, argv[i + 1]);
+ ++i;
+ }
+ else if (flag->type == CFLAGS_TYPE_BOOL || flag->type == CFLAGS_TYPE_BOOL_CALLBACK) {
+ _cflags_process_flag(flag, NULL);
+ }
+ else {
+ fprintf(stderr, "%s: option '--%s' requires an value\n", flags->program, key);
+ return false;
+ }
+
+ break;
+ }
+
+ flag = flag->next;
+ }
+
+ if (!found) {
+ fprintf(stderr, "%s: unrecognized option '--%s'\n", flags->program, key);
+ return false;
+ }
+ }
+ else {
+ // Short
+ while (*pch) {
+ bool is_last_short_flag = (*(pch + 1) == '\0');
+ bool next_arg_is_value = (i + 1 < argc && argv[i + 1][0] != '-');
+
+ bool found = false;
+ cflags_flag_t * flag = flags->first_flag;
+ while (flag) {
+ if (flag->short_name == *pch) {
+ found = true;
+
+ if (is_last_short_flag && next_arg_is_value) {
+ _cflags_process_flag(flag, argv[i + 1]);
+ ++i;
+ }
+ else if (flag->type == CFLAGS_TYPE_BOOL || flag->type == CFLAGS_TYPE_BOOL_CALLBACK) {
+ _cflags_process_flag(flag, NULL);
+ }
+ else {
+ fprintf(stderr, "%s: option '-%c' requires an value\n", flags->program, *pch);
+ return false;
+ }
+
+ break;
+ }
+
+ flag = flag->next;
+ }
+
+ if (!found) {
+ fprintf(stderr, "%s: unrecognized option '-%c'\n", flags->program, *pch);
+ return false;
+ }
+
+ ++pch;
+ }
+ }
+ }
+ else {
+ ++flags->argc;
+ char ** tmp = (char **)realloc(flags->argv, flags->argc * sizeof(char *));
+ if (!tmp) {
+ fprintf(stderr, CFLAGS_ERROR_OOM);
+ return false;
+ }
+ flags->argv = tmp;
+ flags->argv[flags->argc - 1] = pch;
+ }
+ }
+
+ return true;
+}
+
+static void cflags_free(cflags_t * flags)
+{
+ free(flags->argv);
+ flags->argv = NULL;
+
+ cflags_flag_t * tmp = NULL;
+ cflags_flag_t * flag = flags->first_flag;
+ while (flag) {
+ tmp = flag;
+ flag = flag->next;
+ free(tmp);
+ }
+
+ free(flags);
+ flags = NULL;
+}
+
+static void cflags_print_usage(cflags_t * flags, const char * usage, const char * above, const char * below)
+{
+ printf("%s %s\n", flags->program, usage);
+ printf("%s\n\n", above);
+
+ cflags_flag_t * flag = flags->first_flag;
+ while (flag) {
+ printf(" ");
+ if (flag->short_name != '\0') {
+ printf("-%c, ", flag->short_name);
+ }
+ else {
+ printf(" ");
+ }
+ size_t long_name_len = 0;
+ if (flag->long_name) {
+ printf("--%s", flag->long_name);
+ long_name_len = strlen(flag->long_name);
+ }
+ if (long_name_len > 20) {
+ printf("\n ");
+ }
+ else {
+ for (size_t i = 0; i < 20 - long_name_len; ++i) {
+ printf(" ");
+ }
+ }
+ printf("%s\n", flag->description);
+ flag = flag->next;
+ }
+
+ printf("\n%s\n", below);
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
+
+#endif // CFLAGS_H
\ No newline at end of file
diff --git a/external/cflags/include/cflags.hpp b/external/cflags/include/cflags.hpp
new file mode 100644
index 00000000..0e026e8f
--- /dev/null
+++ b/external/cflags/include/cflags.hpp
@@ -0,0 +1,484 @@
+//
+// cflags version 3.0.3
+//
+// MIT License
+//
+// Copyright (c) 2022 Stephen Lane-Walsh
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+#ifndef CFLAGS_HPP
+#define CFLAGS_HPP
+
+#include
+#include
+#include
+#include
+#include
+
+namespace cflags {
+
+using std::string;
+using std::string_view;
+using std::vector;
+using std::function;
+
+struct flag
+{
+public:
+
+ enum class type
+ {
+ Undefined = -1,
+ String,
+ CString,
+ Bool,
+ Int,
+ Float,
+ StringCallback,
+ CStringCallback,
+ BoolCallback,
+ IntCallback,
+ FloatCallback,
+ };
+
+ char short_name;
+ string long_name;
+ string description;
+
+ type type;
+ unsigned count;
+
+ union {
+ string * string_ptr;
+ const char ** cstring_ptr;
+ bool * bool_ptr;
+ int * int_ptr;
+ float * float_ptr;
+ };
+
+ function string_callback;
+ function cstring_callback;
+ function bool_callback;
+ function int_callback;
+ function float_callback;
+
+ flag()
+ : short_name('\0')
+ , type(type::Undefined)
+ , count(0)
+ , string_ptr(nullptr) // This will set all of the *_ptr members
+ { }
+
+ inline void process(const char * value)
+ {
+ auto parse_bool = [](string_view str) {
+ return !(
+ str == "false" ||
+ str == "FALSE" ||
+ str == "0"
+ );
+ };
+
+ ++count;
+
+ switch (type) {
+ case type::String:
+ if (string_ptr) {
+ *string_ptr = value;
+ }
+ break;
+ case type::StringCallback:
+ if (string_callback) {
+ if (value) {
+ string_callback(value);
+ }
+ }
+ break;
+ case type::CString:
+ if (cstring_ptr) {
+ *cstring_ptr = value;
+ }
+ break;
+ case type::CStringCallback:
+ if (cstring_callback) {
+ cstring_callback(value);
+ }
+ break;
+ case type::Bool:
+ if (bool_ptr) {
+ if (value) {
+ *bool_ptr = parse_bool(value);
+ }
+ else {
+ *bool_ptr = true;
+ }
+ }
+ break;
+ case type::BoolCallback:
+ if (bool_callback) {
+ if (value) {
+ bool_callback(parse_bool(value));
+ }
+ else {
+ bool_callback(true);
+ }
+ }
+ break;
+ case type::Int:
+ if (int_ptr) {
+ if (value) {
+ *int_ptr = strtol(value, nullptr, 10);
+ }
+ }
+ break;
+ case type::IntCallback:
+ if (int_callback) {
+ if (value) {
+ int_callback(strtol(value, nullptr, 10));
+ }
+ }
+ break;
+ case type::Float:
+ if (float_ptr) {
+ if (value) {
+ *float_ptr = strtof(value, nullptr);
+ }
+ }
+ break;
+ case type::FloatCallback:
+ if (float_callback) {
+ if (value) {
+ float_callback(strtof(value, nullptr));
+ }
+ }
+ break;
+ default: ;
+ }
+ }
+
+};
+
+class cflags
+{
+public:
+
+ string program;
+ vector args;
+
+ int argc;
+ char ** argv;
+
+ cflags()
+ : argc(0)
+ , argv(nullptr)
+ { }
+
+ flag * add_flag(flag && flag)
+ {
+ _flags.push_back(flag);
+ return &_flags.back();
+ }
+
+ flag * add_string(char short_name, string long_name, string * value_ptr, string description)
+ {
+ flag flag;
+ flag.short_name = short_name;
+ flag.long_name = long_name;
+ flag.type = flag::type::String;
+ flag.string_ptr = value_ptr;
+ flag.description = description;
+
+ _flags.push_back(flag);
+ return &_flags.back();
+ }
+
+ flag * add_cstring(char short_name, string long_name, const char ** value_ptr, string description)
+ {
+ flag flag;
+ flag.short_name = short_name;
+ flag.long_name = long_name;
+ flag.type = flag::type::CString;
+ flag.cstring_ptr = value_ptr;
+ flag.description = description;
+
+ _flags.push_back(flag);
+ return &_flags.back();
+ }
+
+ flag * add_bool(char short_name, string long_name, bool * value_ptr, string description)
+ {
+ flag flag;
+ flag.short_name = short_name;
+ flag.long_name = long_name;
+ flag.type = flag::type::Bool;
+ flag.bool_ptr = value_ptr;
+ flag.description = description;
+
+ _flags.push_back(flag);
+ return &_flags.back();
+ }
+
+ flag * add_int(char short_name, string long_name, int * value_ptr, string description)
+ {
+ flag flag;
+ flag.short_name = short_name;
+ flag.long_name = long_name;
+ flag.type = flag::type::Int;
+ flag.int_ptr = value_ptr;
+ flag.description = description;
+
+ _flags.push_back(flag);
+ return &_flags.back();
+ }
+
+ flag * add_float(char short_name, string long_name, float * value_ptr, string description)
+ {
+ flag flag;
+ flag.short_name = short_name;
+ flag.long_name = long_name;
+ flag.type = flag::type::Float;
+ flag.float_ptr = value_ptr;
+ flag.description = description;
+
+ _flags.push_back(flag);
+ return &_flags.back();
+ }
+
+ flag * add_string_callback(char short_name, string long_name, function callback, string description)
+ {
+ flag flag;
+ flag.short_name = short_name;
+ flag.long_name = long_name;
+ flag.type = flag::type::StringCallback;
+ flag.string_callback = callback;
+ flag.description = description;
+
+ _flags.push_back(flag);
+ return &_flags.back();
+ }
+
+ flag * add_cstring_callback(char short_name, string long_name, function callback, string description)
+ {
+ flag flag;
+ flag.short_name = short_name;
+ flag.long_name = long_name;
+ flag.type = flag::type::CStringCallback;
+ flag.cstring_callback = callback;
+ flag.description = description;
+
+ _flags.push_back(flag);
+ return &_flags.back();
+ }
+
+ flag * add_bool_callback(char short_name, string long_name, function callback, string description)
+ {
+ flag flag;
+ flag.short_name = short_name;
+ flag.long_name = long_name;
+ flag.type = flag::type::BoolCallback;
+ flag.bool_callback = callback;
+ flag.description = description;
+
+ _flags.push_back(flag);
+ return &_flags.back();
+ }
+
+ flag * add_int_callback(char short_name, string long_name, function callback, string description)
+ {
+ flag flag;
+ flag.short_name = short_name;
+ flag.long_name = long_name;
+ flag.type = flag::type::IntCallback;
+ flag.int_callback = callback;
+ flag.description = description;
+
+ _flags.push_back(flag);
+ return &_flags.back();
+ }
+
+ flag * add_float_callback(char short_name, string long_name, function callback, string description)
+ {
+ flag flag;
+ flag.short_name = short_name;
+ flag.long_name = long_name;
+ flag.type = flag::type::FloatCallback;
+ flag.float_callback = callback;
+ flag.description = description;
+
+ _flags.push_back(flag);
+ return &_flags.back();
+ }
+
+ ///
+ ///
+ ///
+ inline bool parse(int main_argc, char * main_argv[])
+ {
+ argc = main_argc;
+ argv = main_argv;
+
+ program = argv[0];
+
+ bool passthrough = false;
+ for (int i = 1; i < argc; ++i) {
+ char * pch = argv[i];
+ if (!passthrough && *pch == '-') {
+ ++pch;
+ if (*pch == '-') {
+ ++pch;
+
+ if (*pch == '\0') {
+ // All following flags are not to be processed
+ passthrough = true;
+ continue;
+ }
+
+ // Long
+ char * key = pch;
+ char * value = NULL;
+
+ char * divider = strchr(pch, '=');
+ if (divider) {
+ *divider = '\0';
+ value = divider + 1;
+ }
+
+ bool next_arg_is_value = (i + 1 < argc && argv[i + 1][0] != '-');
+
+ bool found = false;
+ for (auto& flag : _flags) {
+ if (flag.long_name == key) {
+ found = true;
+
+ if (value) {
+ flag.process(value);
+ }
+ else if (next_arg_is_value) {
+ flag.process(argv[i + 1]);
+ ++i;
+ }
+ else if (flag.type == flag::type::Bool || flag.type == flag::type::BoolCallback) {
+ flag.process(nullptr);
+ }
+ else {
+ fprintf(stderr, "%s: option '--%s' requires an value\n", program.c_str(), key);
+ return false;
+ }
+
+ break;
+ }
+ }
+
+ if (!found) {
+ fprintf(stderr, "%s: unrecognized option '--%s'\n", program.c_str(), key);
+ return false;
+ }
+ }
+ else {
+ // Short
+ while (*pch) {
+ bool is_last_short_flag = (*(pch + 1) == '\0');
+ bool next_arg_is_value = (i + 1 < argc && argv[i + 1][0] != '-');
+
+ bool found = false;
+ for (flag& flag : _flags) {
+ if (flag.short_name == *pch) {
+ found = true;
+
+ if (is_last_short_flag && next_arg_is_value) {
+ flag.process(argv[i + 1]);
+ ++i;
+ }
+ else if (flag.type == flag::type::Bool || flag.type == flag::type::BoolCallback) {
+ flag.process(nullptr);
+ }
+ else {
+ fprintf(stderr, "%s: option '-%c' requires an value\n", program.c_str(), *pch);
+ return false;
+ }
+
+ break;
+ }
+ }
+
+ if (!found) {
+ fprintf(stderr, "%s: unrecognized option '-%c'\n", program.c_str(), *pch);
+ return false;
+ }
+
+ ++pch;
+ }
+ }
+ }
+ else {
+ args.push_back(pch);
+ _argv.push_back(pch);
+ }
+ }
+
+ argc = static_cast(_argv.size());
+ argv = _argv.data();
+
+ return true;
+ }
+
+ void print_usage(const string& usage, const string& above, const string& below)
+ {
+ printf("Usage: %s %s\n", program.c_str(), usage.c_str());
+ printf("%s\n\n", above.c_str());
+
+ for (auto& flag : _flags) {
+ printf(" ");
+ if (flag.short_name != '\0') {
+ printf("-%c, ", flag.short_name);
+ }
+ else {
+ printf(" ");
+ }
+
+ if (!flag.long_name.empty()) {
+ printf("--%s", flag.long_name.c_str());
+ }
+ if (flag.long_name.size() > 20) {
+ printf("\n ");
+ }
+ else {
+ for (size_t i = 0; i < 20 - flag.long_name.size(); ++i) {
+ printf(" ");
+ }
+ }
+ printf("%s\n", flag.description.c_str());
+ }
+
+ printf("\n%s\n", below.c_str());
+ }
+
+private:
+
+ vector _argv;
+
+ vector _flags;
+
+};
+
+} // namespace cflags
+
+#endif // CFLAGS_HPP
\ No newline at end of file