diff options
27 files changed, 652 insertions, 352 deletions
diff --git a/deps/openvic-dataloader b/deps/openvic-dataloader -Subproject b1f726c79f20f89641d7e091b7abeed48227fad +Subproject f55e1fc6090854dab49f994c41644a6cd86a5d4 diff --git a/src/headless/main.cpp b/src/headless/main.cpp index 69e1e67..9366f12 100644 --- a/src/headless/main.cpp +++ b/src/headless/main.cpp @@ -1,13 +1,13 @@ #ifdef OPENVIC_SIM_HEADLESS -#include <openvic/utility/Logger.hpp> #include <openvic/GameManager.hpp> #include <openvic/dataloader/Dataloader.hpp> +#include <openvic/utility/Logger.hpp> using namespace OpenVic; static char const* get_program_name(char const* name) { - static char const* const missing_name = "<program>"; + static constexpr char const* missing_name = "<program>"; if (name == nullptr) return missing_name; char const* last_separator = name; while (*name != '\0') { @@ -20,14 +20,8 @@ static char const* get_program_name(char const* name) { return last_separator; } -int main(int argc, char const* argv[]) { - if (argc < 2) { - std::cout << "Usage: " << get_program_name(argc > 0 ? argv[0] : nullptr) << " <base defines dir> [[mod defines dir]+]" << std::endl; - std::cout << "Requires defines path(s) as arguments, starting with the base defines and continuing with mods (paths with spaces need to be enclosed in \"quotes\")." << std::endl; - return -1; - } - - std::cout << "!!! HEADLESS SIMULATION START !!!" << std::endl; +static return_t headless_load(std::vector<std::filesystem::path> const& roots) { + return_t ret = SUCCESS; Logger::set_info_func([](std::string&& str) { std::cout << str; }); Logger::set_error_func([](std::string&& str) { std::cerr << str; }); @@ -37,20 +31,47 @@ int main(int argc, char const* argv[]) { } }; Dataloader dataloader; - std::vector<std::filesystem::path> roots; - for (int i = 1; i < argc; ++i) { - roots.push_back(argv[i]); + if (dataloader.set_roots(roots) != SUCCESS) { + Logger::error("Failed to set dataloader roots!"); + ret = FAILURE; } - dataloader.set_roots(roots); - if (dataloader.load_defines(game_manager) != SUCCESS) { Logger::error("Failed to load defines!"); + ret = FAILURE; } if (game_manager.load_hardcoded_defines() != SUCCESS) { Logger::error("Failed to load hardcoded defines!"); + ret = FAILURE; } + return ret; +} + +int main(int argc, char const* argv[]) { + std::vector<std::filesystem::path> roots; + if (argc < 2) { + // TODO - non-Windows default paths + static const std::filesystem::path default_path = "C:/Program Files (x86)/Steam/steamapps/common/Victoria 2"; + + std::cout << "Usage: " << get_program_name(argc > 0 ? argv[0] : nullptr) << " <base defines dir> [[mod defines dir]+]\n" + << "Requires defines path(s) as arguments, starting with the base defines and continuing with mods.\n" + << "(Paths with spaces need to be enclosed in \"quotes\").\n" + << "Defaulting to " << default_path << std::endl; + roots.push_back(default_path); + } else { + for (int i = 1; i < argc; ++i) { + roots.push_back(argv[i]); + } + } + + std::cout << "!!! HEADLESS SIMULATION START !!!" << std::endl; + + const return_t ret = headless_load(roots); + std::cout << "!!! HEADLESS SIMULATION END !!!" << std::endl; - return 0; + + std::cout << "\nLoad returned: " << (ret == SUCCESS ? "SUCCESS" : "FAILURE") << std::endl; + + return ret == SUCCESS ? 0 : -1; } #endif diff --git a/src/openvic/GameManager.cpp b/src/openvic/GameManager.cpp index b4fa8b5..f9c9664 100644 --- a/src/openvic/GameManager.cpp +++ b/src/openvic/GameManager.cpp @@ -103,19 +103,14 @@ return_t GameManager::load_hardcoded_defines() { return HIGH_ALPHA_VALUE | (fraction_to_colour_byte(province.get_total_population(), map.get_highest_province_population() + 1, 0.1f, 1.0f) << 8); } }, { "mapmode_culture", - // TODO - use std::max_element and maybe change distribution_t to be an ordered std::map [](Map const& map, Province const& province) -> colour_t { - distribution_t const& cultures = province.get_culture_distribution(); - if (!cultures.empty()) { - // This breaks if replaced with distribution_t::value_type, something - // about operator=(volatile const&) being deleted. - std::pair<HasIdentifierAndColour const*, float> culture = *cultures.begin(); - for (distribution_t::value_type const p : cultures) { - if (p.second > culture.second) culture = p; - } - return HIGH_ALPHA_VALUE | culture.first->get_colour(); - } - return NULL_COLOUR; + HasIdentifierAndColour const* largest = get_largest_item(province.get_culture_distribution()).first; + return largest != nullptr ? HIGH_ALPHA_VALUE | largest->get_colour() : NULL_COLOUR; + } }, + { "mapmode_religion", + [](Map const& map, Province const& province) -> colour_t { + HasIdentifierAndColour const* largest = get_largest_item(province.get_religion_distribution()).first; + return largest != nullptr ? HIGH_ALPHA_VALUE | largest->get_colour() : NULL_COLOUR; } } }; for (mapmode_t const& mapmode : mapmodes) diff --git a/src/openvic/dataloader/Dataloader.cpp b/src/openvic/dataloader/Dataloader.cpp index 90537c9..f2b725e 100644 --- a/src/openvic/dataloader/Dataloader.cpp +++ b/src/openvic/dataloader/Dataloader.cpp @@ -1,10 +1,10 @@ #include "Dataloader.hpp" +#include "openvic/GameManager.hpp" #include "openvic/utility/Logger.hpp" -#include "openvic//GameManager.hpp" -#include "openvic/dataloader/NodeTools.hpp" #include <openvic-dataloader/detail/CallbackOStream.hpp> +#include <openvic-dataloader/v2script/Parser.hpp> using namespace OpenVic; using namespace ovdl::v2script; @@ -14,19 +14,26 @@ return_t Dataloader::set_roots(std::vector<std::filesystem::path> new_roots) { Logger::error("Overriding existing dataloader roots!"); roots.clear(); } + return_t ret = SUCCESS; for (std::reverse_iterator<std::vector<std::filesystem::path>::const_iterator> it = new_roots.crbegin(); it != new_roots.crend(); ++it) { - if (std::filesystem::is_directory(*it)) { - Logger::info("Adding dataloader root: ", *it); - roots.push_back(*it); + if (std::find(roots.begin(), roots.end(), *it) == roots.end()) { + if (std::filesystem::is_directory(*it)) { + Logger::info("Adding dataloader root: ", *it); + roots.push_back(*it); + } else { + Logger::error("Invalid dataloader root (must be an existing directory): ", *it); + ret = FAILURE; + } } else { - Logger::error("Invalid dataloader root (must be an existing directory): ", *it); + Logger::error("Duplicate dataloader root: ", *it); + ret = FAILURE; } } if (roots.empty()) { Logger::error("Dataloader has no roots after attempting to add ", new_roots.size()); - return FAILURE; + ret = FAILURE; } - return SUCCESS; + return ret; } std::filesystem::path Dataloader::lookup_file(std::filesystem::path const& path) const { @@ -40,28 +47,52 @@ std::filesystem::path Dataloader::lookup_file(std::filesystem::path const& path) return {}; } -static bool contains_file_with_name(std::vector<std::filesystem::path> const& paths, std::filesystem::path const& name) { +const std::filesystem::path Dataloader::TXT = ".txt"; + +static bool contains_file_with_name(std::vector<std::filesystem::path> const& paths, + std::filesystem::path const& name) { + for (std::filesystem::path const& path : paths) { if (path.filename() == name) return true; } return false; } -std::vector<std::filesystem::path> Dataloader::lookup_files_in_dir(std::filesystem::path const& path) const { +std::vector<std::filesystem::path> Dataloader::lookup_files_in_dir(std::filesystem::path const& path, + std::filesystem::path const* extension) const { + std::vector<std::filesystem::path> ret; for (std::filesystem::path const& root : roots) { const std::filesystem::path composed = root / path; std::error_code ec; for (std::filesystem::directory_entry const& entry : std::filesystem::directory_iterator { composed, ec }) { - if (entry.is_regular_file() && !contains_file_with_name(ret, entry.path().filename())) { - ret.push_back(entry); + if (entry.is_regular_file()) { + const std::filesystem::path file = entry; + if (extension == nullptr || file.extension() == *extension) { + if (!contains_file_with_name(ret, file.filename())) { + ret.push_back(file); + } + } } } } return ret; } -static Parser parse_defines(std::filesystem::path const& path) { +return_t Dataloader::apply_to_files_in_dir(std::filesystem::path const& path, + std::function<return_t(std::filesystem::path const&)> callback, + std::filesystem::path const* extension) const { + + return_t ret = SUCCESS; + for (std::filesystem::path const& file : lookup_files_in_dir(path, extension)) { + if (callback(file) != SUCCESS) { + ret = FAILURE; + } + } + return ret; +} + +Parser Dataloader::parse_defines(std::filesystem::path const& path) { Parser parser; std::string buffer; auto error_log_stream = ovdl::detail::CallbackStream { @@ -81,8 +112,8 @@ static Parser parse_defines(std::filesystem::path const& path) { Logger::error("Parser load errors:\n\n", buffer, "\n"); buffer.clear(); } - if (parser.has_fatal_error() || parser.has_error() || parser.has_warning()) { - Logger::error("Parser warnings/errors while loading ", path); + if (parser.has_fatal_error() || parser.has_error()) { + Logger::error("Parser errors while loading ", path); return parser; } parser.simple_parse(); @@ -90,51 +121,74 @@ static Parser parse_defines(std::filesystem::path const& path) { Logger::error("Parser parse errors:\n\n", buffer, "\n"); buffer.clear(); } - if (parser.has_fatal_error() || parser.has_error() || parser.has_warning()) { - Logger::error("Parser warnings/errors while parsing ", path); + if (parser.has_fatal_error() || parser.has_error()) { + Logger::error("Parser errors while parsing ", path); } return parser; } +Parser Dataloader::parse_defines_lookup(std::filesystem::path const& path) const { + return parse_defines(lookup_file(path)); +} + +return_t Dataloader::_load_pop_types(PopManager& pop_manager, std::filesystem::path const& pop_type_directory) const { + return_t ret = SUCCESS; + if (apply_to_files_in_dir(pop_type_directory, [&pop_manager](std::filesystem::path const& file) -> return_t { + return pop_manager.load_pop_type_file(file, parse_defines(file).get_file_node()); + }) != SUCCESS) { + Logger::error("Failed to load pop types!"); + ret = FAILURE; + } + pop_manager.lock_pop_types(); + return ret; +} + return_t Dataloader::load_defines(GameManager& game_manager) const { + static const std::filesystem::path good_file = "common/goods.txt"; + static const std::filesystem::path pop_type_directory = "poptypes"; static const std::filesystem::path graphical_culture_type_file = "common/graphicalculturetype.txt"; static const std::filesystem::path culture_file = "common/cultures.txt"; + static const std::filesystem::path religion_file = "common/religion.txt"; return_t ret = SUCCESS; - if (game_manager.pop_manager.culture_manager.load_graphical_culture_type_file(parse_defines( - lookup_file(graphical_culture_type_file)).get_file_node()) != SUCCESS) { + if (game_manager.good_manager.load_good_file(parse_defines_lookup(good_file).get_file_node()) != SUCCESS) { + Logger::error("Failed to load goods!"); + ret = FAILURE; + } + if (_load_pop_types(game_manager.pop_manager, pop_type_directory) != SUCCESS) { + Logger::error("Failed to load pop types!"); + ret = FAILURE; + } + if (game_manager.pop_manager.culture_manager.load_graphical_culture_type_file(parse_defines_lookup(graphical_culture_type_file).get_file_node()) != SUCCESS) { Logger::error("Failed to load graphical culture types!"); ret = FAILURE; } - if (game_manager.pop_manager.culture_manager.load_culture_file(parse_defines( - lookup_file(culture_file)).get_file_node()) != SUCCESS) { + if (game_manager.pop_manager.culture_manager.load_culture_file(parse_defines_lookup(culture_file).get_file_node()) != SUCCESS) { Logger::error("Failed to load cultures!"); ret = FAILURE; } + if (game_manager.pop_manager.religion_manager.load_religion_file(parse_defines_lookup(religion_file).get_file_node()) != SUCCESS) { + Logger::error("Failed to load religions!"); + ret = FAILURE; + } return ret; } -static return_t load_pop_history_file(GameManager& game_manager, std::filesystem::path const& path) { - return NodeTools::expect_dictionary(parse_defines(path).get_file_node(), [&game_manager](std::string_view province_key, ast::NodeCPtr province_node) -> return_t { - Province* province = game_manager.map.get_province_by_identifier(province_key); - if (province == nullptr) { - Logger::error("Invalid province id: ", province_key); - return FAILURE; - } - return NodeTools::expect_list(province_node, [&game_manager, &province](ast::NodeCPtr pop_node) -> return_t { - return game_manager.pop_manager.load_pop_into_province(*province, pop_node); - }); - }, true); -} - return_t Dataloader::load_pop_history(GameManager& game_manager, std::filesystem::path const& path) const { - return_t ret = SUCCESS; - for (std::filesystem::path const& file : lookup_files_in_dir(path)) { - if (load_pop_history_file(game_manager, file) != SUCCESS) { - ret = FAILURE; - } - } - return ret; + return apply_to_files_in_dir(path, [&game_manager](std::filesystem::path const& file) -> return_t { + return NodeTools::expect_dictionary(parse_defines(file).get_file_node(), + [&game_manager](std::string_view province_key, ast::NodeCPtr province_node) -> return_t { + Province* province = game_manager.map.get_province_by_identifier(province_key); + if (province == nullptr) { + Logger::error("Invalid province id: ", province_key); + return FAILURE; + } + return NodeTools::expect_list(province_node, [&game_manager, &province](ast::NodeCPtr pop_node) -> return_t { + return game_manager.pop_manager.load_pop_into_province(*province, pop_node); + } + ); + }, true); + }); } diff --git a/src/openvic/dataloader/Dataloader.hpp b/src/openvic/dataloader/Dataloader.hpp index b080300..3c868a3 100644 --- a/src/openvic/dataloader/Dataloader.hpp +++ b/src/openvic/dataloader/Dataloader.hpp @@ -1,14 +1,23 @@ #pragma once +#include <filesystem> + #include "openvic/types/Return.hpp" + #include "openvic-dataloader/v2script/Parser.hpp" namespace OpenVic { struct GameManager; + struct PopManager; class Dataloader { std::vector<std::filesystem::path> roots; + static ovdl::v2script::Parser parse_defines(std::filesystem::path const& path); + ovdl::v2script::Parser parse_defines_lookup(std::filesystem::path const& path) const; + + return_t _load_pop_types(PopManager& pop_manager, std::filesystem::path const& pop_type_directory) const; + public: Dataloader() = default; @@ -16,7 +25,12 @@ namespace OpenVic { return_t set_roots(std::vector<std::filesystem::path> new_roots); std::filesystem::path lookup_file(std::filesystem::path const& path) const; - std::vector<std::filesystem::path> lookup_files_in_dir(std::filesystem::path const& path) const; + static const std::filesystem::path TXT; + std::vector<std::filesystem::path> lookup_files_in_dir(std::filesystem::path const& path, + std::filesystem::path const* extension = &TXT) const; + return_t apply_to_files_in_dir(std::filesystem::path const& path, + std::function<return_t(std::filesystem::path const&)> callback, + std::filesystem::path const* extension = &TXT) const; return_t load_defines(GameManager& game_manager) const; return_t load_pop_history(GameManager& game_manager, std::filesystem::path const& path) const; diff --git a/src/openvic/dataloader/NodeTools.cpp b/src/openvic/dataloader/NodeTools.cpp index a37892f..6ab2ba6 100644 --- a/src/openvic/dataloader/NodeTools.cpp +++ b/src/openvic/dataloader/NodeTools.cpp @@ -66,11 +66,9 @@ return_t NodeTools::expect_bool(ast::NodeCPtr node, std::function<return_t(bool) return_t NodeTools::expect_int(ast::NodeCPtr node, std::function<return_t(int64_t)> callback) { return expect_identifier(node, [callback](std::string_view identifier) -> return_t { - int64_t val; - char const* const start = identifier.data(); - char const* const end = start + identifier.size(); - const std::from_chars_result result = std::from_chars(start, end, val); - if (result.ec == std::errc{} && result.ptr == end) { + bool successful = false; + const int64_t val = StringUtils::string_to_int64(identifier, &successful, 10); + if (successful) { return callback(val); } Logger::error("Invalid int identifier text: ", identifier); @@ -80,10 +78,9 @@ return_t NodeTools::expect_int(ast::NodeCPtr node, std::function<return_t(int64_ return_t NodeTools::expect_uint(ast::NodeCPtr node, std::function<return_t(uint64_t)> callback) { return expect_identifier(node, [callback](std::string_view identifier) -> return_t { - uint64_t val; - char const* identifier_end = identifier.data() + identifier.size(); - const std::from_chars_result result = std::from_chars(identifier.data(), identifier_end, val); - if (result.ec == std::errc{} && result.ptr == identifier_end) { + bool successful = false; + const uint64_t val = StringUtils::string_to_uint64(identifier, &successful, 10); + if (successful) { return callback(val); } Logger::error("Invalid uint identifier text: ", identifier); @@ -94,7 +91,7 @@ return_t NodeTools::expect_uint(ast::NodeCPtr node, std::function<return_t(uint6 return_t NodeTools::expect_fixed_point(ast::NodeCPtr node, std::function<return_t(FP)> callback) { return expect_identifier(node, [callback](std::string_view identifier) -> return_t { bool successful = false; - FP val = FP::parse(identifier.data(), identifier.length(), &successful); + const FP val = FP::parse(identifier.data(), identifier.length(), &successful); if (successful) { return callback(val); } @@ -106,7 +103,7 @@ return_t NodeTools::expect_fixed_point(ast::NodeCPtr node, std::function<return_ return_t NodeTools::expect_colour(ast::NodeCPtr node, std::function<return_t(colour_t)> callback) { colour_t col = NULL_COLOUR; uint32_t components = 0; - return_t ret = expect_list(node, std::bind(expect_fixed_point, std::placeholders::_1, + return_t ret = expect_list_of_length(node, std::bind(expect_fixed_point, std::placeholders::_1, [&col, &components](FP val) -> return_t { return_t ret = SUCCESS; if (val < 0 || val > 255) { @@ -127,7 +124,7 @@ return_t NodeTools::expect_colour(ast::NodeCPtr node, std::function<return_t(col return_t NodeTools::expect_date(ast::NodeCPtr node, std::function<return_t(Date)> callback) { return expect_identifier(node, [callback](std::string_view identifier) -> return_t { bool successful = false; - Date date = Date::from_string(identifier, &successful); + const Date date = Date::from_string(identifier, &successful); if (successful) { return callback(date); } @@ -142,16 +139,16 @@ return_t NodeTools::expect_assign(ast::NodeCPtr node, std::function<return_t(std }); } -return_t NodeTools::expect_list(ast::NodeCPtr node, std::function<return_t(ast::NodeCPtr)> callback, size_t length, bool file_node) { - const std::function<return_t(std::vector<ast::NodeUPtr> const&)> list_func = [length, callback](std::vector<ast::NodeUPtr> const& list) -> return_t { +return_t NodeTools::expect_list_and_length(ast::NodeCPtr node, std::function<size_t(size_t)> length_callback, std::function<return_t(ast::NodeCPtr)> callback, bool file_node) { + const std::function<return_t(std::vector<ast::NodeUPtr> const&)> list_func = [length_callback, callback](std::vector<ast::NodeUPtr> const& list) -> return_t { return_t ret = SUCCESS; - size_t size = list.size(); - if (length > 0 && size != length) { - Logger::error("List length ", size, " does not match expected length ", length); + size_t size = length_callback(list.size()); + if (size > list.size()) { + Logger::error("Trying to read more values than the list contains: ", size, " > ", list.size()); + size = list.size(); ret = FAILURE; - if (length < size) size = length; } - std::for_each(list.begin(), list.begin() + size, [callback, &ret](ast::NodeUPtr const& sub_node) { + std::for_each(list.begin(), list.begin() + size, [callback, &ret](ast::NodeUPtr const& sub_node) -> void { if (callback(sub_node.get()) != SUCCESS) ret = FAILURE; }); return ret; @@ -167,13 +164,34 @@ return_t NodeTools::expect_list(ast::NodeCPtr node, std::function<return_t(ast:: } } -return_t NodeTools::expect_dictionary(ast::NodeCPtr node, std::function<return_t(const std::string_view, ast::NodeCPtr)> callback, bool file_node) { - return expect_list(node, std::bind(expect_assign, std::placeholders::_1, callback), 0, file_node); +return_t NodeTools::expect_list_of_length(ast::NodeCPtr node, std::function<return_t(ast::NodeCPtr)> callback, size_t length, bool file_node) { + return_t ret = SUCCESS; + if (expect_list_and_length(node, [length, &ret](size_t size) -> size_t { + if (size != length) { + Logger::error("List length ", size, " does not match expected length ", length); + ret = FAILURE; + if (length < size) return length; + } + return size; + }, callback, file_node) != SUCCESS) ret = FAILURE; + return ret; +} + +return_t NodeTools::expect_list(ast::NodeCPtr node, std::function<return_t(ast::NodeCPtr)> callback, bool file_node) { + return expect_list_and_length(node, default_length_callback, callback, file_node); +} + +return_t NodeTools::expect_dictionary_and_length(ast::NodeCPtr node, std::function<size_t(size_t)> length_callback, std::function<return_t(std::string_view, ast::NodeCPtr)> callback, bool file_node) { + return expect_list_and_length(node, length_callback, std::bind(expect_assign, std::placeholders::_1, callback), file_node); +} + +return_t NodeTools::expect_dictionary(ast::NodeCPtr node, std::function<return_t(std::string_view, ast::NodeCPtr)> callback, bool file_node) { + return expect_dictionary_and_length(node, default_length_callback, callback, file_node); } return_t NodeTools::expect_dictionary_keys(ast::NodeCPtr node, dictionary_key_map_t const& keys, bool allow_other_keys, bool file_node) { std::map<std::string, size_t, std::less<void>> key_count; - return_t ret = expect_dictionary(node, [keys, allow_other_keys, &key_count](const std::string_view key, ast::NodeCPtr value) -> return_t { + return_t ret = expect_dictionary(node, [keys, allow_other_keys, &key_count](std::string_view key, ast::NodeCPtr value) -> return_t { const dictionary_key_map_t::const_iterator it = keys.find(key); if (it == keys.end()) { if (allow_other_keys) return SUCCESS; diff --git a/src/openvic/dataloader/NodeTools.hpp b/src/openvic/dataloader/NodeTools.hpp index ca7130c..48c3710 100644 --- a/src/openvic/dataloader/NodeTools.hpp +++ b/src/openvic/dataloader/NodeTools.hpp @@ -2,13 +2,13 @@ #include <map> -#include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> - #include "openvic/types/Colour.hpp" -#include "openvic/types/Return.hpp" #include "openvic/types/Date.hpp" +#include "openvic/types/Return.hpp" #include "openvic/types/fixed_point/FP.hpp" +#include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> + namespace OpenVic { namespace ast = ovdl::v2script::ast; @@ -27,7 +27,24 @@ namespace OpenVic { return_t expect_colour(ast::NodeCPtr node, std::function<return_t(colour_t)> callback); return_t expect_date(ast::NodeCPtr node, std::function<return_t(Date)> callback); return_t expect_assign(ast::NodeCPtr node, std::function<return_t(std::string_view, ast::NodeCPtr)> callback); - return_t expect_list(ast::NodeCPtr node, std::function<return_t(ast::NodeCPtr)> callback, size_t length = 0, bool file_node = false); + + static const std::function<size_t(size_t)> default_length_callback = [](size_t size) -> size_t { return size; }; + + template<typename T> requires requires(T& t) { + { t.size() } -> std::same_as<size_t>; + t.reserve( size_t {} ); + } + std::function<size_t(size_t)> reserve_length_callback(T& t) { + return [&t](size_t size) -> size_t { + t.reserve(t.size() + size); + return size; + }; + } + + return_t expect_list_and_length(ast::NodeCPtr node, std::function<size_t(size_t)> length_callback, std::function<return_t(ast::NodeCPtr)> callback, bool file_node = false); + return_t expect_list_of_length(ast::NodeCPtr node, std::function<return_t(ast::NodeCPtr)> callback, size_t length, bool file_node = false); + return_t expect_list(ast::NodeCPtr node, std::function<return_t(ast::NodeCPtr)> callback, bool file_node = false); + return_t expect_dictionary_and_length(ast::NodeCPtr node, std::function<size_t(size_t)> length_callback, std::function<return_t(std::string_view, ast::NodeCPtr)> callback, bool file_node = false); return_t expect_dictionary(ast::NodeCPtr node, std::function<return_t(std::string_view, ast::NodeCPtr)> callback, bool file_node = false); static const std::function<return_t(ast::NodeCPtr)> success_callback = [](ast::NodeCPtr) -> return_t { return SUCCESS; }; diff --git a/src/openvic/economy/Good.cpp b/src/openvic/economy/Good.cpp index f14c170..fe92e4f 100644 --- a/src/openvic/economy/Good.cpp +++ b/src/openvic/economy/Good.cpp @@ -4,7 +4,9 @@ using namespace OpenVic; -Good::Good(const std::string_view new_identifier, colour_t new_colour, const std::string_view new_category, price_t new_base_price, +GoodCategory::GoodCategory(const std::string_view new_identifier) : HasIdentifier { new_identifier } {} + +Good::Good(const std::string_view new_identifier, colour_t new_colour, GoodCategory const& new_category, price_t new_base_price, bool new_default_available, bool new_tradeable, bool new_currency, bool new_overseas_maintenance) : HasIdentifierAndColour { new_identifier, new_colour, true }, category { new_category }, @@ -16,7 +18,7 @@ Good::Good(const std::string_view new_identifier, colour_t new_colour, const std assert(base_price > NULL_PRICE); } -std::string const& Good::get_category() const { +GoodCategory const& Good::get_category() const { return category; } @@ -41,9 +43,25 @@ void Good::reset_to_defaults() { price = base_price; } -GoodManager::GoodManager() : goods { "goods" } {} +GoodManager::GoodManager() : good_categories { "good categories" }, goods { "goods" } {} + +return_t GoodManager::add_good_category(const std::string_view identifier) { + if (identifier.empty()) { + Logger::error("Invalid good category identifier - empty!"); + return FAILURE; + } + return good_categories.add_item({ identifier }); +} + +void GoodManager::lock_good_categories() { + good_categories.lock(); +} + +GoodCategory const* GoodManager::get_good_category_by_identifier(const std::string_view identifier) const { + return good_categories.get_item_by_identifier(identifier); +} -return_t GoodManager::add_good(const std::string_view identifier, colour_t colour, const std::string_view category, +return_t GoodManager::add_good(const std::string_view identifier, colour_t colour, GoodCategory const* category, Good::price_t base_price, bool default_available, bool tradeable, bool currency, bool overseas_maintenance) { if (identifier.empty()) { Logger::error("Invalid good identifier - empty!"); @@ -53,26 +71,21 @@ return_t GoodManager::add_good(const std::string_view identifier, colour_t colou Logger::error("Invalid good colour for ", identifier, ": ", Good::colour_to_hex_string(colour)); return FAILURE; } - if (category.empty()) { - Logger::error("Invalid good category for ", identifier, ": empty!"); + if (category == nullptr) { + Logger::error("Invalid good category for ", identifier, ": null"); return FAILURE; } if (base_price <= Good::NULL_PRICE) { Logger::error("Invalid base price for ", identifier, ": ", base_price); return FAILURE; } - return goods.add_item({ identifier, colour, category, base_price, default_available, tradeable, currency, overseas_maintenance }); + return goods.add_item({ identifier, colour, *category, base_price, default_available, tradeable, currency, overseas_maintenance }); } void GoodManager::lock_goods() { goods.lock(); } -void GoodManager::reset_to_defaults() { - for (Good& good : goods.get_items()) - good.reset_to_defaults(); -} - Good const* GoodManager::get_good_by_index(size_t index) const { return goods.get_item_by_index(index); } @@ -82,9 +95,26 @@ Good const* GoodManager::get_good_by_identifier(const std::string_view identifie } size_t GoodManager::get_good_count() const { - return goods.get_item_count(); + return goods.size(); } std::vector<Good> const& GoodManager::get_goods() const { return goods.get_items(); } + +void GoodManager::reset_to_defaults() { + for (Good& good : goods.get_items()) + good.reset_to_defaults(); +} + +return_t GoodManager::load_good_file(ast::NodeCPtr root) { + + // TODO - good loading + + return_t ret = add_good_category("test_good_category"); + lock_good_categories(); + if (add_good("test_good", 0x00FF00, get_good_category_by_identifier("test_good_category"), FP::_0_99(), true, true, false, false) != SUCCESS) + ret = FAILURE; + lock_goods(); + return ret; +} diff --git a/src/openvic/economy/Good.hpp b/src/openvic/economy/Good.hpp index 5c8e68c..a3cd10b 100644 --- a/src/openvic/economy/Good.hpp +++ b/src/openvic/economy/Good.hpp @@ -1,11 +1,21 @@ #pragma once #include "openvic/types/IdentifierRegistry.hpp" -#include "openvic/types/fixed_point/FP.hpp" +#include "openvic/dataloader/NodeTools.hpp" namespace OpenVic { struct GoodManager; + struct GoodCategory : HasIdentifier { + friend struct GoodManager; + + private: + GoodCategory(const std::string_view new_identifier); + + public: + GoodCategory(GoodCategory&&) = default; + }; + /* REQUIREMENTS: * * ECON-3 , ECON-4 , ECON-5 , ECON-6 , ECON-7 , ECON-8 , ECON-9 , ECON-10, ECON-11, ECON-12, ECON-13, ECON-14, @@ -25,19 +35,19 @@ namespace OpenVic { static constexpr price_t NULL_PRICE = FP::_0(); private: - const std::string category; + GoodCategory const& category; const price_t base_price; price_t price; const bool default_available, tradeable, currency, overseas_maintenance; bool available; - Good(const std::string_view new_identifier, colour_t new_colour, const std::string_view new_category, price_t new_base_price, + Good(const std::string_view new_identifier, colour_t new_colour, GoodCategory const& new_category, price_t new_base_price, bool new_default_available, bool new_tradeable, bool new_currency, bool new_overseas_maintenance); public: Good(Good&&) = default; - std::string const& get_category() const; + GoodCategory const& get_category() const; price_t get_base_price() const; price_t get_price() const; bool is_default_available() const; @@ -47,19 +57,25 @@ namespace OpenVic { struct GoodManager { private: + IdentifierRegistry<GoodCategory> good_categories; IdentifierRegistry<Good> goods; public: GoodManager(); - return_t add_good(const std::string_view identifier, colour_t colour, const std::string_view category, Good::price_t base_price, + return_t add_good_category(const std::string_view identifier); + void lock_good_categories(); + GoodCategory const* get_good_category_by_identifier(const std::string_view identifier) const; + + return_t add_good(const std::string_view identifier, colour_t colour, GoodCategory const* category, Good::price_t base_price, bool default_available, bool tradeable, bool currency, bool overseas_maintenance); void lock_goods(); - void reset_to_defaults(); - Good const* get_good_by_index(size_t index) const; Good const* get_good_by_identifier(const std::string_view identifier) const; size_t get_good_count() const; std::vector<Good> const& get_goods() const; + + void reset_to_defaults(); + return_t load_good_file(ast::NodeCPtr root); }; } diff --git a/src/openvic/map/Building.cpp b/src/openvic/map/Building.cpp index d2662c4..00e0135 100644 --- a/src/openvic/map/Building.cpp +++ b/src/openvic/map/Building.cpp @@ -2,8 +2,8 @@ #include <cassert> -#include "openvic/utility/Logger.hpp" #include "openvic/map/Province.hpp" +#include "openvic/utility/Logger.hpp" using namespace OpenVic; diff --git a/src/openvic/map/Map.cpp b/src/openvic/map/Map.cpp index 95d94fd..c9dd9d2 100644 --- a/src/openvic/map/Map.cpp +++ b/src/openvic/map/Map.cpp @@ -31,7 +31,7 @@ Map::Map() : provinces { "provinces" }, mapmodes { "mapmodes" } {} return_t Map::add_province(const std::string_view identifier, colour_t colour) { - if (provinces.get_item_count() >= Province::MAX_INDEX) { + if (provinces.size() >= Province::MAX_INDEX) { Logger::error("The map's province list is full - there can be at most ", Province::MAX_INDEX, " provinces"); return FAILURE; } @@ -43,7 +43,7 @@ return_t Map::add_province(const std::string_view identifier, colour_t colour) { Logger::error("Invalid province colour for ", identifier, ": ", Province::colour_to_hex_string(colour)); return FAILURE; } - Province new_province { identifier, colour, static_cast<Province::index_t>(provinces.get_item_count() + 1) }; + Province new_province { identifier, colour, static_cast<Province::index_t>(provinces.size() + 1) }; const Province::index_t index = get_index_from_colour(colour); if (index != Province::NULL_INDEX) { Logger::error("Duplicate province colours: ", get_province_by_index(index)->to_string(), " and ", new_province.to_string()); @@ -101,7 +101,7 @@ return_t Map::add_region(const std::string_view identifier, std::vector<std::str size_t other_region_index = reinterpret_cast<size_t>(province->get_region()); if (other_region_index != 0) { other_region_index--; - if (other_region_index < regions.get_item_count()) + if (other_region_index < regions.size()) Logger::error("Cannot add province ", province_identifier, " to region ", identifier, " - it is already part of ", regions.get_item_by_index(other_region_index)->get_identifier()); else Logger::error("Cannot add province ", province_identifier, " to region ", identifier, " - it is already part of an unknown region with index ", other_region_index); @@ -124,7 +124,7 @@ return_t Map::add_region(const std::string_view identifier, std::vector<std::str // Used to detect provinces listed in multiple regions, will // be corrected once regions is stable (i.e. lock_regions). - Region* tmp_region_index = reinterpret_cast<Region*>(regions.get_item_count()); + Region* tmp_region_index = reinterpret_cast<Region*>(regions.size()); for (Province* province : new_region.get_provinces()) province->region = tmp_region_index; if (regions.add_item(std::move(new_region)) != SUCCESS) ret = FAILURE; @@ -139,7 +139,7 @@ void Map::lock_regions() { } size_t Map::get_province_count() const { - return provinces.get_item_count(); + return provinces.size(); } Province* Map::get_province_by_index(Province::index_t index) { @@ -225,7 +225,7 @@ return_t Map::generate_province_shape_image(size_t new_width, size_t new_height, height = new_height; province_shape_image.resize(width * height); - std::vector<bool> province_checklist(provinces.get_item_count()); + std::vector<bool> province_checklist(provinces.size()); return_t ret = SUCCESS; std::unordered_set<colour_t> unrecognised_province_colours, unrecognised_terrain_colours; @@ -324,7 +324,7 @@ return_t Map::add_mapmode(const std::string_view identifier, Mapmode::colour_fun Logger::error("Mapmode colour function is null for identifier: ", identifier); return FAILURE; } - return mapmodes.add_item({ identifier, mapmodes.get_item_count(), colour_func }); + return mapmodes.add_item({ identifier, mapmodes.size(), colour_func }); } void Map::lock_mapmodes() { @@ -332,7 +332,7 @@ void Map::lock_mapmodes() { } size_t Map::get_mapmode_count() const { - return mapmodes.get_item_count(); + return mapmodes.size(); } Mapmode const* Map::get_mapmode_by_index(size_t index) const { @@ -354,7 +354,7 @@ return_t Map::generate_mapmode_colours(Mapmode::index_t index, uint8_t* target) // Not an error if mapmodes haven't yet been loaded, // e.g. if we want to allocate the province colour // texture before mapmodes are loaded. - if (!(mapmodes.get_item_count() == 0 && index == 0)) { + if (!(mapmodes.size() == 0 && index == 0)) { Logger::error("Invalid mapmode index: ", index); ret = FAILURE; } diff --git a/src/openvic/map/Province.cpp b/src/openvic/map/Province.cpp index 76e478f..f4588d4 100644 --- a/src/openvic/map/Province.cpp +++ b/src/openvic/map/Province.cpp @@ -91,6 +91,10 @@ distribution_t const& Province::get_culture_distribution() const { return cultures; } +distribution_t const& Province::get_religion_distribution() const { + return religions; +} + /* REQUIREMENTS: * MAP-65 */ @@ -98,10 +102,12 @@ void Province::update_pops() { total_population = 0; pop_types.clear(); cultures.clear(); + religions.clear(); for (Pop const& pop : pops) { total_population += pop.get_size(); pop_types[&pop.get_type()] += pop.get_size(); cultures[&pop.get_culture()] += pop.get_size(); + religions[&pop.get_religion()] += pop.get_size(); } } diff --git a/src/openvic/map/Province.hpp b/src/openvic/map/Province.hpp index 3556bcb..527a6fe 100644 --- a/src/openvic/map/Province.hpp +++ b/src/openvic/map/Province.hpp @@ -1,7 +1,7 @@ #pragma once -#include "openvic/pop/Pop.hpp" #include "openvic/map/Building.hpp" +#include "openvic/pop/Pop.hpp" namespace OpenVic { struct Map; @@ -30,7 +30,7 @@ namespace OpenVic { std::vector<Pop> pops; Pop::pop_size_t total_population; - distribution_t pop_types, cultures; + distribution_t pop_types, cultures, religions; Province(const std::string_view new_identifier, colour_t new_colour, index_t new_index); @@ -55,6 +55,7 @@ namespace OpenVic { Pop::pop_size_t get_total_population() const; distribution_t const& get_pop_type_distribution() const; distribution_t const& get_culture_distribution() const; + distribution_t const& get_religion_distribution() const; void update_pops(); void update_state(Date const& today); diff --git a/src/openvic/pop/Culture.cpp b/src/openvic/pop/Culture.cpp index 3bfb9b9..b0c04c2 100644 --- a/src/openvic/pop/Culture.cpp +++ b/src/openvic/pop/Culture.cpp @@ -101,11 +101,13 @@ Culture const* CultureManager::get_culture_by_identifier(const std::string_view } return_t CultureManager::load_graphical_culture_type_file(ast::NodeCPtr root) { - const return_t ret = NodeTools::expect_list(root, [this](ast::NodeCPtr node) -> return_t { - return NodeTools::expect_identifier(node, [this](std::string_view identifier) -> return_t { - return add_graphical_culture_type(identifier); - }); - }, 0, true); + const return_t ret = NodeTools::expect_list_and_length(root, + NodeTools::reserve_length_callback(graphical_culture_types), + [this](ast::NodeCPtr node) -> return_t { + return NodeTools::expect_identifier(node, [this](std::string_view identifier) -> return_t { + return add_graphical_culture_type(identifier); + }); + }, true); lock_graphical_culture_types(); return ret; } @@ -156,21 +158,25 @@ return_t CultureManager::load_culture_file(ast::NodeCPtr root) { return NodeTools::expect_dictionary(culture_group_value, [this, culture_group](std::string_view key, ast::NodeCPtr value) -> return_t { if (key == "leader" || key == "unit" || key == "union" || key == "is_overseas") return SUCCESS; - colour_t colour = NULL_COLOUR; + colour_t colour = NULL_COLOUR; Culture::name_list_t first_names, last_names; - static const std::function<return_t(Culture::name_list_t&, ast::NodeCPtr)> read_name_list = [](Culture::name_list_t& names, ast::NodeCPtr node) -> return_t { - return NodeTools::expect_list(node, [&names](ast::NodeCPtr val) -> return_t { - return NodeTools::expect_identifier_or_string(val, [&names](std::string_view str) -> return_t { - if (!str.empty()) { - names.push_back(std::string { str }); - return SUCCESS; + static const std::function<return_t(Culture::name_list_t&, ast::NodeCPtr)> read_name_list = + [](Culture::name_list_t& names, ast::NodeCPtr node) -> return_t { + return NodeTools::expect_list_and_length(node, + NodeTools::reserve_length_callback(names), + [&names](ast::NodeCPtr val) -> return_t { + return NodeTools::expect_identifier_or_string(val, [&names](std::string_view str) -> return_t { + if (!str.empty()) { + names.push_back(std::string { str }); + return SUCCESS; + } + Logger::error("Empty identifier or string"); + return FAILURE; + }); } - Logger::error("Empty identifier or string"); - return FAILURE; - }); - }); - }; + ); + }; return_t ret = NodeTools::expect_dictionary_keys(value, { { "color", { true, false, [&colour](ast::NodeCPtr node) -> return_t { diff --git a/src/openvic/pop/Culture.hpp b/src/openvic/pop/Culture.hpp index 7012512..495ba1d 100644 --- a/src/openvic/pop/Culture.hpp +++ b/src/openvic/pop/Culture.hpp @@ -1,7 +1,7 @@ #pragma once -#include "openvic/types/IdentifierRegistry.hpp" #include "openvic/dataloader/NodeTools.hpp" +#include "openvic/types/IdentifierRegistry.hpp" namespace OpenVic { @@ -72,7 +72,7 @@ namespace OpenVic { void lock_cultures(); Culture const* get_culture_by_identifier(const std::string_view identifier) const; - return_t load_graphical_culture_type_file(ast::NodeCPtr node); - return_t load_culture_file(ast::NodeCPtr node); + return_t load_graphical_culture_type_file(ast::NodeCPtr root); + return_t load_culture_file(ast::NodeCPtr root); }; } diff --git a/src/openvic/pop/Pop.cpp b/src/openvic/pop/Pop.cpp index 710d3e0..445aec3 100644 --- a/src/openvic/pop/Pop.cpp +++ b/src/openvic/pop/Pop.cpp @@ -98,27 +98,7 @@ bool PopType::get_is_slave() const { return is_slave; } -static const std::string test_graphical_culture_type = "test_graphical_culture_type"; -static const std::string test_culture_group = "test_culture_group"; -static const std::string test_culture = "test_culture"; -static const std::string test_religion_group = "test_religion_group"; -static const std::string test_religion = "test_religion"; -static const std::string test_pop_type_poor = "test_pop_type_poor"; -static const std::string test_pop_type_middle = "test_pop_type_middle"; -static const std::string test_pop_type_rich = "test_pop_type_rich"; - -PopManager::PopManager() : pop_types { "pop types" } { - religion_manager.add_religion_group(test_religion_group); - religion_manager.lock_religion_groups(); - - religion_manager.add_religion(test_religion, 0xFF0000, religion_manager.get_religion_group_by_identifier(test_religion_group), 1, false); - religion_manager.lock_religions(); - - add_pop_type(test_pop_type_poor, 0xFF0000, PopType::strata_t::POOR, 1, 1, 1, false, false, false, false); - add_pop_type(test_pop_type_middle, 0x00FF00, PopType::strata_t::MIDDLE, 1, 1, 1, false, false, false, false); - add_pop_type(test_pop_type_rich, 0x0000FF, PopType::strata_t::RICH, 1, 1, 1, false, false, false, false); - lock_pop_types(); -} +PopManager::PopManager() : pop_types { "pop types" } {} return_t PopManager::add_pop_type(const std::string_view identifier, colour_t colour, PopType::strata_t strata, PopType::sprite_t sprite, Pop::pop_size_t max_size, Pop::pop_size_t merge_max_size, bool state_capital_only, bool demote_migrant, bool is_artisan, bool is_slave) { @@ -153,8 +133,17 @@ PopType const* PopManager::get_pop_type_by_identifier(const std::string_view ide return pop_types.get_item_by_identifier(identifier); } +return_t PopManager::load_pop_type_file(std::filesystem::path const& path, ast::NodeCPtr root) { + + // TODO - pop type loading + + if (pop_types.empty()) + return add_pop_type("test_pop_type", 0xFF0000, PopType::strata_t::POOR, 1, 1, 1, false, false, false, false); + return SUCCESS; +} + return_t PopManager::load_pop_into_province(Province& province, ast::NodeCPtr root) const { - static PopType const* type = get_pop_type_by_identifier("test_pop_type_poor"); + static PopType const* type = get_pop_type_by_identifier("test_pop_type"); Culture const* culture = nullptr; static Religion const* religion = religion_manager.get_religion_by_identifier("test_religion"); Pop::pop_size_t size = 0; @@ -176,8 +165,7 @@ return_t PopManager::load_pop_into_province(Province& province, ast::NodeCPtr ro size = val; return SUCCESS; } else { - Logger::error("Invalid pop size: ", val, " (setting to 1 instead)"); - size = 1; + Logger::error("Invalid pop size: ", val); return FAILURE; } }); @@ -190,7 +178,7 @@ return_t PopManager::load_pop_into_province(Province& province, ast::NodeCPtr ro if (type != nullptr && culture != nullptr && religion != nullptr && size > 0) { if (province.add_pop({ *type, *culture, *religion, size }) != SUCCESS) ret = FAILURE; } else { - Logger::error("Some pop arguments are missing: type = ", type, ", culture = ", culture, ", religion = ", religion, ", size = ", size); + Logger::error("Some pop arguments are invalid: type = ", type, ", culture = ", culture, ", religion = ", religion, ", size = ", size); ret = FAILURE; } return ret; diff --git a/src/openvic/pop/Pop.hpp b/src/openvic/pop/Pop.hpp index f5a7c4e..144d53c 100644 --- a/src/openvic/pop/Pop.hpp +++ b/src/openvic/pop/Pop.hpp @@ -1,5 +1,7 @@ #pragma once +#include <filesystem> + #include "openvic/pop/Culture.hpp" #include "openvic/pop/Religion.hpp" @@ -94,6 +96,7 @@ namespace OpenVic { void lock_pop_types(); PopType const* get_pop_type_by_identifier(const std::string_view identifier) const; + return_t load_pop_type_file(std::filesystem::path const& path, ast::NodeCPtr root); return_t load_pop_into_province(Province& province, ast::NodeCPtr root) const; }; } diff --git a/src/openvic/pop/Religion.cpp b/src/openvic/pop/Religion.cpp index 0cfc7a6..8ff143f 100644 --- a/src/openvic/pop/Religion.cpp +++ b/src/openvic/pop/Religion.cpp @@ -78,3 +78,15 @@ void ReligionManager::lock_religions() { Religion const* ReligionManager::get_religion_by_identifier(const std::string_view identifier) const { return religions.get_item_by_identifier(identifier); } + +return_t ReligionManager::load_religion_file(ast::NodeCPtr root) { + + // TODO - religion loading + + return_t ret = add_religion_group("test_religion_group"); + lock_religion_groups(); + if (add_religion("test_religion", 0xFF0000, get_religion_group_by_identifier("test_religion_group"), 1, false) != SUCCESS) + ret = FAILURE; + lock_religions(); + return ret; +} diff --git a/src/openvic/pop/Religion.hpp b/src/openvic/pop/Religion.hpp index e629977..730454e 100644 --- a/src/openvic/pop/Religion.hpp +++ b/src/openvic/pop/Religion.hpp @@ -1,6 +1,7 @@ #pragma once #include "openvic/types/IdentifierRegistry.hpp" +#include "openvic/dataloader/NodeTools.hpp" namespace OpenVic { @@ -50,5 +51,7 @@ namespace OpenVic { return_t add_religion(const std::string_view identifier, colour_t colour, ReligionGroup const* group, Religion::icon_t icon, bool pagan); void lock_religions(); Religion const* get_religion_by_identifier(const std::string_view identifier) const; + + return_t load_religion_file(ast::NodeCPtr root); }; } diff --git a/src/openvic/types/Date.cpp b/src/openvic/types/Date.cpp index b6e1367..1f13808 100644 --- a/src/openvic/types/Date.cpp +++ b/src/openvic/types/Date.cpp @@ -6,6 +6,7 @@ #include <charconv> #include "openvic/utility/Logger.hpp" +#include "openvic/utility/StringUtils.hpp" using namespace OpenVic; @@ -55,12 +56,16 @@ Timespan::operator double() const { return days; } -Timespan::operator std::string() const { +std::string Timespan::to_string() const { return std::to_string(days); } +Timespan::operator std::string() const { + return to_string(); +} + std::ostream& OpenVic::operator<<(std::ostream& out, Timespan const& timespan) { - return out << static_cast<std::string>(timespan); + return out << timespan.to_string(); } Timespan Date::_dateToTimespan(year_t year, month_t month, day_t day) { @@ -146,95 +151,112 @@ Date Date::operator++(int) { return old; } -Date::operator std::string() const { +std::string Date::to_string() const { std::stringstream ss; ss << *this; return ss.str(); } +Date::operator std::string() const { + return to_string(); +} + std::ostream& OpenVic::operator<<(std::ostream& out, Date const& date) { - return out << static_cast<int>(date.getYear()) << '.' << static_cast<int>(date.getMonth()) << '.' << static_cast<int>(date.getDay()); + return out << static_cast<int>(date.getYear()) << Date::SEPARATOR_CHARACTER << static_cast<int>(date.getMonth()) << Date::SEPARATOR_CHARACTER << static_cast<int>(date.getDay()); } // Parsed from string of the form YYYY.MM.DD -Date Date::from_string(const std::string_view date, bool* successful) { +Date Date::from_string(char const* const str, char const* const end, bool* successful) { if (successful != nullptr) *successful = true; - size_t first_pos = 0; - while (first_pos < date.length() && std::isdigit(date[first_pos])) { - first_pos++; + + year_t year = 0; + month_t month = 1; + day_t day = 1; + + if (str == nullptr || end <= str) { + Logger::error("Invalid string start/end pointers: ", static_cast<void const*>(str), " - ", static_cast<void const*>(end)); + if (successful != nullptr) *successful = false; + return { year, month, day }; } - if (first_pos == 0) { - Logger::error("Failed to find year digits in date: ", date); + char const* year_end = str; + while (std::isdigit(*year_end) && ++year_end < end); + + if (year_end <= str) { + Logger::error("Failed to find year digits in date: ", std::string_view { str, static_cast<size_t>(end - str) }); if (successful != nullptr) *successful = false; - return {}; + return { year, month, day }; } - int val = 0; - char const* start = date.data(); - char const* end = start + first_pos; - std::from_chars_result result = std::from_chars(start, end, val); - if (result.ec != std::errc{} || result.ptr != end || val < 0 || val >= 1 << (8 * sizeof(year_t))) { - Logger::error("Failed to read year: ", date); + bool sub_successful = false; + uint64_t val = StringUtils::string_to_uint64(str, year_end, &sub_successful, 10); + if (!sub_successful || val >= 1 << (8 * sizeof(year_t))) { + Logger::error("Failed to read year: ", std::string_view { str, static_cast<size_t>(end - str) }); if (successful != nullptr) *successful = false; - return {}; + return { year, month, day }; } - year_t year = val; - month_t month = 1; - day_t day = 1; - if (first_pos < date.length()) { - if (date[first_pos] == '.') { - size_t second_pos = ++first_pos; - while (second_pos < date.length() && std::isdigit(date[second_pos])) { - second_pos++; + year = val; + if (year_end < end) { + if (*year_end == SEPARATOR_CHARACTER) { + char const* const month_start = year_end + 1; + char const* month_end = month_start; + if (month_start < end) { + while (std::isdigit(*month_end) && ++month_end < end); } - if (first_pos == second_pos) { - Logger::error("Failed to find month digits in date: ", date); + if (month_start >= month_end) { + Logger::error("Failed to find month digits in date: ", std::string_view { str, static_cast<size_t>(end - str) }); if (successful != nullptr) *successful = false; } else { - start = date.data() + first_pos; - end = date.data() + second_pos; - result = std::from_chars(start, end, val); - if (result.ec != std::errc{} || result.ptr != end || val < 1 || val > MONTHS_IN_YEAR) { - Logger::error("Failed to read month: ", date); + sub_successful = false; + val = StringUtils::string_to_uint64(month_start, month_end, &sub_successful, 10); + if (!sub_successful || val < 1 || val > MONTHS_IN_YEAR) { + Logger::error("Failed to read month: ", std::string_view { str, static_cast<size_t>(end - str) }); if (successful != nullptr) *successful = false; } else { month = val; - if (second_pos < date.length()) { - if (date[second_pos] == '.') { - size_t third_pos = ++second_pos; - while (third_pos < date.length() && std::isdigit(date[third_pos])) { - third_pos++; + if (month_end < end) { + if (*month_end == SEPARATOR_CHARACTER) { + char const* const day_start = month_end + 1; + char const* day_end = day_start; + if (day_start < end) { + while (std::isdigit(*day_end) && ++day_end < end); } - if (second_pos == third_pos) { - Logger::error("Failed to find day digits in date: ", date); + if (day_start >= day_end) { + Logger::error("Failed to find day digits in date: ", std::string_view { str, static_cast<size_t>(end - str) }); if (successful != nullptr) *successful = false; } else { - start = date.data() + second_pos; - end = date.data() + third_pos; - result = std::from_chars(start, end, val); - if (result.ec != std::errc{} || result.ptr != end || val < 1 || val > DAYS_IN_MONTH[month - 1]) { - Logger::error("Failed to read day: ", date); + sub_successful = false; + val = StringUtils::string_to_uint64(day_start, day_end, &sub_successful); + if (!sub_successful || val < 1 || val > DAYS_IN_MONTH[month - 1]) { + Logger::error("Failed to read day: ", std::string_view { str, static_cast<size_t>(end - str) }); if (successful != nullptr) *successful = false; } else { day = val; - if (third_pos < date.length()) { - Logger::error("Unexpected string \"", date.substr(third_pos), "\" at the end of date ", date); + if (day_end < end) { + Logger::error("Unexpected string \"", std::string_view { day_end, static_cast<size_t>(end - day_end) }, "\" at the end of date ", std::string_view { str, static_cast<size_t>(end - str) }); if (successful != nullptr) *successful = false; } } } } else { - Logger::error("Unexpected character \"", date[second_pos], "\" in month of date ", date); + Logger::error("Unexpected character \"", *month_end, "\" in month of date ", std::string_view { str, static_cast<size_t>(end - str) }); if (successful != nullptr) *successful = false; } } } } } else { - Logger::error("Unexpected character \"", date[first_pos], "\" in year of date ", date); + Logger::error("Unexpected character \"", *year_end, "\" in year of date ", std::string_view { str, static_cast<size_t>(end - str) }); if (successful != nullptr) *successful = false; } } - return _dateToTimespan(year, month, day); + return { year, month, day }; }; + +Date Date::from_string(char const* str, size_t length, bool* successful) { + return from_string(str, str + length, successful); +} + +Date Date::from_string(const std::string_view str, bool* successful) { + return from_string(str.data(), str.length(), successful); +} diff --git a/src/openvic/types/Date.hpp b/src/openvic/types/Date.hpp index ca5645c..601f9dc 100644 --- a/src/openvic/types/Date.hpp +++ b/src/openvic/types/Date.hpp @@ -33,6 +33,7 @@ namespace OpenVic { explicit operator day_t() const; explicit operator double() const; + std::string to_string() const; explicit operator std::string() const; }; std::ostream& operator<<(std::ostream& out, Timespan const& timespan); @@ -50,6 +51,8 @@ namespace OpenVic { static Timespan::day_t const* DAYS_UP_TO_MONTH; static month_t const* MONTH_FROM_DAY_IN_YEAR; + static constexpr char SEPARATOR_CHARACTER = '.'; + private: // Number of days since Jan 1st, Year 0 Timespan timespan; @@ -82,9 +85,12 @@ namespace OpenVic { Date& operator++(); Date operator++(int); + std::string to_string() const; explicit operator std::string() const; // Parsed from string of the form YYYY.MM.DD - static Date from_string(const std::string_view date, bool* successful = nullptr); + static Date from_string(char const* str, char const* end, bool* successful = nullptr); + static Date from_string(char const* str, size_t length, bool* successful = nullptr); + static Date from_string(const std::string_view str, bool* successful = nullptr); }; std::ostream& operator<<(std::ostream& out, Date const& date); } diff --git a/src/openvic/types/IdentifierRegistry.cpp b/src/openvic/types/IdentifierRegistry.cpp index 97a1ff5..3710387 100644 --- a/src/openvic/types/IdentifierRegistry.cpp +++ b/src/openvic/types/IdentifierRegistry.cpp @@ -35,3 +35,11 @@ HasIdentifierAndColour::HasIdentifierAndColour(const std::string_view new_identi const colour_t new_colour, bool can_be_null) : HasIdentifier { new_identifier }, HasColour { new_colour, can_be_null } {} + +distribution_t::value_type OpenVic::get_largest_item(distribution_t const& dist) { + const distribution_t::const_iterator result = std::max_element(dist.begin(), dist.end(), + [](distribution_t::value_type a, distribution_t::value_type b) -> bool { + return a.second < b.second; + }); + return result != dist.end() ? *result : distribution_t::value_type { nullptr, -1.0f }; +} diff --git a/src/openvic/types/IdentifierRegistry.hpp b/src/openvic/types/IdentifierRegistry.hpp index e5ac94b..989db01 100644 --- a/src/openvic/types/IdentifierRegistry.hpp +++ b/src/openvic/types/IdentifierRegistry.hpp @@ -64,7 +64,9 @@ namespace OpenVic { HasIdentifierAndColour& operator=(HasIdentifierAndColour&&) = delete; }; - using distribution_t = std::unordered_map<HasIdentifierAndColour const*, float>; + using distribution_t = std::map<HasIdentifierAndColour const*, float>; + + distribution_t::value_type get_largest_item(distribution_t const& dist); /* * Template for a list of objects with unique string identifiers that can @@ -102,7 +104,7 @@ namespace OpenVic { Logger::error("Failed to lock ", name, " registry - already locked!"); } else { locked = true; - if (log) Logger::info("Locked ", name, " registry after registering ", get_item_count(), " items"); + if (log) Logger::info("Locked ", name, " registry after registering ", size(), " items"); } } bool is_locked() const { @@ -113,9 +115,19 @@ namespace OpenVic { items.clear(); locked = false; } - size_t get_item_count() const { + size_t size() const { return items.size(); } + bool empty() const { + return items.empty(); + } + void reserve(size_t size) { + if (locked) { + Logger::error("Failed to reserve space for ", size, " items in ", name, " registry - already locked!"); + } else { + items.reserve(size); + } + } T* get_item_by_identifier(const std::string_view identifier) { const identifier_index_map_t::const_iterator it = identifier_index_map.find(identifier); if (it != identifier_index_map.end()) return &items[it->second]; diff --git a/src/openvic/types/fixed_point/FP.hpp b/src/openvic/types/fixed_point/FP.hpp index 8b32906..42ddf79 100644 --- a/src/openvic/types/fixed_point/FP.hpp +++ b/src/openvic/types/fixed_point/FP.hpp @@ -1,16 +1,17 @@ #pragma once -#include <cstdint> -#include <cstdlib> #include <cerrno> #include <cmath> +#include <cstdint> +#include <cstdlib> #include <limits> -#include <cstring> +#include <string_view> -#include "FPLUT.hpp" -#include "openvic/utility/FloatUtils.hpp" -#include "openvic/utility/StringUtils.hpp" #include "openvic/utility/Logger.hpp" +#include "openvic/utility/NumberUtils.hpp" +#include "openvic/utility/StringUtils.hpp" + +#include "FPLUT.hpp" namespace OpenVic { struct FP { @@ -41,7 +42,7 @@ namespace OpenVic { static constexpr FP _0() { return 0; - } + } static constexpr FP _1() { return 1; @@ -195,13 +196,17 @@ namespace OpenVic { return static_cast<int64_t>(178145LL); } + constexpr bool is_negative() const { + return value < 0; + } + constexpr FP abs() const { - return value >= 0 ? value : -value; + return !is_negative() ? value : -value; } - // Not including sign, so -1.1 -> 0.1 + // Doesn't account for sign, so -n.abc -> 1 - 0.abc constexpr FP get_frac() const { - return abs().value & ((1 << FPLUT::PRECISION) - 1); + return value & (FPLUT::ONE - 1); } constexpr int64_t to_int64_t() const { @@ -217,33 +222,35 @@ namespace OpenVic { } constexpr int32_t to_int32_t() const { - return static_cast<int32_t>(value >> FPLUT::PRECISION); + return static_cast<int32_t>(to_int64_t()); } constexpr float to_float() const { - return value / 65536.0F; + return value / static_cast<float>(FPLUT::ONE); } constexpr float to_float_rounded() const { - return static_cast<float>(FloatUtils::round_to_int64((value / 65536.0F) * 100000.0F)) / 100000.0F; + return static_cast<float>(NumberUtils::round_to_int64((value / static_cast<float>(FPLUT::ONE)) * 100000.0f)) / 100000.0f; } - constexpr float to_double() const { - return value / 65536.0; + constexpr double to_double() const { + return value / static_cast<double>(FPLUT::ONE); } constexpr float to_double_rounded() const { - return FloatUtils::round_to_int64((value / 65536.0) * 100000.0) / 100000.0; + return NumberUtils::round_to_int64((value / static_cast<double>(FPLUT::ONE)) * 100000.0) / 100000.0; } std::string to_string() const { - std::string str = std::to_string(to_int64_t()) + "."; - FP frac_part = get_frac(); + FP val = abs(); + std::string str = std::to_string(val.to_int64_t()) + "."; + if (is_negative()) str = "-" + str; + val = val.get_frac(); do { - frac_part.value *= 10; - str += std::to_string(frac_part.to_int64_t()); - frac_part = frac_part.get_frac(); - } while (frac_part > 0); + val *= 10; + str.push_back('0' + static_cast<char>(val.to_int64_t())); + val = val.get_frac(); + } while (val > 0); return str; } @@ -258,7 +265,7 @@ namespace OpenVic { } // Deterministic - static constexpr FP parse(const char* str, const char* end, bool *successful = nullptr) { + static constexpr FP parse(char const* str, char const* const end, bool* successful = nullptr) { if (successful != nullptr) *successful = false; if (str == nullptr || str >= end) { @@ -273,7 +280,7 @@ namespace OpenVic { if (str == end) return _0(); } - const char* dot_pointer = str; + char const* dot_pointer = str; while (*dot_pointer != '.' && ++dot_pointer != end); if (dot_pointer == str && dot_pointer + 1 == end) { @@ -287,31 +294,35 @@ namespace OpenVic { if (dot_pointer != str) { // Non-empty integer part bool int_successful = false; - result += parse_integer(str, dot_pointer, &int_successful); + result += parse_integer(str, dot_pointer, &int_successful); if (!int_successful && successful != nullptr) *successful = false; } if (dot_pointer + 1 < end) { // Non-empty fractional part bool frac_successful = false; - result += parse_fraction(dot_pointer + 1, end, &frac_successful); + result += parse_fraction(dot_pointer + 1, end, &frac_successful); if (!frac_successful && successful != nullptr) *successful = false; } return negative ? -result : result; } - static constexpr FP parse(const char* str, size_t length, bool *successful = nullptr) { + static constexpr FP parse(char const* str, size_t length, bool* successful = nullptr) { return parse(str, str + length, successful); } + static FP parse(const std::string_view str, bool* successful = nullptr) { + return parse(str.data(), str.length(), successful); + } + // Not Deterministic static constexpr FP parse_unsafe(float value) { return static_cast<int64_t>(value * FPLUT::ONE + 0.5f * (value < 0 ? -1 : 1)); } // Not Deterministic - static FP parse_unsafe(const char* value) { + static FP parse_unsafe(char const* value) { char* endpointer; double double_value = std::strtod(value, &endpointer); @@ -325,7 +336,7 @@ namespace OpenVic { } constexpr operator int32_t() const { - return to_int32_t(); + return to_int32_t(); } constexpr operator int64_t() const { @@ -344,218 +355,225 @@ namespace OpenVic { return to_string(); } - friend std::ostream& operator<<(std::ostream& stream, const FP& obj) { + friend std::ostream& operator<<(std::ostream& stream, FP const& obj) { return stream << obj.to_string(); } - constexpr friend FP operator-(const FP& obj) { + constexpr friend FP operator-(FP const& obj) { return -obj.value; } - constexpr friend FP operator+(const FP& obj) { + constexpr friend FP operator+(FP const& obj) { return +obj.value; } - constexpr friend FP operator+(const FP& lhs, const FP& rhs) { + constexpr friend FP operator+(FP const& lhs, FP const& rhs) { return lhs.value + rhs.value; } - constexpr friend FP operator+(const FP& lhs, const int32_t& rhs) { + constexpr friend FP operator+(FP const& lhs, int32_t const& rhs) { return lhs.value + (static_cast<int64_t>(rhs) << FPLUT::PRECISION); } - constexpr friend FP operator+(const int32_t& lhs, const FP& rhs) { + constexpr friend FP operator+(int32_t const& lhs, FP const& rhs) { return (static_cast<int64_t>(lhs) << FPLUT::PRECISION) + rhs.value; } - constexpr FP operator+=(const FP& obj) { + constexpr FP operator+=(FP const& obj) { value += obj.value; return *this; } - constexpr FP operator+=(const int32_t& obj) { + constexpr FP operator+=(int32_t const& obj) { value += (static_cast<int64_t>(obj) << FPLUT::PRECISION); return *this; } - constexpr friend FP operator-(const FP& lhs, const FP& rhs) { + constexpr friend FP operator-(FP const& lhs, FP const& rhs) { return lhs.value - rhs.value; } - constexpr friend FP operator-(const FP& lhs, const int32_t& rhs) { + constexpr friend FP operator-(FP const& lhs, int32_t const& rhs) { return lhs.value - (static_cast<int64_t>(rhs) << FPLUT::PRECISION); } - constexpr friend FP operator-(const int32_t& lhs, const FP& rhs) { + constexpr friend FP operator-(int32_t const& lhs, FP const& rhs) { return (static_cast<int64_t>(lhs) << FPLUT::PRECISION) - rhs.value; } - constexpr FP operator-=(const FP& obj) { + constexpr FP operator-=(FP const& obj) { value -= obj.value; return *this; } - constexpr FP operator-=(const int32_t& obj) { + constexpr FP operator-=(int32_t const& obj) { value -= (static_cast<int64_t>(obj) << FPLUT::PRECISION); return *this; } - constexpr friend FP operator*(const FP& lhs, const FP& rhs) { + constexpr friend FP operator*(FP const& lhs, FP const& rhs) { return lhs.value * rhs.value >> FPLUT::PRECISION; } - constexpr friend FP operator*(const FP& lhs, const int32_t& rhs) { + constexpr friend FP operator*(FP const& lhs, int32_t const& rhs) { return lhs.value * rhs; } - constexpr friend FP operator*(const int32_t& lhs, const FP& rhs) { + constexpr friend FP operator*(int32_t const& lhs, FP const& rhs) { return lhs * rhs.value; } - constexpr FP operator*=(const FP& obj) { + constexpr FP operator*=(FP const& obj) { value *= obj.value >> FPLUT::PRECISION; return *this; } - constexpr FP operator*=(const int32_t& obj) { + constexpr FP operator*=(int32_t const& obj) { value *= obj; return *this; } - constexpr friend FP operator/(const FP& lhs, const FP& rhs) { - return (lhs.value << FPLUT::PRECISION) / rhs.value; + constexpr friend FP operator/(FP const& lhs, FP const& rhs) { + return (lhs.value << FPLUT::PRECISION) / rhs.value; } - constexpr friend FP operator/(const FP& lhs, const int32_t& rhs) { + constexpr friend FP operator/(FP const& lhs, int32_t const& rhs) { return lhs.value / rhs; } - constexpr friend FP operator/(const int32_t& lhs, const FP& rhs) { - return (static_cast<int64_t>(lhs) << (2 / FPLUT::PRECISION)) / rhs.value; + constexpr friend FP operator/(int32_t const& lhs, FP const& rhs) { + return (static_cast<int64_t>(lhs) << (2 * FPLUT::PRECISION)) / rhs.value; } - constexpr FP operator/=(const FP& obj) { + constexpr FP operator/=(FP const& obj) { value = (value << FPLUT::PRECISION) / obj.value; return *this; } - constexpr FP operator/=(const int32_t& obj) { + constexpr FP operator/=(int32_t const& obj) { value /= obj; return *this; } - constexpr friend FP operator%(const FP& lhs, const FP& rhs) { + constexpr friend FP operator%(FP const& lhs, FP const& rhs) { return lhs.value % rhs.value; } - constexpr friend FP operator%(const FP& lhs, const int32_t& rhs) { + constexpr friend FP operator%(FP const& lhs, int32_t const& rhs) { return lhs.value % (static_cast<int64_t>(rhs) << FPLUT::PRECISION); } - constexpr friend FP operator%(const int32_t& lhs, const FP& rhs) { + constexpr friend FP operator%(int32_t const& lhs, FP const& rhs) { return (static_cast<int64_t>(lhs) << FPLUT::PRECISION) % rhs.value; } - constexpr FP operator%=(const FP& obj) { + constexpr FP operator%=(FP const& obj) { value %= obj.value; return *this; } - constexpr FP operator%=(const int32_t& obj) { + constexpr FP operator%=(int32_t const& obj) { value %= (static_cast<int64_t>(obj) << FPLUT::PRECISION); return *this; } - constexpr friend bool operator<(const FP& lhs, const FP& rhs) { + constexpr friend bool operator<(FP const& lhs, FP const& rhs) { return lhs.value < rhs.value; } - constexpr friend bool operator<(const FP& lhs, const int32_t& rhs) { + constexpr friend bool operator<(FP const& lhs, int32_t const& rhs) { return lhs.value < static_cast<int64_t>(rhs) << FPLUT::PRECISION; } - constexpr friend bool operator<(const int32_t& lhs, const FP& rhs) { + constexpr friend bool operator<(int32_t const& lhs, FP const& rhs) { return static_cast<int64_t>(lhs) << FPLUT::PRECISION < rhs.value; } - constexpr friend bool operator<=(const FP& lhs, const FP& rhs) { + constexpr friend bool operator<=(FP const& lhs, FP const& rhs) { return lhs.value <= rhs.value; } - constexpr friend bool operator<=(const FP& lhs, const int32_t& rhs) { + constexpr friend bool operator<=(FP const& lhs, int32_t const& rhs) { return lhs.value <= static_cast<int64_t>(rhs) << FPLUT::PRECISION; } - constexpr friend bool operator<=(const int32_t& lhs, const FP& rhs) { + constexpr friend bool operator<=(int32_t const& lhs, FP const& rhs) { return static_cast<int64_t>(lhs) << FPLUT::PRECISION <= rhs.value; } - constexpr friend bool operator>(const FP& lhs, const FP& rhs) { + constexpr friend bool operator>(FP const& lhs, FP const& rhs) { return lhs.value > rhs.value; } - constexpr friend bool operator>(const FP& lhs, const int32_t& rhs) { + constexpr friend bool operator>(FP const& lhs, int32_t const& rhs) { return lhs.value > static_cast<int64_t>(rhs) << FPLUT::PRECISION; } - constexpr friend bool operator>(const int32_t& lhs, const FP& rhs) { + constexpr friend bool operator>(int32_t const& lhs, FP const& rhs) { return static_cast<int64_t>(lhs) << FPLUT::PRECISION > rhs.value; } - constexpr friend bool operator>=(const FP& lhs, const FP& rhs) { + constexpr friend bool operator>=(FP const& lhs, FP const& rhs) { return lhs.value >= rhs.value; } - constexpr friend bool operator>=(const FP& lhs, const int32_t& rhs) { + constexpr friend bool operator>=(FP const& lhs, int32_t const& rhs) { return lhs.value >= static_cast<int64_t>(rhs) << FPLUT::PRECISION; } - constexpr friend bool operator>=(const int32_t& lhs, const FP& rhs) { + constexpr friend bool operator>=(int32_t const& lhs, FP const& rhs) { return static_cast<int64_t>(lhs) << FPLUT::PRECISION >= rhs.value; } - constexpr friend bool operator==(const FP& lhs, const FP& rhs) { + constexpr friend bool operator==(FP const& lhs, FP const& rhs) { return lhs.value == rhs.value; } - constexpr friend bool operator==(const FP& lhs, const int32_t& rhs) { + constexpr friend bool operator==(FP const& lhs, int32_t const& rhs) { return lhs.value == static_cast<int64_t>(rhs) << FPLUT::PRECISION; } - constexpr friend bool operator==(const int32_t& lhs, const FP& rhs) { + constexpr friend bool operator==(int32_t const& lhs, FP const& rhs) { return static_cast<int64_t>(lhs) << FPLUT::PRECISION == rhs.value; } - constexpr friend bool operator!=(const FP& lhs, const FP& rhs) { + constexpr friend bool operator!=(FP const& lhs, FP const& rhs) { return lhs.value != rhs.value; } - constexpr friend bool operator!=(const FP& lhs, const int32_t& rhs) { + constexpr friend bool operator!=(FP const& lhs, int32_t const& rhs) { return lhs.value != static_cast<int64_t>(rhs) << FPLUT::PRECISION; } - constexpr friend bool operator!=(const int32_t& lhs, const FP& rhs) { + constexpr friend bool operator!=(int32_t const& lhs, FP const& rhs) { return static_cast<int64_t>(lhs) << FPLUT::PRECISION != rhs.value; } - private: int64_t value; - static constexpr FP parse_integer(const char* str, const char* end, bool* successful) { + static constexpr FP parse_integer(char const* str, char const* const end, bool* successful) { int64_t parsed_value = StringUtils::string_to_int64(str, end, successful, 10); return parse(parsed_value); } - static constexpr FP parse_fraction(const char* str, const char* end, bool* successful) { - constexpr size_t READ_SIZE = 5; - if (str + READ_SIZE < end) { - Logger::error("Fixed point fraction parse will only use the first ", READ_SIZE, - " characters of ", std::string_view { str, static_cast<size_t>(end - str) }); - end = str + READ_SIZE; + static constexpr FP parse_fraction(char const* str, char const* end, bool* successful) { + char const* const read_end = str + FPLUT::PRECISION; + if (read_end < end) end = read_end; + uint64_t parsed_value = StringUtils::string_to_uint64(str, end, successful, 10); + while (end++ < read_end) { + parsed_value *= 10; } - int64_t parsed_value = StringUtils::string_to_int64(str, end, successful, 10); - return parse_raw(parsed_value * 65536 / 100000); + uint64_t decimal = NumberUtils::pow(static_cast<uint64_t>(10), FPLUT::PRECISION); + int64_t ret = 0; + for (int i = FPLUT::PRECISION - 1; i >= 0; --i) { + decimal >>= 1; + if (parsed_value > decimal) { + parsed_value -= decimal; + ret |= 1 << i; + } + } + return ret; } }; diff --git a/src/openvic/utility/FloatUtils.hpp b/src/openvic/utility/FloatUtils.hpp deleted file mode 100644 index 4fc83fd..0000000 --- a/src/openvic/utility/FloatUtils.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#include <cstdint> - -namespace OpenVic::FloatUtils { - constexpr int round_to_int(double num) { - return (num > 0.0) ? (num + 0.5) : (num - 0.5); - } - - constexpr int64_t round_to_int64(double num) { - return (num > 0.0) ? (num + 0.5) : (num - 0.5); - } -} diff --git a/src/openvic/utility/NumberUtils.hpp b/src/openvic/utility/NumberUtils.hpp new file mode 100644 index 0000000..6211772 --- /dev/null +++ b/src/openvic/utility/NumberUtils.hpp @@ -0,0 +1,27 @@ +#include <cstdint> + +namespace OpenVic::NumberUtils { + constexpr int64_t round_to_int64(float num) { + return (num > 0.0f) ? (num + 0.5f) : (num - 0.5f); + } + + constexpr int64_t round_to_int64(double num) { + return (num > 0.0) ? (num + 0.5) : (num - 0.5); + } + + constexpr uint64_t pow(uint64_t base, size_t exponent) { + uint64_t ret = 1; + while (exponent-- > 0) { + ret *= base; + } + return ret; + } + + constexpr int64_t pow(int64_t base, size_t exponent) { + int64_t ret = 1; + while (exponent-- > 0) { + ret *= base; + } + return ret; + } +} diff --git a/src/openvic/utility/StringUtils.hpp b/src/openvic/utility/StringUtils.hpp index 72f4038..5d6001c 100644 --- a/src/openvic/utility/StringUtils.hpp +++ b/src/openvic/utility/StringUtils.hpp @@ -1,16 +1,17 @@ #include <cstdint> #include <limits> +#include <string_view> namespace OpenVic::StringUtils { - /* The constexpr function 'string_to_int64' will convert a string into a int64 integer value. - * The function takes four parameters: the input string (as a pair of pointers marking the start and - * end of the string), a bool pointer for reporting success, and the base for numerical conversion. - * The base parameter defaults to 10 (decimal), but it can be any value between 2 and 36. If the base - * given is 0, it will be set to 16 if the string starts with "0x" or "0X", otherwise 8 if the string - * still starts with "0", otherwise 10. The success bool pointer parameter is used to report whether - * or not conversion was successful. It can be nullptr if this information is not needed. - */ - constexpr int64_t string_to_int64(char const* str, const char* end, bool* successful, int base = 10) { + /* The constexpr function 'string_to_uint64' will convert a string into a uint64_t integer value. + * The function takes four parameters: the input string (as a pair of pointers marking the start and + * end of the string), a bool pointer for reporting success, and the base for numerical conversion. + * The base parameter defaults to 10 (decimal), but it can be any value between 2 and 36. If the base + * given is 0, it will be set to 16 if the string starts with "0x" or "0X", otherwise 8 if the string + * still starts with "0", otherwise 10. The success bool pointer parameter is used to report whether + * or not conversion was successful. It can be nullptr if this information is not needed. + */ + constexpr uint64_t string_to_uint64(char const* str, const char* const end, bool* successful = nullptr, int base = 10) { if (successful != nullptr) *successful = false; // Base value should be between 2 and 36. If it's not, return 0 as an invalid case. @@ -18,24 +19,14 @@ namespace OpenVic::StringUtils { return 0; // The result of the conversion will be stored in this variable. - int64_t result = 0; - // This flag will be set if the number is negative. - bool is_negative = false; - - // Check if there is a sign character. - if (*str == '+' || *str == '-') { - if (*str == '-') - is_negative = true; - ++str; - if (str == end) return 0; - } + uint64_t result = 0; // If base is zero, base is determined by the string prefix. if (base == 0) { if (*str == '0') { if (str + 1 != end && (str[1] == 'x' || str[1] == 'X')) { base = 16; // Hexadecimal. - str += 2; // Skip '0x' or '0X' + str += 2; // Skip '0x' or '0X' if (str == end) return 0; } else { base = 8; // Octal. @@ -69,15 +60,15 @@ namespace OpenVic::StringUtils { } // Check for overflow on multiplication - if (result > std::numeric_limits<int64_t>::max() / base) { - return is_negative ? std::numeric_limits<int64_t>::min() : std::numeric_limits<int64_t>::max(); + if (result > std::numeric_limits<uint64_t>::max() / base) { + return std::numeric_limits<uint64_t>::max(); } result *= base; // Check for overflow on addition - if (result > std::numeric_limits<int64_t>::max() - digit) { - return is_negative ? std::numeric_limits<int64_t>::min() : std::numeric_limits<int64_t>::max(); + if (result > std::numeric_limits<uint64_t>::max() - digit) { + return std::numeric_limits<uint64_t>::max(); } result += digit; @@ -87,7 +78,50 @@ namespace OpenVic::StringUtils { // set *successful to true (if not it is already false). if (successful != nullptr && str == end) *successful = true; - // Return the result. If the number was negative, the result is negated. - return is_negative ? -result : result; + return result; + } + + constexpr uint64_t string_to_uint64(char const* str, size_t length, bool* successful = nullptr, int base = 10) { + return string_to_uint64(str, str + length, successful, base); + } + + inline uint64_t string_to_uint64(const std::string_view str, bool* successful = nullptr, int base = 10) { + return string_to_uint64(str.data(), str.length(), successful, base); + } + + constexpr int64_t string_to_int64(char const* str, const char* const end, bool* successful = nullptr, int base = 10) { + if (successful != nullptr) *successful = false; + + if (str == nullptr || end <= str) return 0; + + // This flag will be set if the number is negative. + bool is_negative = false; + + // Check if there is a sign character. + if (*str == '+' || *str == '-') { + if (*str == '-') + is_negative = true; + ++str; + if (str == end) return 0; + } + + const uint64_t result = string_to_uint64(str, end, successful, base); + if (!is_negative) { + if (result >= std::numeric_limits<int64_t>::max()) + return std::numeric_limits<int64_t>::max(); + return result; + } else { + if (result > std::numeric_limits<int64_t>::max()) + return std::numeric_limits<int64_t>::min(); + return -result; + } + } + + constexpr int64_t string_to_int64(char const* str, size_t length, bool* successful = nullptr, int base = 10) { + return string_to_int64(str, str + length, successful, base); + } + + inline int64_t string_to_int64(const std::string_view str, bool* successful = nullptr, int base = 10) { + return string_to_int64(str.data(), str.length(), successful, base); } } |