diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a49f9a2e62..0cf10cbb3a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,6 +15,7 @@ set(SOURCE_FILES util/Meta.hxx util/Exception.{hxx,cxx} util/OSystem.{hxx,cxx} + util/AppOptions.{hxx,cxx} services/Randomizer.{hxx,cxx} services/GameClock.{hxx,cxx} services/GameClock.inl.hxx diff --git a/src/main.cxx b/src/main.cxx index 4642939d54..74785ec6ee 100644 --- a/src/main.cxx +++ b/src/main.cxx @@ -4,6 +4,7 @@ #include "MainMenu.hxx" #include "Exception.hxx" #include "LOG.hxx" +#include "AppOptions.hxx" #include "engine/WindowManager.hxx" #include @@ -51,32 +52,15 @@ int protected_main(int argc, char **argv) LOG(LOG_INFO) << VERSION; - // add commandline parameter to skipMenu - auto has_args = [argv, argc](const std::string ¶m) - { - for (int i = 1; i < argc; ++i) - if (param == argv[i]) - return i; - - LOG(LOG_DEBUG) << "Unknown game option " << param; - return 0; - }; - - bool skipMenu = has_args("--skipMenu"); - uint32_t videoOpt = has_args("--video"); - const char *videoDriver = nullptr; - if (videoOpt) - { - videoDriver = argv[videoOpt + 1]; - } + bool skipMenu = AppOptions::parseOption("skipMenu", ""); + std::string videoDriver = AppOptions::parseOption("video", "", ""); LOG(LOG_DEBUG) << "Launching Cytopia"; - Cytopia::Game game; LOG(LOG_DEBUG) << "Initializing Cytopia"; - if (!initialize(videoDriver)) + if (!initialize(videoDriver.c_str())) return EXIT_FAILURE; else LOG(LOG_DEBUG) << "DONE Cytopia"; @@ -101,6 +85,11 @@ int protected_main(int argc, char **argv) int main(int argc, char **argv) { + AppOptions::setArgvOptions(argc, argv); + std::string errorInfo; + if (!AppOptions::parseOptions(errorInfo)) + LOG(LOG_DEBUG) << "Options error: " << errorInfo; + systemSetupCrashHandler(); try diff --git a/src/util/AppOptions.cxx b/src/util/AppOptions.cxx new file mode 100644 index 0000000000..e745b5fda9 --- /dev/null +++ b/src/util/AppOptions.cxx @@ -0,0 +1,120 @@ +#include "AppOptions.hxx" + +#include + +// build a vector of argv options +// the program path argv[0] is excluded +static std::vector argvOptions; + +void AppOptions::setArgvOptions(int argc, char **argv) +{ + argvOptions.clear(); + for (int i = 1; i < argc; i++) + argvOptions.emplace_back(std::string(argv[i])); +} + +bool AppOptions::parseOptions(const std::string &errorInfo) +{ + std::string arg, subArg, subArg2; + + for (auto &arg : argvOptions) + { + if (arg.compare(0, 2, "--") == 0) + { + parseOption(arg.substr(2), errorInfo); + } + else if (arg[0] == '-') + { + size_t i; + for (i = 1; i < arg.length(); ++i) + { + if (i > 1 && isalpha((unsigned char)arg[i]) && arg[i - 1] != 'x') + { + // parse the previous option in subArg + parseOption(subArg, errorInfo); + subArg = ""; + } + + if (arg[i] == '-') + { + parseOption(subArg2, errorInfo); + subArg2 = ""; + i++; + } + + // append the current option to subArg + subArg.append(1, arg[i]); + subArg2.append(1, arg[i]); + } + + subArg = ""; + } + else + { + parseOption(arg, errorInfo); + subArg = ""; + } + } + + if (errorInfo.length() > 0) + return false; + + return true; +} + +bool AppOptions::isOption(const std::string &arg, const char *op) +{ + return arg.compare(op) == 0; +} + +bool AppOptions::isOption(const std::string &arg, const char *a, const char *b) +{ + return (isOption(arg, a) || isOption(arg, b)); +} + +bool AppOptions::isParamOption(const std::string &arg, const char *option) +{ + bool retVal = arg.compare(0, strlen(option), option) == 0; + // if comparing for short option, 2nd char of arg must be numeric + if (retVal && strlen(option) == 1 && arg.length() > 1) + if (!isdigit((unsigned char)arg[1])) + retVal = false; + return retVal; +} + +bool AppOptions::isParamOption(const std::string &arg, const char *option1, const char *option2) +{ + return isParamOption(arg, option1) || isParamOption(arg, option2); +} + +std::string AppOptions::getParam(const std::string &arg, const char *op) +{ + std::string param = arg.substr(strlen(op)); + if (param.size() > 1 && param.front() == '=') + param.erase(0, 1); + return param; +} + +std::string AppOptions::getParam(const std::string &arg, const char *op1, const char *op2) +{ + return isParamOption(arg, op1) ? getParam(arg, op1) : getParam(arg, op2); +} + +std::vector &AppOptions::getArgvOptions() +{ + return argvOptions; +} + +void AppOptions::parseOption(const std::string &arg, const std::string &errorInfo) +{ + // sample how to check options + if (isParamOption(arg, "s", "map-size=")) + { + std::string mapSizeStr = getParam(arg, "s", "map-size="); + int mapSize = 0; + if (mapSizeStr.length() > 0) + mapSize = atoi(mapSizeStr.c_str()); + + return; + } +} \ No newline at end of file diff --git a/src/util/AppOptions.hxx b/src/util/AppOptions.hxx new file mode 100644 index 0000000000..85c194fcb8 --- /dev/null +++ b/src/util/AppOptions.hxx @@ -0,0 +1,76 @@ +#ifndef APPOPTIONS_HXX_ +#define APPOPTIONS_HXX_ + +#include +#include + +struct AppOptions { + template + static bool parseOption(const char *a, const char *b, const Func &func) + { + bool hasb = (b && *b); + for (auto &arg : getArgvOptions()) + { + if (arg.compare(0, 2, "--") == 0) + { + std::string rarg = arg.substr(2); + if (hasb && isParamOption(rarg, a, b)) { + std::string param = getParam(rarg, a, b); + return func(param); + } + else if (isParamOption(rarg, a)) + { + std::string param = getParam(rarg, a); + return func(param); + } + } + else if (arg.front() == '-') + { + std::string rarg = arg.substr(1); + if (hasb && isParamOption(rarg, a, b)) { + std::string param = getParam(rarg, a, b); + return func(param); + } + else if (isParamOption(rarg, a)) + { + std::string param = getParam(rarg, a); + return func(param); + } + } + else + { + if (hasb && isOption(arg, a, b)) { + return func(""); + } + else if (isOption(arg, a)) { + return func(""); + } + } + } + return false; + } + + static bool parseOption(const char *a, const char *b) { return parseOption(a, b, [] (auto &) { return true; }); } + static std::string parseOption(const char *a, const char *b, const char *def) { + std::string param; + bool found = parseOption(a, b, [¶m] (auto &p) { param = p; return true; }); + return found ? param : def; + } + + static void setArgvOptions(int argc, char **argv); + static bool parseOptions(const std::string &errorInfo); + +private: + static void parseOption(const std::string &arg, const std::string &errorInfo); + static bool isOption(const std::string &arg, const char *op); + static bool isOption(const std::string &arg, const char *a, const char *b); + static bool isParamOption(const std::string &arg, const char *option); + static bool isParamOption(const std::string &arg, const char *option1, const char *option2); + static std::string getParam(const std::string &arg, const char *op); + static std::string getParam(const std::string &arg, const char *op1, const char *op2); + static std::vector &getArgvOptions(); + + AppOptions(const AppOptions &) = delete; + AppOptions &operator=(AppOptions &) = delete; +}; +#endif // APPOPTIONS_HXX_