diff --git a/src/xenia/base/cvar.cc b/src/xenia/base/cvar.cc new file mode 100644 index 000000000..d7c82c970 --- /dev/null +++ b/src/xenia/base/cvar.cc @@ -0,0 +1,56 @@ +#include "cvar.h" + +namespace cvar { +cxxopts::Options options("xenia", "Xbox 360 Emulator"); +std::map* CmdVars; +std::map* ConfigVars; + +void PrintHelpAndExit() { + std::cout << options.help({""}) << std::endl; + std::cout << "For the full list of command line arguments, see xenia.cfg." + << std::endl; + exit(0); +} + +void ParseLaunchArguments(int argc, char** argv) { + options.add_options()("help", "Prints help and exit."); + if (!CmdVars) CmdVars = new std::map(); + if (!ConfigVars) ConfigVars = new std::map(); + for (auto& it : *CmdVars) { + auto cmdVar = it.second; + cmdVar->AddToLaunchOptions(&options); + } + std::vector vars; + for (const auto& s : *ConfigVars) vars.push_back(s.second); + + for (auto& it : *ConfigVars) { + auto configVar = it.second; + configVar->AddToLaunchOptions(&options); + } + try { + options.positional_help("[Path to .iso/.xex]"); + options.parse_positional({"target"}); + + auto result = options.parse(argc, argv); + if (result.count("help")) { + PrintHelpAndExit(); + } + for (auto& it : *CmdVars) { + auto cmdVar = static_cast(it.second); + if (result.count(cmdVar->GetName())) { + cmdVar->LoadFromLaunchOptions(&result); + } + } + for (auto& it : *ConfigVars) { + auto configVar = static_cast(it.second); + if (result.count(configVar->GetName())) { + configVar->LoadFromLaunchOptions(&result); + } + } + } catch (const cxxopts::OptionException& e) { + std::cout << e.what() << std::endl; + PrintHelpAndExit(); + } +} + +} // namespace cvar diff --git a/src/xenia/base/cvar.h b/src/xenia/base/cvar.h new file mode 100644 index 000000000..4661671d9 --- /dev/null +++ b/src/xenia/base/cvar.h @@ -0,0 +1,258 @@ +#ifndef XENIA_CVAR_H_ +#define XENIA_CVAR_H_ +#include +#include +#include "cpptoml/include/cpptoml.h" +#include "cxxopts/include/cxxopts.hpp" +#include "xenia/base/string_util.h" +namespace cvar { + +class ICommandVar { + public: + virtual ~ICommandVar() = default; + virtual std::string GetName() = 0; + virtual std::string GetDescription() = 0; + virtual void UpdateValue() = 0; + virtual void AddToLaunchOptions(cxxopts::Options* options) = 0; + virtual void LoadFromLaunchOptions(cxxopts::ParseResult* result) = 0; +}; + +class IConfigVar : virtual public ICommandVar { + public: + virtual std::string GetCategory() = 0; + virtual std::string GetConfigValue() = 0; + virtual void LoadConfigValue(std::shared_ptr result) = 0; + virtual void LoadGameConfigValue(std::shared_ptr result) = 0; +}; + +template +class CommandVar : virtual public ICommandVar { + public: + CommandVar(const char* name, T* defaultValue, const char* description); + std::string GetName() override; + std::string GetDescription() override; + void AddToLaunchOptions(cxxopts::Options* options) override; + void LoadFromLaunchOptions(cxxopts::ParseResult* result) override; + T* GetCurrentValue() { return currentValue_; } + + protected: + std::string name_; + T defaultValue_; + T* currentValue_; + std::unique_ptr commandLineValue_; + std::string description_; + T Convert(std::string val); + static std::string ToString(T val); + void SetValue(T val); + void SetCommandLineValue(T val); + void UpdateValue() override; +}; +#pragma warning(push) +#pragma warning(disable : 4250) +template +class ConfigVar : public CommandVar, virtual public IConfigVar { + public: + ConfigVar(const char* name, T* defaultValue, const char* description, + const char* category); + std::string GetConfigValue() override; + std::string GetCategory() override; + void AddToLaunchOptions(cxxopts::Options* options) override; + void LoadConfigValue(std::shared_ptr result) override; + void LoadGameConfigValue(std::shared_ptr result) override; + void SetConfigValue(T val); + void SetGameConfigValue(T val); + + private: + std::string category_; + std::unique_ptr configValue_ = nullptr; + std::unique_ptr gameConfigValue_ = nullptr; + void UpdateValue() override; +}; + +#pragma warning(pop) +template +std::string CommandVar::GetName() { + return name_; +} +template +std::string CommandVar::GetDescription() { + return description_; +} +template +void CommandVar::AddToLaunchOptions(cxxopts::Options* options) { + options->add_options()(this->name_, this->description_, cxxopts::value()); +} +template +void ConfigVar::AddToLaunchOptions(cxxopts::Options* options) { + options->add_options(category_)(this->name_, this->description_, + cxxopts::value()); +} +template +void CommandVar::LoadFromLaunchOptions(cxxopts::ParseResult* result) { + T value = (*result)[this->name_].template as(); + SetCommandLineValue(value); +} +template +void ConfigVar::LoadConfigValue(std::shared_ptr result) { + SetConfigValue(*cpptoml::get_impl(result)); +} +template +void ConfigVar::LoadGameConfigValue(std::shared_ptr result) { + SetGameConfigValue(*cpptoml::get_impl(result)); +} +template +CommandVar::CommandVar(const char* name, T* defaultValue, + const char* description) + : name_(name), + defaultValue_(*defaultValue), + description_(description), + currentValue_(defaultValue) {} + +template +ConfigVar::ConfigVar(const char* name, T* defaultValue, + const char* description, const char* category) + : CommandVar(name, defaultValue, description), category_(category) {} + +template +void CommandVar::UpdateValue() { + if (this->commandLineValue_) return this->SetValue(*this->commandLineValue_); + return this->SetValue(defaultValue_); +} +template +void ConfigVar::UpdateValue() { + if (this->commandLineValue_) return this->SetValue(*this->commandLineValue_); + if (this->gameConfigValue_) return this->SetValue(*this->gameConfigValue_); + if (this->configValue_) return this->SetValue(*this->configValue_); + return this->SetValue(this->defaultValue_); +} +template +T CommandVar::Convert(std::string val) { + return xe::string_util::from_string(val); +} +template <> +inline std::string CommandVar::Convert(std::string val) { + return val; +} + +template <> +inline std::string CommandVar::ToString(bool val) { + return val ? "true" : "false"; +} +template <> +inline std::string CommandVar::ToString(std::string val) { + return "\"" + val + "\""; +} + +template +std::string CommandVar::ToString(T val) { + return std::to_string(val); +} + +template +void CommandVar::SetValue(T val) { + *currentValue_ = val; +} +template +std::string ConfigVar::GetCategory() { + return category_; +} +template +std::string ConfigVar::GetConfigValue() { + if (this->configValue_) return this->ToString(*this->configValue_); + return this->ToString(this->defaultValue_); +} +template +void CommandVar::SetCommandLineValue(const T val) { + this->commandLineValue_ = std::make_unique(val); + this->UpdateValue(); +} +template +void ConfigVar::SetConfigValue(T val) { + this->configValue_ = std::make_unique(val); + this->UpdateValue(); +} +template +void ConfigVar::SetGameConfigValue(T val) { + this->gameConfigValue_ = std::make_unique(val); + this->UpdateValue(); +} + +extern std::map* CmdVars; +extern std::map* ConfigVars; + +inline void AddConfigVar(IConfigVar* cv) { + if (!ConfigVars) ConfigVars = new std::map(); + ConfigVars->insert(std::pair(cv->GetName(), cv)); +} +inline void AddCommandVar(ICommandVar* cv) { + if (!CmdVars) CmdVars = new std::map(); + CmdVars->insert(std::pair(cv->GetName(), cv)); +} +void ParseLaunchArguments(int argc, char** argv); + +template +T* define_configvar(const char* name, T* defaultValue, const char* description, + const char* category) { + IConfigVar* cfgVar = + new ConfigVar(name, defaultValue, description, category); + AddConfigVar(cfgVar); + return defaultValue; +} + +template +T* define_cmdvar(const char* name, T* defaultValue, const char* description) { + ICommandVar* cmdVar = new CommandVar(name, defaultValue, description); + AddCommandVar(cmdVar); + return defaultValue; +} +#define DEFINE_double(name, defaultValue, description, category) \ + DEFINE_CVar(name, defaultValue, description, category, double) + +#define DEFINE_int32(name, defaultValue, description, category) \ + DEFINE_CVar(name, defaultValue, description, category, int32_t) + +#define DEFINE_uint64(name, defaultValue, description, category) \ + DEFINE_CVar(name, defaultValue, description, category, uint64_t) + +#define DEFINE_string(name, defaultValue, description, category) \ + DEFINE_CVar(name, defaultValue, description, category, std::string) + +#define DEFINE_bool(name, defaultValue, description, category) \ + DEFINE_CVar(name, defaultValue, description, category, bool) + +#define DEFINE_CVar(name, defaultValue, description, category, type) \ + namespace cvars { \ + type name = defaultValue; \ + } \ + namespace cv { \ + static auto cv_##name = \ + cvar::define_configvar(#name, &cvars::name, description, category); \ + } + +// CmdVars can only be strings for now, we don't need any others +#define CmdVar(name, defaultValue, description) \ + namespace cvars { \ + std::string name = defaultValue; \ + } \ + namespace cv { \ + static auto cv_##name = \ + cvar::define_cmdvar(#name, &cvars::name, description); \ + } + +#define DECLARE_double(name) DECLARE_CVar(name, double) + +#define DECLARE_bool(name) DECLARE_CVar(name, bool) + +#define DECLARE_string(name) DECLARE_CVar(name, std::string) + +#define DECLARE_int32(name) DECLARE_CVar(name, int32_t) + +#define DECLARE_uint64(name) DECLARE_CVar(name, uint64_t) + +#define DECLARE_CVar(name, type) \ + namespace cvars { \ + extern type name; \ + } + +} // namespace cvar +#endif // XENIA_CVAR_H_