diff --git a/src/ImageSelectState.hpp b/src/ImageSelectState.hpp index ba402bc..7e5f8b8 100644 --- a/src/ImageSelectState.hpp +++ b/src/ImageSelectState.hpp @@ -36,9 +36,9 @@ public: ImageSelectState(); ImageSelectState(const ImageSelectState& orig) = delete; virtual ~ImageSelectState(); - + bool IsActive() override; - void ProcessInput(const SDL_Event& event, bool &processed) override; + void ProcessInput(const SDL_Event& event, bool& processed) override; void Draw(SDL_Renderer* target) override; void Update() override; void Init(); diff --git a/src/MainGameState.hpp b/src/MainGameState.hpp index 132764e..2350933 100644 --- a/src/MainGameState.hpp +++ b/src/MainGameState.hpp @@ -31,9 +31,9 @@ public: MainGameState(); MainGameState(const MainGameState& orig) = delete; virtual ~MainGameState(); - + bool IsActive() override; - void ProcessInput(const SDL_Event& event, bool &processed) override; + void ProcessInput(const SDL_Event& event, bool& processed) override; void Draw(SDL_Renderer* target) override; void Update() override; diff --git a/src/PuzzleSingleImageState.cpp b/src/PuzzleSingleImageState.cpp index 0dfb6e7..f02fc62 100644 --- a/src/PuzzleSingleImageState.cpp +++ b/src/PuzzleSingleImageState.cpp @@ -29,9 +29,11 @@ https://github.com/sago007/saland #include #include "SagoImGui.hpp" #include "rhash.hpp" +#include "config.hpp" PuzzleSingleImageState::PuzzleSingleImageState() { - + std::map config = LoadConfigMap(); + flipMode = GetConfigBool(config, "flipMode", false); } PuzzleSingleImageState::~PuzzleSingleImageState() { @@ -127,6 +129,10 @@ void PuzzleSingleImageState::Draw(SDL_Renderer* target) { if (ImGui::BeginMenu("Settings")) { if (ImGui::MenuItem("Flip Mode", nullptr, flipMode)) { flipMode = !flipMode; + // Save the updated configuration + std::map config = LoadConfigMap(); + SetConfigBool(config, "flipMode", flipMode); + SaveConfigMap(config); Shuffle(); } ImGui::EndMenu(); diff --git a/src/PuzzleSingleImageState.hpp b/src/PuzzleSingleImageState.hpp index 6556ac7..7ff97f0 100644 --- a/src/PuzzleSingleImageState.hpp +++ b/src/PuzzleSingleImageState.hpp @@ -35,7 +35,7 @@ public: virtual ~PuzzleSingleImageState(); bool IsActive() override; - void ProcessInput(const SDL_Event& event, bool &processed) override; + void ProcessInput(const SDL_Event& event, bool& processed) override; void Draw(SDL_Renderer* target) override; void Update() override; diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 0000000..490016f --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,109 @@ +/* +Copyright (c) 2025 Poul Sander + +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. +*/ + +#include "config.hpp" +#include "os.hpp" +#include + +std::map LoadConfigMap() { + std::map config; + std::string configPath = getPathToSaveFiles() + "/config.txt"; + std::ifstream configFile(configPath); + + if (configFile.is_open()) { + std::string line; + while (std::getline(configFile, line)) { + // Skip empty lines and comments + if (line.empty() || line[0] == '#') { + continue; + } + + // Find the '=' separator + size_t separatorPos = line.find('='); + if (separatorPos != std::string::npos) { + std::string key = line.substr(0, separatorPos); + std::string value = line.substr(separatorPos + 1); + config[key] = value; + } + } + configFile.close(); + } + + return config; +} + +void SaveConfigMap(const std::map& config) { + std::string configPath = getPathToSaveFiles() + "/config.txt"; + std::ofstream configFile(configPath); + + if (configFile.is_open()) { + configFile << "# Sago Multi Scrambler Puzzle II Configuration" << std::endl; + for (const auto& pair : config) { + configFile << pair.first << "=" << pair.second << std::endl; + } + configFile.close(); + } +} + +bool GetConfigBool(const std::map& config, const std::string& key, bool defaultValue) { + auto it = config.find(key); + if (it != config.end()) { + const std::string& value = it->second; + return (value == "1" || value == "true"); + } + return defaultValue; +} + +void SetConfigBool(std::map& config, const std::string& key, bool value) { + config[key] = value ? "1" : "0"; +} + +int GetConfigInt(const std::map& config, const std::string& key, int defaultValue) { + auto it = config.find(key); + if (it != config.end()) { + try { + return std::stoi(it->second); + } + catch (...) { + return defaultValue; + } + } + return defaultValue; +} + +void SetConfigInt(std::map& config, const std::string& key, int value) { + config[key] = std::to_string(value); +} + +std::string GetConfigString(const std::map& config, const std::string& key, const std::string& defaultValue) { + auto it = config.find(key); + if (it != config.end()) { + return it->second; + } + return defaultValue; +} + +void SetConfigString(std::map& config, const std::string& key, const std::string& value) { + config[key] = value; +} diff --git a/src/config.hpp b/src/config.hpp new file mode 100644 index 0000000..11a41ea --- /dev/null +++ b/src/config.hpp @@ -0,0 +1,94 @@ +/* +Copyright (c) 2025 Poul Sander + +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 CONFIG_HPP +#define CONFIG_HPP + +#include +#include + +/** + * @brief Loads configuration from file into a map + * @return Map of configuration key-value pairs + */ +std::map LoadConfigMap(); + +/** + * @brief Saves configuration map to file + * @param config Map of configuration key-value pairs + */ +void SaveConfigMap(const std::map& config); + +/** + * @brief Gets a boolean value from config map + * @param config The configuration map + * @param key The configuration key + * @param defaultValue Default value if key not found + * @return The boolean value + */ +bool GetConfigBool(const std::map& config, const std::string& key, bool defaultValue); + +/** + * @brief Sets a boolean value in config map + * @param config The configuration map + * @param key The configuration key + * @param value The boolean value + */ +void SetConfigBool(std::map& config, const std::string& key, bool value); + +/** + * @brief Gets an integer value from config map + * @param config The configuration map + * @param key The configuration key + * @param defaultValue Default value if key not found + * @return The integer value + */ +int GetConfigInt(const std::map& config, const std::string& key, int defaultValue); + +/** + * @brief Sets an integer value in config map + * @param config The configuration map + * @param key The configuration key + * @param value The integer value + */ +void SetConfigInt(std::map& config, const std::string& key, int value); + +/** + * @brief Gets a string value from config map + * @param config The configuration map + * @param key The configuration key + * @param defaultValue Default value if key not found + * @return The string value + */ +std::string GetConfigString(const std::map& config, const std::string& key, const std::string& defaultValue); + +/** + * @brief Sets a string value in config map + * @param config The configuration map + * @param key The configuration key + * @param value The string value + */ +void SetConfigString(std::map& config, const std::string& key, const std::string& value); + +#endif // CONFIG_HPP diff --git a/src/rhash.hpp b/src/rhash.hpp index cde203e..8ddd1d7 100644 --- a/src/rhash.hpp +++ b/src/rhash.hpp @@ -29,109 +29,109 @@ https://github.com/sago007/saland class RHash { public: - RHash(unsigned hash_mask) { - hash_ids.push_back(hash_mask); - } - - RHash(std::initializer_list hash_list) : RHash(0) { - for (unsigned hash_id : hash_list) { - add_hash(hash_id); - } - } - - ~RHash() { - if (ctx_) { - rhash_free(ctx_); - ctx_ = nullptr; - } - } - - void reinit() { - if (ctx_) { - rhash_free(ctx_); - ctx_ = nullptr; - } - unsigned hash_mask = 0; - for (unsigned hash_id : hash_ids) { - hash_mask |= hash_id; - } - ctx_ = rhash_init(hash_mask); - if (ctx_ == nullptr) { - throw std::runtime_error("Failed to initialize RHash context"); - } - done = false; - } - - void add_hash(unsigned hash_id) { - hash_ids.push_back(hash_id); - } - - void update(const std::string& data) { - if (!ctx_) { - reinit(); - } - if (done) { - throw std::runtime_error("\"update\" called on a finalized rhash context"); - } - rhash_update(ctx_, data.c_str(), data.length()); - length += data.length(); - } - - void finish() { - rhash_final(ctx_, nullptr); - done = true; - } - - std::string hash_as_string(unsigned hash_id, int flags) { - if (!done) { - finish(); - } - size_t length = rhash_get_hash_length(hash_id); - std::vector buffer(length); - rhash_print(buffer.data(), ctx_, hash_id, flags); - return std::string(buffer.data(), buffer.size()); - } - - std::string hex(unsigned hash_id) { - return hash_as_string(hash_id, RHPR_HEX); - } - - std::string HEX(unsigned hash_id) { - return hash_as_string(hash_id, RHPR_HEX | RHPR_UPPERCASE); - } - - std::string base32(unsigned hash_id) { - return hash_as_string(hash_id, RHPR_BASE32); - } - - std::string base64(unsigned hash_id) { - return hash_as_string(hash_id, RHPR_BASE64); - } - - size_t size() { - return length; - } - - void hash_file(const std::string& filename) { - FILE* file = fopen(filename.c_str(), "rb"); - if (file == nullptr) { - throw std::runtime_error("Failed to open file for hashing: " + filename); - } - reinit(); //Unconditionally reinit when loading a new file - char buffer[1024]; - size_t bytes_read; - while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) { - rhash_update(ctx_, buffer, bytes_read); - length += bytes_read; - } - - fclose(file); - rhash_final(ctx_, nullptr); - } + RHash(unsigned hash_mask) { + hash_ids.push_back(hash_mask); + } + + RHash(std::initializer_list hash_list) : RHash(0) { + for (unsigned hash_id : hash_list) { + add_hash(hash_id); + } + } + + ~RHash() { + if (ctx_) { + rhash_free(ctx_); + ctx_ = nullptr; + } + } + + void reinit() { + if (ctx_) { + rhash_free(ctx_); + ctx_ = nullptr; + } + unsigned hash_mask = 0; + for (unsigned hash_id : hash_ids) { + hash_mask |= hash_id; + } + ctx_ = rhash_init(hash_mask); + if (ctx_ == nullptr) { + throw std::runtime_error("Failed to initialize RHash context"); + } + done = false; + } + + void add_hash(unsigned hash_id) { + hash_ids.push_back(hash_id); + } + + void update(const std::string& data) { + if (!ctx_) { + reinit(); + } + if (done) { + throw std::runtime_error("\"update\" called on a finalized rhash context"); + } + rhash_update(ctx_, data.c_str(), data.length()); + length += data.length(); + } + + void finish() { + rhash_final(ctx_, nullptr); + done = true; + } + + std::string hash_as_string(unsigned hash_id, int flags) { + if (!done) { + finish(); + } + size_t length = rhash_get_hash_length(hash_id); + std::vector buffer(length); + rhash_print(buffer.data(), ctx_, hash_id, flags); + return std::string(buffer.data(), buffer.size()); + } + + std::string hex(unsigned hash_id) { + return hash_as_string(hash_id, RHPR_HEX); + } + + std::string HEX(unsigned hash_id) { + return hash_as_string(hash_id, RHPR_HEX | RHPR_UPPERCASE); + } + + std::string base32(unsigned hash_id) { + return hash_as_string(hash_id, RHPR_BASE32); + } + + std::string base64(unsigned hash_id) { + return hash_as_string(hash_id, RHPR_BASE64); + } + + size_t size() { + return length; + } + + void hash_file(const std::string& filename) { + FILE* file = fopen(filename.c_str(), "rb"); + if (file == nullptr) { + throw std::runtime_error("Failed to open file for hashing: " + filename); + } + reinit(); //Unconditionally reinit when loading a new file + char buffer[1024]; + size_t bytes_read; + while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) { + rhash_update(ctx_, buffer, bytes_read); + length += bytes_read; + } + + fclose(file); + rhash_final(ctx_, nullptr); + } private: - struct rhash_context* ctx_ = nullptr; - std::vector hash_ids; - size_t length = 0; - bool done = false; + struct rhash_context* ctx_ = nullptr; + std::vector hash_ids; + size_t length = 0; + bool done = false; }; \ No newline at end of file