diff --git a/include/libultraship/color.h b/include/libultraship/color.h index 654faeb17..a3796b828 100644 --- a/include/libultraship/color.h +++ b/include/libultraship/color.h @@ -7,12 +7,34 @@ extern "C" { #endif -typedef struct { +typedef struct Color_RGB8_t { uint8_t r, g, b; + +#ifdef __cplusplus + bool operator==(struct Color_RGB8_t const& other) const { + return (r == other.r) && (g == other.g) && (b == other.b); + } + + bool operator!=(struct Color_RGB8_t const& other) const { + return !(*this == other); + } +#endif + } Color_RGB8; -typedef struct { +typedef struct Color_RGBA8_t { uint8_t r, g, b, a; + +#ifdef __cplusplus + bool operator==(Color_RGBA8_t const& other) const { + return (r == other.r) && (g == other.g) && (b == other.b) && (a == other.a); + } + + bool operator!=(Color_RGBA8_t const& other) const { + return !(*this == other); + } +#endif + } Color_RGBA8; // only use when necessary for alignment purposes diff --git a/src/core/ConsoleVariable.cpp b/src/core/ConsoleVariable.cpp index 011f62848..99f40605d 100644 --- a/src/core/ConsoleVariable.cpp +++ b/src/core/ConsoleVariable.cpp @@ -11,45 +11,62 @@ namespace Ship { ConsoleVariable::ConsoleVariable() { } -std::shared_ptr ConsoleVariable::Get(const char* name) { +std::shared_ptr ConsoleVariable::Get(const std::string& name) { auto it = mVariables.find(name); return it != mVariables.end() ? it->second : nullptr; } -int32_t ConsoleVariable::GetInteger(const char* name, int32_t defaultValue) { +int32_t ConsoleVariable::GetInteger(const std::string& name, int32_t defaultValue) { auto variable = Get(name); if (variable != nullptr && variable->Type == ConsoleVariableType::Integer) { + assert(Get(name) == variable->Integer); + return variable->Integer; } + RegisterEmbedded(name, defaultValue); + assert(Get(name) == defaultValue); + return defaultValue; } -float ConsoleVariable::GetFloat(const char* name, float defaultValue) { +float ConsoleVariable::GetFloat(const std::string& name, float defaultValue) { auto variable = Get(name); if (variable != nullptr && variable->Type == ConsoleVariableType::Float) { + assert(Get(name) == variable->Float); + return variable->Float; } + RegisterEmbedded(name, defaultValue); + assert(Get(name) == defaultValue); + return defaultValue; } -const char* ConsoleVariable::GetString(const char* name, const char* defaultValue) { +const char* ConsoleVariable::GetString(const std::string& name, const char* defaultValue) { auto variable = Get(name); if (variable != nullptr && variable->Type == ConsoleVariableType::String) { + assert(Get(name) == variable->String); + return variable->String.c_str(); } + RegisterEmbedded(name, std::string(defaultValue)); + assert(Get(name) == defaultValue); + return defaultValue; } -Color_RGBA8 ConsoleVariable::GetColor(const char* name, Color_RGBA8 defaultValue) { +Color_RGBA8 ConsoleVariable::GetColor(const std::string& name, Color_RGBA8 defaultValue) { auto variable = Get(name); if (variable != nullptr && variable->Type == ConsoleVariableType::Color) { + assert(Get(name) == variable->Color); + return variable->Color; } else if (variable != nullptr && variable->Type == ConsoleVariableType::Color24) { Color_RGBA8 temp; @@ -60,13 +77,18 @@ Color_RGBA8 ConsoleVariable::GetColor(const char* name, Color_RGBA8 defaultValue return temp; } + RegisterEmbedded(name, defaultValue); + assert(Get(name) == defaultValue); + return defaultValue; } -Color_RGB8 ConsoleVariable::GetColor24(const char* name, Color_RGB8 defaultValue) { +Color_RGB8 ConsoleVariable::GetColor24(const std::string& name, Color_RGB8 defaultValue) { auto variable = Get(name); if (variable != nullptr && variable->Type == ConsoleVariableType::Color24) { + assert(Get(name) == variable->Color24); + return variable->Color24; } else if (variable != nullptr && variable->Type == ConsoleVariableType::Color) { Color_RGB8 temp; @@ -76,93 +98,118 @@ Color_RGB8 ConsoleVariable::GetColor24(const char* name, Color_RGB8 defaultValue return temp; } + RegisterEmbedded(name, defaultValue); + assert(Get(name) == defaultValue); + return defaultValue; } -void ConsoleVariable::SetInteger(const char* name, int32_t value) { +void ConsoleVariable::SetInteger(const std::string& name, int32_t value) { auto& variable = mVariables[name]; if (variable == nullptr) { variable = std::make_shared(); + + RegisterEmbedded(name, value); } variable->Type = ConsoleVariableType::Integer; variable->Integer = value; + + Set(name, value); } -void ConsoleVariable::SetFloat(const char* name, float value) { +void ConsoleVariable::SetFloat(const std::string& name, float value) { auto& variable = mVariables[name]; if (variable == nullptr) { variable = std::make_shared(); + + RegisterEmbedded(name, value); } variable->Type = ConsoleVariableType::Float; variable->Float = value; + + Set(name, value); } -void ConsoleVariable::SetString(const char* name, const char* value) { +void ConsoleVariable::SetString(const std::string& name, const char* value) { auto& variable = mVariables[name]; if (variable == nullptr) { variable = std::make_shared(); + + RegisterEmbedded(name, std::string(value)); } variable->Type = ConsoleVariableType::String; variable->String = std::string(value); + + Set(name, value); } -void ConsoleVariable::SetColor(const char* name, Color_RGBA8 value) { +void ConsoleVariable::SetColor(const std::string& name, Color_RGBA8 value) { auto& variable = mVariables[name]; if (!variable) { variable = std::make_shared(); + + RegisterEmbedded(name, value); } variable->Type = ConsoleVariableType::Color; variable->Color = value; + + Set(name, value); } -void ConsoleVariable::SetColor24(const char* name, Color_RGB8 value) { +void ConsoleVariable::SetColor24(const std::string& name, Color_RGB8 value) { auto& variable = mVariables[name]; if (!variable) { variable = std::make_shared(); + + RegisterEmbedded(name, value); } variable->Type = ConsoleVariableType::Color24; variable->Color24 = value; + + Set(name, value); } -void ConsoleVariable::RegisterInteger(const char* name, int32_t defaultValue) { +void ConsoleVariable::RegisterInteger(const std::string& name, int32_t defaultValue) { if (Get(name) == nullptr) { SetInteger(name, defaultValue); } } -void ConsoleVariable::RegisterFloat(const char* name, float defaultValue) { +void ConsoleVariable::RegisterFloat(const std::string& name, float defaultValue) { if (Get(name) == nullptr) { SetFloat(name, defaultValue); } } -void ConsoleVariable::RegisterString(const char* name, const char* defaultValue) { +void ConsoleVariable::RegisterString(const std::string& name, const char* defaultValue) { if (Get(name) == nullptr) { SetString(name, defaultValue); } } -void ConsoleVariable::RegisterColor(const char* name, Color_RGBA8 defaultValue) { +void ConsoleVariable::RegisterColor(const std::string& name, Color_RGBA8 defaultValue) { if (Get(name) == nullptr) { SetColor(name, defaultValue); } } -void ConsoleVariable::RegisterColor24(const char* name, Color_RGB8 defaultValue) { +void ConsoleVariable::RegisterColor24(const std::string& name, Color_RGB8 defaultValue) { if (Get(name) == nullptr) { SetColor24(name, defaultValue); } } -void ConsoleVariable::ClearVariable(const char* name) { +void ConsoleVariable::ClearVariable(const std::string& name) { std::shared_ptr conf = Ship::Window::GetInstance()->GetConfig(); mVariables.erase(name); - conf->erase(StringHelper::Sprintf("CVars.%s", name)); + conf->erase(StringHelper::Sprintf("CVars.%s", name.c_str())); + mVariables_New.erase(name); + conf->erase(StringHelper::Sprintf("CVars2.%s", name.c_str())); } void ConsoleVariable::Save() { @@ -194,6 +241,35 @@ void ConsoleVariable::Save() { } } + for (const auto& variable : mVariables_New) { + if (variable.second->ShouldSave()) { + const std::string key = StringHelper::Sprintf("CVars2.%s", variable.first.c_str()); + CVarInterface::VauleType cvarValue = variable.second->Get(); + if (int32_t* data = std::get_if(&cvarValue)) { + conf->setInt(key, *data); + } else if (float* data = std::get_if(&cvarValue)) { + conf->setFloat(key, *data); + } else if (std::string* data = std::get_if(&cvarValue)) { + if (data->length() > 0) { + conf->setString(key, *data); + } + } else if (Color_RGBA8* data = std::get_if(&cvarValue)) { + const char* keyStr = key.c_str(); + conf->setUInt(StringHelper::Sprintf("%s.R", keyStr), data->r); + conf->setUInt(StringHelper::Sprintf("%s.G", keyStr), data->g); + conf->setUInt(StringHelper::Sprintf("%s.B", keyStr), data->b); + conf->setUInt(StringHelper::Sprintf("%s.A", keyStr), data->a); + conf->setString(StringHelper::Sprintf("%s.Type", keyStr), mercuryRGBAObjectType); + } else if (Color_RGB8* data = std::get_if(&cvarValue)) { + const char* keyStr = key.c_str(); + conf->setUInt(StringHelper::Sprintf("%s.R", keyStr), data->r); + conf->setUInt(StringHelper::Sprintf("%s.G", keyStr), data->g); + conf->setUInt(StringHelper::Sprintf("%s.B", keyStr), data->b); + conf->setString(StringHelper::Sprintf("%s.Type", keyStr), mercuryRGBObjectType); + } + } + } + conf->save(); } @@ -202,6 +278,7 @@ void ConsoleVariable::Load() { conf->reload(); LoadFromPath("", conf->rjson["CVars"].items()); + LoadFromPath("", conf->rjson["CVars2"].items()); LoadLegacy(); } @@ -225,35 +302,36 @@ void ConsoleVariable::LoadFromPath( clr.g = value["G"].get(); clr.b = value["B"].get(); clr.a = value["A"].get(); - SetColor(itemPath.c_str(), clr); + SetColor(itemPath, clr); } else if (value.contains("Type") && value["Type"].get() == mercuryRGBObjectType) { Color_RGB8 clr; clr.r = value["R"].get(); clr.g = value["G"].get(); clr.b = value["B"].get(); - SetColor24(itemPath.c_str(), clr); + SetColor24(itemPath, clr); } else { LoadFromPath(itemPath, value.items()); } break; case nlohmann::detail::value_t::string: - SetString(itemPath.c_str(), value.get().c_str()); + SetString(itemPath, value.get().c_str()); break; case nlohmann::detail::value_t::boolean: - SetInteger(itemPath.c_str(), value.get()); + SetInteger(itemPath, value.get()); break; case nlohmann::detail::value_t::number_unsigned: case nlohmann::detail::value_t::number_integer: - SetInteger(itemPath.c_str(), value.get()); + SetInteger(itemPath, value.get()); break; case nlohmann::detail::value_t::number_float: - SetFloat(itemPath.c_str(), value.get()); + SetFloat(itemPath, value.get()); break; default:; } } } + void ConsoleVariable::LoadLegacy() { auto conf = Ship::Window::GetPathRelativeToAppDirectory("cvars.cfg"); if (File::Exists(conf)) { @@ -302,4 +380,5 @@ void ConsoleVariable::LoadLegacy() { fs::remove(conf); } } + } // namespace Ship diff --git a/src/core/ConsoleVariable.h b/src/core/ConsoleVariable.h index 3ee4d4d79..c69496ccb 100644 --- a/src/core/ConsoleVariable.h +++ b/src/core/ConsoleVariable.h @@ -4,8 +4,10 @@ #include #include #include -#include #include +#include +#include +#include namespace Ship { typedef enum class ConsoleVariableType { Integer, Float, String, Color, Color24 } ConsoleVariableType; @@ -13,6 +15,62 @@ typedef union CVarValue { } CVarValue; +class CVarInterface { + public: + using VauleType = std::variant; + + CVarInterface() = default; + virtual ~CVarInterface() = default; + + virtual VauleType Get() const = 0; + + virtual void Set(const VauleType&) = 0; + + virtual bool ShouldSave() = 0; +}; + +template +class CVarDefaulted : public CVarInterface { + + using DefualtType = std::remove_reference::type; + + public: + CVarDefaulted(StorageType& initialValue, const DefualtType& defaultValue) : + mValue(initialValue), + mDefault(defaultValue) + { + }; + + virtual ~CVarDefaulted() = default; + + VauleType Get() const override { + return VauleType{ mValue }; + } + + void Set(const VauleType& newValue) override { + // TODO assert type + if (const DefualtType* value = std::get_if(&newValue)) { + mValue = *value; + } + } + + bool ShouldSave() override { + return mValue != mDefault; + } + + private: + StorageType mValue; + const DefualtType mDefault; +}; + +// Represents a CVar with embedded storage i.e. the lifetime of the value matches the lifetime of the CVar +template +using CVarEmbedded = CVarDefaulted; + +// Represents a CVar with embedded storage i.e. the lifetime of the value is independent of that of the CVar's +template +using CVarManaged = CVarDefaulted; + typedef struct CVar { const char* Name; ConsoleVariableType Type; @@ -27,37 +85,86 @@ class ConsoleVariable { public: ConsoleVariable(); - std::shared_ptr Get(const char* name); + std::shared_ptr Get(const std::string& name); + + int32_t GetInteger(const std::string& name, int32_t defaultValue); + float GetFloat(const std::string& name, float defaultValue); + const char* GetString(const std::string& name, const char* defaultValue); + Color_RGBA8 GetColor(const std::string& name, Color_RGBA8 defaultValue); + Color_RGB8 GetColor24(const std::string& name, Color_RGB8 defaultValue); + + void SetInteger(const std::string& name, int32_t value); + void SetFloat(const std::string& name, float value); + void SetString(const std::string& name, const char* value); + void SetColor(const std::string& name, Color_RGBA8 value); + void SetColor24(const std::string& name, Color_RGB8 value); + + void RegisterInteger(const std::string& name, int32_t defaultValue); + void RegisterFloat(const std::string& name, float defaultValue); + void RegisterString(const std::string& name, const char* defaultValue); + void RegisterColor(const std::string& name, Color_RGBA8 defaultValue); + void RegisterColor24(const std::string& name, Color_RGB8 defaultValue); + + void ClearVariable(const std::string& name); - int32_t GetInteger(const char* name, int32_t defaultValue); - float GetFloat(const char* name, float defaultValue); - const char* GetString(const char* name, const char* defaultValue); - Color_RGBA8 GetColor(const char* name, Color_RGBA8 defaultValue); - Color_RGB8 GetColor24(const char* name, Color_RGB8 defaultValue); + template + T Get(const std::string& name) { + auto cvarIter = mVariables_New.find(name); + if (cvarIter == mVariables_New.end()) { + // TODO assert? CVar was not registered/created + return T{}; + } - void SetInteger(const char* name, int32_t value); - void SetFloat(const char* name, float value); - void SetString(const char* name, const char* value); - void SetColor(const char* name, Color_RGBA8 value); - void SetColor24(const char* name, Color_RGB8 value); + auto cvarValue = cvarIter->second->Get(); + if (const T* ret = std::get_if(&cvarValue)) { + return *ret; + } - void RegisterInteger(const char* name, int32_t defaultValue); - void RegisterFloat(const char* name, float defaultValue); - void RegisterString(const char* name, const char* defaultValue); - void RegisterColor(const char* name, Color_RGBA8 defaultValue); - void RegisterColor24(const char* name, Color_RGB8 defaultValue); + // TODO assert? Incorrect type is probably a mistake - void ClearVariable(const char* name); + return T{}; + } + + template + void Set(const std::string& name, const T& value) { + auto cvarIter = mVariables_New.find(name); + if (cvarIter == mVariables_New.end()) { + // TODO assert? CVar was not registered/created + return; + } + + cvarIter->second->Set(value); + } + + // Creates a CVar that manages the value + template + void RegisterEmbedded(const std::string& name, T value) { + if (mVariables_New.find(name) != mVariables_New.end()) { + return; + } + + mVariables_New[name] = std::make_unique>(value, value); + } + + // Creates a CVar that tracks the value + template + void RegisterManaged(const std::string& name, T& value) { + if (mVariables_New.find(name) != mVariables_New.end()) { + return; + } + + mVariables_New[name] = std::make_unique>(value, value); + } void Save(); void Load(); - protected: + private: void LoadFromPath(std::string path, nlohmann::detail::iteration_proxy> items); void LoadLegacy(); - private: - std::map, std::less<>> mVariables; + std::unordered_map> mVariables; + std::unordered_map> mVariables_New; }; } // namespace Ship diff --git a/src/core/Window.cpp b/src/core/Window.cpp index 807850a99..82292ac6a 100644 --- a/src/core/Window.cpp +++ b/src/core/Window.cpp @@ -99,6 +99,8 @@ void Window::Initialize(const std::vector& otrFiles, const std::uno InitializeControlDeck(); InitializeCrashHandler(); + CVarLoad(); + bool steamDeckGameMode = false; #ifdef __linux__ @@ -364,6 +366,8 @@ void Window::InitializeControlDeck() { void Window::InitializeConsoleVariables() { mConsoleVariables = std::make_shared(); + + Ship::ExecuteHooks(); } void Window::InitializeCrashHandler() { diff --git a/src/menu/ImGuiImpl.cpp b/src/menu/ImGuiImpl.cpp index 1586b8380..ddcee3c85 100644 --- a/src/menu/ImGuiImpl.cpp +++ b/src/menu/ImGuiImpl.cpp @@ -377,7 +377,6 @@ void LoadTexture(const std::string& name, const std::string& path) { // MARK: - Public API void Init(WindowImpl window_impl) { - CVarLoad(); impl = window_impl; ImGuiContext* ctx = ImGui::CreateContext(); ImGui::SetCurrentContext(ctx); diff --git a/src/misc/Hooks.h b/src/misc/Hooks.h index 5c6733fd1..64c14c1b0 100644 --- a/src/misc/Hooks.h +++ b/src/misc/Hooks.h @@ -30,4 +30,5 @@ DEFINE_HOOK(GfxInit, void()); DEFINE_HOOK(ExitGame, void()); DEFINE_HOOK(LoadFile, void(uint32_t fileNum)); DEFINE_HOOK(DeleteFile, void(uint32_t fileNum)); +DEFINE_HOOK(CVarInit, void()); } // namespace Ship