aboutsummaryrefslogtreecommitdiff
path: root/src/openvic-simulation
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvic-simulation')
-rw-r--r--src/openvic-simulation/country/Country.cpp19
-rw-r--r--src/openvic-simulation/country/Country.hpp4
-rw-r--r--src/openvic-simulation/dataloader/Dataloader.cpp339
-rw-r--r--src/openvic-simulation/dataloader/Dataloader.hpp26
-rw-r--r--src/openvic-simulation/map/Map.cpp8
-rw-r--r--src/openvic-simulation/map/Province.cpp26
-rw-r--r--src/openvic-simulation/map/Province.hpp9
-rw-r--r--src/openvic-simulation/military/Deployment.cpp16
-rw-r--r--src/openvic-simulation/military/LeaderTrait.hpp2
-rw-r--r--src/openvic-simulation/pop/Culture.cpp2
-rw-r--r--src/openvic-simulation/types/IdentifierRegistry.hpp42
-rw-r--r--src/openvic-simulation/utility/Logger.cpp34
-rw-r--r--src/openvic-simulation/utility/Logger.hpp21
-rw-r--r--src/openvic-simulation/utility/StringUtils.hpp68
14 files changed, 376 insertions, 240 deletions
diff --git a/src/openvic-simulation/country/Country.cpp b/src/openvic-simulation/country/Country.cpp
index 9e244c1..6ad13ee 100644
--- a/src/openvic-simulation/country/Country.cpp
+++ b/src/openvic-simulation/country/Country.cpp
@@ -78,6 +78,12 @@ bool CountryManager::add_country(
Logger::error("Invalid country identifier - empty!");
return false;
}
+ if (!valid_basic_identifier(identifier)) {
+ Logger::error(
+ "Invalid country identifier: ", identifier, " (can only contain alphanumeric characters and underscores)"
+ );
+ return false;
+ }
if (colour > MAX_COLOUR_RGB) {
Logger::error("Invalid country colour for ", identifier, ": ", colour_to_hex_string(colour));
return false;
@@ -93,14 +99,13 @@ bool CountryManager::add_country(
});
}
-bool CountryManager::load_countries(
- GameManager const& game_manager, Dataloader const& dataloader, fs::path const& countries_dir, ast::NodeCPtr root
-) {
+bool CountryManager::load_countries(GameManager const& game_manager, Dataloader const& dataloader, ast::NodeCPtr root) {
+ static constexpr std::string_view common_dir = "common/";
bool is_dynamic = false;
const bool ret = expect_dictionary_reserve_length(
countries,
- [this, &game_manager, &is_dynamic, &dataloader, &countries_dir](std::string_view key, ast::NodeCPtr value) -> bool {
+ [this, &game_manager, &is_dynamic, &dataloader](std::string_view key, ast::NodeCPtr value) -> bool {
if (key == "dynamic_tags") {
return expect_bool([&is_dynamic](bool val) -> bool {
if (val == is_dynamic) {
@@ -115,10 +120,12 @@ bool CountryManager::load_countries(
})(value);
}
if (expect_string(
- [this, &game_manager, is_dynamic, &dataloader, &countries_dir, &key](std::string_view filepath) -> bool {
+ [this, &game_manager, is_dynamic, &dataloader, &key](std::string_view filepath) -> bool {
if (load_country_data_file(
game_manager, key, is_dynamic,
- Dataloader::parse_defines(dataloader.lookup_file_case_insensitive(countries_dir / filepath)).get_file_node()
+ Dataloader::parse_defines(
+ dataloader.lookup_file(StringUtils::append_string_views(common_dir, filepath))
+ ).get_file_node()
)) {
return true;
}
diff --git a/src/openvic-simulation/country/Country.hpp b/src/openvic-simulation/country/Country.hpp
index 7754a0b..50cca04 100644
--- a/src/openvic-simulation/country/Country.hpp
+++ b/src/openvic-simulation/country/Country.hpp
@@ -103,9 +103,7 @@ namespace OpenVic {
);
IDENTIFIER_REGISTRY_ACCESSORS_CUSTOM_PLURAL(country, countries)
- bool load_countries(
- GameManager const& game_manager, Dataloader const& dataloader, fs::path const& countries_dir, ast::NodeCPtr root
- );
+ bool load_countries(GameManager const& game_manager, Dataloader const& dataloader, ast::NodeCPtr root);
bool load_country_data_file(
GameManager const& game_manager, std::string_view name, bool is_dynamic, ast::NodeCPtr root
);
diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp
index e98e63b..0174eb3 100644
--- a/src/openvic-simulation/dataloader/Dataloader.cpp
+++ b/src/openvic-simulation/dataloader/Dataloader.cpp
@@ -34,6 +34,10 @@ using namespace OpenVic;
using namespace OpenVic::NodeTools;
using namespace ovdl;
+using StringUtils::append_string_views;
+
+#define FILESYSTEM_CASE_INSENSITIVE (defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)))
+#define FILESYSTEM_NEEDS_FORWARD_SLASHES (!defined(_WIN32))
static constexpr bool path_equals_case_insensitive(std::string_view lhs, std::string_view rhs) {
constexpr auto ichar_equals = [](unsigned char l, unsigned char r) {
@@ -44,7 +48,7 @@ static constexpr bool path_equals_case_insensitive(std::string_view lhs, std::st
// Windows and Mac by default act like case insensitive filesystems
static constexpr bool path_equals(std::string_view lhs, std::string_view rhs) {
-#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__))
+#if FILESYSTEM_CASE_INSENSITIVE
return path_equals_case_insensitive(lhs, rhs);
#else
return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
@@ -74,13 +78,13 @@ static bool filename_equals(const is_filename auto& lhs, const is_filename auto&
static fs::path _search_for_game_path(fs::path hint_path = {}) {
// Apparently max amount of steam libraries is 8, if incorrect please correct it to the correct max amount
- constexpr int max_amount_of_steam_libraries = 8;
- constexpr std::string_view Victoria_2_folder = "Victoria 2";
- constexpr std::string_view v2_game_exe = "v2game.exe";
- constexpr std::string_view steamapps = "steamapps";
- constexpr std::string_view libraryfolders = "libraryfolders.vdf";
- constexpr std::string_view vic2_appmanifest = "appmanifest_42960.acf";
- constexpr std::string_view common_folder = "common";
+ static constexpr int max_amount_of_steam_libraries = 8;
+ static constexpr std::string_view Victoria_2_folder = "Victoria 2";
+ static constexpr std::string_view v2_game_exe = "v2game.exe";
+ static constexpr std::string_view steamapps = "steamapps";
+ static constexpr std::string_view libraryfolders = "libraryfolders.vdf";
+ static constexpr std::string_view vic2_appmanifest = "appmanifest_42960.acf";
+ static constexpr std::string_view common_folder = "common";
std::error_code error_code;
@@ -364,23 +368,33 @@ bool Dataloader::set_roots(path_vector_t const& new_roots) {
return ret;
}
-fs::path Dataloader::lookup_file(fs::path const& path, bool print_error) const {
+fs::path Dataloader::lookup_file(std::string_view path, bool print_error) const {
+#if FILESYSTEM_NEEDS_FORWARD_SLASHES
+ /* Back-slashes need to be converted into forward-slashes */
+ const std::string forward_slash_path {
+ StringUtils::make_forward_slash_path(StringUtils::remove_leading_slashes(path))
+ };
+ path = forward_slash_path;
+#endif
+
+ const fs::path filepath { path };
+
+#if FILESYSTEM_CASE_INSENSITIVE
+ /* Case-insensitive filesystem */
for (fs::path const& root : roots) {
- const fs::path composed = root / path;
+ const fs::path composed = root / filepath;
if (fs::is_regular_file(composed)) {
return composed;
}
}
- if (print_error) {
- Logger::error("Lookup for ", path, " failed!");
- }
- return {};
-}
-
-fs::path Dataloader::lookup_file_case_insensitive(fs::path const& path, bool print_error) const {
- const std::string filename = path.filename().string();
+#else
+ /* Case-sensitive filesystem */
+ const std::string_view filename = StringUtils::get_filename(path);
for (fs::path const& root : roots) {
- const fs::path composed = root / path;
+ const fs::path composed = root / filepath;
+ if (fs::is_regular_file(composed)) {
+ return composed;
+ }
std::error_code ec;
for (fs::directory_entry const& entry : fs::directory_iterator { composed.parent_path(), ec }) {
if (entry.is_regular_file()) {
@@ -391,32 +405,44 @@ fs::path Dataloader::lookup_file_case_insensitive(fs::path const& path, bool pri
}
}
}
+#endif
+
if (print_error) {
- Logger::error("Lookup for ", path, " failed!");
+ Logger::error("Lookup for \"", path, "\" failed!");
}
return {};
}
-static bool contains_file_with_name(Dataloader::path_vector_t const& paths, fs::path const& name) {
- for (fs::path const& path : paths) {
- if (path.filename() == name) {
- return true;
- }
- }
- return false;
-}
-
-Dataloader::path_vector_t Dataloader::lookup_files_in_dir(fs::path const& path, fs::path const& extension) const {
+template<typename _DirIterator, std::predicate<fs::path const&, fs::path const&> _Equiv>
+Dataloader::path_vector_t Dataloader::_lookup_files_in_dir(std::string_view path, fs::path const& extension) const {
+#if FILESYSTEM_NEEDS_FORWARD_SLASHES
+ /* Back-slashes need to be converted into forward-slashes */
+ const std::string forward_slash_path {
+ StringUtils::make_forward_slash_path(StringUtils::remove_leading_slashes(path))
+ };
+ path = forward_slash_path;
+#endif
+ const fs::path filepath { path };
+ static constexpr _Equiv Equiv {};
path_vector_t ret;
+ size_t start_of_current_root_entries;
for (fs::path const& root : roots) {
- const fs::path composed = root / path;
+ start_of_current_root_entries = ret.size();
+ const fs::path composed = root / filepath;
std::error_code ec;
- for (fs::directory_entry const& entry : fs::directory_iterator { composed, ec }) {
+ for (fs::directory_entry const& entry : _DirIterator { composed, ec }) {
if (entry.is_regular_file()) {
const fs::path file = entry;
- if (extension.empty() || file.extension() == extension) {
- if (!contains_file_with_name(ret, file.filename())) {
+ if ((extension.empty() || file.extension() == extension) && !Equiv(file, {})) {
+ size_t index = 0;
+ for (; index < ret.size() && !Equiv(file, ret[index]); ++index) {}
+ if (index >= ret.size()) {
ret.push_back(file);
+ } else if (start_of_current_root_entries <= index) {
+ Logger::warning(
+ "Files in the same directory with conflicting names: ", ret[index], " (accepted) and ",
+ file, " (rejected)"
+ );
}
}
}
@@ -425,11 +451,43 @@ Dataloader::path_vector_t Dataloader::lookup_files_in_dir(fs::path const& path,
return ret;
}
-bool Dataloader::apply_to_files_in_dir(
- fs::path const& path, fs::path const& extension, callback_t<fs::path const&> callback
+struct EquivFilename {
+ bool operator()(fs::path const& lhs, fs::path const& rhs) const {
+ return lhs.filename() == rhs.filename();
+ }
+};
+
+Dataloader::path_vector_t Dataloader::lookup_files_in_dir(std::string_view path, fs::path const& extension) const {
+ return _lookup_files_in_dir<fs::directory_iterator, EquivFilename>(path, extension);
+}
+
+Dataloader::path_vector_t Dataloader::lookup_files_in_dir_recursive(std::string_view path, fs::path const& extension) const {
+ return _lookup_files_in_dir<fs::recursive_directory_iterator, EquivFilename>(path, extension);
+}
+
+struct EquivBasicIdentifierPrefix {
+ bool operator()(fs::path const& lhs, fs::path const& rhs) const {
+ const std::string lhs_str = lhs.stem().string();
+ const std::string rhs_str = rhs.stem().string();
+ return extract_basic_identifier_prefix(lhs_str) == extract_basic_identifier_prefix(rhs_str);
+ }
+};
+
+Dataloader::path_vector_t Dataloader::lookup_basic_indentifier_prefixed_files_in_dir(
+ std::string_view path, fs::path const& extension
) const {
+ return _lookup_files_in_dir<fs::directory_iterator, EquivBasicIdentifierPrefix>(path, extension);
+}
+
+Dataloader::path_vector_t Dataloader::lookup_basic_indentifier_prefixed_files_in_dir_recursive(
+ std::string_view path, fs::path const& extension
+) const {
+ return _lookup_files_in_dir<fs::recursive_directory_iterator, EquivBasicIdentifierPrefix>(path, extension);
+}
+
+bool Dataloader::apply_to_files(path_vector_t const& files, callback_t<fs::path const&> callback) const {
bool ret = true;
- for (fs::path const& file : lookup_files_in_dir(path, extension)) {
+ for (fs::path const& file : files) {
if (!callback(file)) {
Logger::error("Callback failed for file: ", file);
ret = false;
@@ -504,8 +562,9 @@ csv::Windows1252Parser Dataloader::parse_csv(fs::path const& path) {
bool Dataloader::_load_pop_types(
PopManager& pop_manager, UnitManager const& unit_manager, GoodManager const& good_manager
) const {
- static const fs::path pop_type_directory = "poptypes";
- const bool ret = apply_to_files_in_dir(pop_type_directory, ".txt",
+ static constexpr std::string_view pop_type_directory = "poptypes";
+ const bool ret = apply_to_files(
+ lookup_files_in_dir(pop_type_directory, ".txt"),
[&pop_manager, &unit_manager, &good_manager](fs::path const& file) -> bool {
return pop_manager.load_pop_type_file(
file.stem().string(), unit_manager, good_manager, parse_defines(file).get_file_node()
@@ -517,36 +576,40 @@ bool Dataloader::_load_pop_types(
}
bool Dataloader::_load_units(UnitManager& unit_manager, GoodManager const& good_manager) const {
- static const fs::path units_directory = "units";
- const bool ret = apply_to_files_in_dir(units_directory, ".txt",
+ static constexpr std::string_view units_directory = "units";
+ const bool ret = apply_to_files(
+ lookup_files_in_dir(units_directory, ".txt"),
[&unit_manager, &good_manager](fs::path const& file) -> bool {
return unit_manager.load_unit_file(good_manager, parse_defines(file).get_file_node());
- });
+ }
+ );
unit_manager.lock_units();
return ret;
}
-bool Dataloader::_load_history(GameManager& game_manager) const {
- static const fs::path country_history_directory = "history/countries";
- static const fs::path province_history_directory = "history/provinces";
+bool Dataloader::_load_history(GameManager& game_manager, bool unused_history_file_warnings) const {
/* Country History */
- bool ret = apply_to_files_in_dir(country_history_directory, ".txt", [this, &game_manager](fs::path const& file) -> bool {
- const std::string filename = file.stem().string();
- // TODO - standardise rules on country idenifiers characters (probably letters + underscore) and enforce them
- const size_t len = std::min(std::min(filename.find(" "), filename.find("-")), filename.length());
- const std::string_view country_id { filename.data(), len };
-
- Country const* country = game_manager.get_country_manager().get_country_by_identifier(country_id);
- if (country == nullptr) {
- Logger::warning("Found history file for non-existent country: ", country_id);
- return true;
- }
+ static constexpr std::string_view country_history_directory = "history/countries";
+ bool ret = apply_to_files(
+ lookup_basic_indentifier_prefixed_files_in_dir(country_history_directory, ".txt"),
+ [this, &game_manager, unused_history_file_warnings](fs::path const& file) -> bool {
+ const std::string filename = file.stem().string();
+ const std::string_view country_id = extract_basic_identifier_prefix(filename);
+
+ Country const* country = game_manager.get_country_manager().get_country_by_identifier(country_id);
+ if (country == nullptr) {
+ if (unused_history_file_warnings) {
+ Logger::warning("Found history file for non-existent country: ", country_id);
+ }
+ return true;
+ }
- return game_manager.get_history_manager().get_country_manager().load_country_history_file(
- game_manager, *this, *country, parse_defines(lookup_file(file)).get_file_node()
- );
- });
+ return game_manager.get_history_manager().get_country_manager().load_country_history_file(
+ game_manager, *this, *country, parse_defines(file).get_file_node()
+ );
+ }
+ );
game_manager.get_history_manager().get_country_manager().lock_country_histories();
{
@@ -558,53 +621,51 @@ bool Dataloader::_load_history(GameManager& game_manager) const {
}
/* Province History */
- for (auto root : roots) {
- const fs::path path = root / province_history_directory;
- std::error_code ec;
- for (fs::directory_entry const& entry : fs::directory_iterator { path, ec }) {
- if (entry.is_directory()) {
- bool ret = apply_to_files_in_dir(entry, ".txt", [this, &game_manager](fs::path const& file) -> bool {
- const std::string filename = file.stem().string();
- const size_t len = std::min(filename.find(" "), filename.length());
- const std::string_view province_id { filename.data(), len };
-
- Province const* province = game_manager.get_map().get_province_by_identifier(province_id);
- if (province == nullptr) {
- Logger::warning("Found history file for non-existent province: ", province_id);
- return true;
- }
-
- return game_manager.get_history_manager().get_province_manager().load_province_history_file(
- game_manager, *province, parse_defines(lookup_file(file)).get_file_node()
- );
- });
+ static constexpr std::string_view province_history_directory = "history/provinces";
+ ret &= apply_to_files(
+ lookup_basic_indentifier_prefixed_files_in_dir_recursive(province_history_directory, ".txt"),
+ [this, &game_manager, unused_history_file_warnings](fs::path const& file) -> bool {
+ const std::string filename = file.stem().string();
+ const std::string_view province_id = extract_basic_identifier_prefix(filename);
+
+ Province const* province = game_manager.get_map().get_province_by_identifier(province_id);
+ if (province == nullptr) {
+ if (unused_history_file_warnings) {
+ Logger::warning("Found history file for non-existent province: ", province_id);
+ }
+ return true;
}
+
+ return game_manager.get_history_manager().get_province_manager().load_province_history_file(
+ game_manager, *province, parse_defines(file).get_file_node()
+ );
}
- }
+ );
game_manager.get_history_manager().get_province_manager().lock_province_histories();
return ret;
}
bool Dataloader::_load_map_dir(GameManager& game_manager) const {
- static const fs::path map_directory = "map";
+ static constexpr std::string_view map_directory = "map/";
Map& map = game_manager.get_map();
- static const fs::path defaults_filename = "default.map";
- static const std::string default_definitions = "definition.csv";
- static const std::string default_provinces = "provinces.bmp";
- static const std::string default_positions = "positions.txt";
- static const std::string default_terrain = "terrain.bmp";
- static const std::string default_rivers = "rivers.bmp";
- static const std::string default_terrain_definition = "terrain.txt";
- static const std::string default_tree_definition = "trees.txt";
- static const std::string default_continent = "continent.txt";
- static const std::string default_adjacencies = "adjacencies.csv";
- static const std::string default_region = "region.txt";
- static const std::string default_region_sea = "region_sea.txt";
- static const std::string default_province_flag_sprite = "province_flag_sprites";
-
- const v2script::Parser parser = parse_defines(lookup_file(map_directory / defaults_filename));
+ static constexpr std::string_view defaults_filename = "default.map";
+ static constexpr std::string_view default_definitions = "definition.csv";
+ static constexpr std::string_view default_provinces = "provinces.bmp";
+ static constexpr std::string_view default_positions = "positions.txt";
+ static constexpr std::string_view default_terrain = "terrain.bmp";
+ static constexpr std::string_view default_rivers = "rivers.bmp";
+ static constexpr std::string_view default_terrain_definition = "terrain.txt";
+ static constexpr std::string_view default_tree_definition = "trees.txt";
+ static constexpr std::string_view default_continent = "continent.txt";
+ static constexpr std::string_view default_adjacencies = "adjacencies.csv";
+ static constexpr std::string_view default_region = "region.txt";
+ static constexpr std::string_view default_region_sea = "region_sea.txt";
+ static constexpr std::string_view default_province_flag_sprite = "province_flag_sprites";
+
+ const v2script::Parser parser =
+ parse_defines(lookup_file(append_string_views(map_directory, defaults_filename)));
std::vector<std::string_view> water_province_identifiers;
@@ -658,20 +719,24 @@ bool Dataloader::_load_map_dir(GameManager& game_manager) const {
Logger::error("Failed to load map default file!");
}
- if (!map.load_province_definitions(parse_csv(lookup_file(map_directory / definitions)).get_lines())) {
+ if (!map.load_province_definitions(
+ parse_csv(lookup_file(append_string_views(map_directory, definitions))).get_lines()
+ )) {
Logger::error("Failed to load province definitions file!");
ret = false;
}
if (!map.load_province_positions(
game_manager.get_economy_manager().get_building_manager(),
- parse_defines(lookup_file(map_directory / positions)).get_file_node()
+ parse_defines(lookup_file(append_string_views(map_directory, positions))).get_file_node()
)) {
Logger::error("Failed to load province positions file!");
ret = false;
}
- if (!map.load_region_file(parse_defines(lookup_file(map_directory / region)).get_file_node())) {
+ if (!map.load_region_file(
+ parse_defines(lookup_file(append_string_views(map_directory, region))).get_file_node()
+ )) {
Logger::error("Failed to load region file!");
ret = false;
}
@@ -682,18 +747,24 @@ bool Dataloader::_load_map_dir(GameManager& game_manager) const {
}
if (!map.get_terrain_type_manager().load_terrain_types(
- game_manager.get_modifier_manager(), parse_defines(lookup_file(map_directory / terrain_definition)).get_file_node()
+ game_manager.get_modifier_manager(),
+ parse_defines(lookup_file(append_string_views(map_directory, terrain_definition))).get_file_node()
)) {
Logger::error("Failed to load terrain types!");
ret = false;
}
- if (!map.load_map_images(lookup_file(map_directory / provinces), lookup_file(map_directory / terrain), false)) {
+ if (!map.load_map_images(
+ lookup_file(append_string_views(map_directory, provinces)),
+ lookup_file(append_string_views(map_directory, terrain)), false
+ )) {
Logger::error("Failed to load map images!");
ret = false;
}
- if (!map.generate_and_load_province_adjacencies(parse_csv(lookup_file(map_directory / adjacencies)).get_lines())) {
+ if (!map.generate_and_load_province_adjacencies(
+ parse_csv(lookup_file(append_string_views(map_directory, adjacencies))).get_lines()
+ )) {
Logger::error("Failed to generate and load province adjacencies!");
ret = false;
}
@@ -702,20 +773,20 @@ bool Dataloader::_load_map_dir(GameManager& game_manager) const {
}
bool Dataloader::load_defines(GameManager& game_manager) const {
- static const fs::path defines_file = "common/defines.lua";
- static const fs::path buildings_file = "common/buildings.txt";
- static const fs::path bookmark_file = "common/bookmarks.txt";
- static const fs::path countries_file = "common/countries.txt";
- static const fs::path culture_file = "common/cultures.txt";
- static const fs::path goods_file = "common/goods.txt";
- static const fs::path governments_file = "common/governments.txt";
- static const fs::path graphical_culture_type_file = "common/graphicalculturetype.txt";
- static const fs::path ideology_file = "common/ideologies.txt";
- static const fs::path issues_file = "common/issues.txt";
- static const fs::path national_values_file = "common/nationalvalues.txt";
- static const fs::path production_types_file = "common/production_types.txt";
- static const fs::path religion_file = "common/religion.txt";
- static const fs::path leader_traits_file = "common/traits.txt";
+ static const std::string defines_file = "common/defines.lua";
+ static const std::string buildings_file = "common/buildings.txt";
+ static const std::string bookmark_file = "common/bookmarks.txt";
+ static const std::string countries_file = "common/countries.txt";
+ static const std::string culture_file = "common/cultures.txt";
+ static const std::string goods_file = "common/goods.txt";
+ static const std::string governments_file = "common/governments.txt";
+ static const std::string graphical_culture_type_file = "common/graphicalculturetype.txt";
+ static const std::string ideology_file = "common/ideologies.txt";
+ static const std::string issues_file = "common/issues.txt";
+ static const std::string national_values_file = "common/nationalvalues.txt";
+ static const std::string production_types_file = "common/production_types.txt";
+ static const std::string religion_file = "common/religion.txt";
+ static const std::string leader_traits_file = "common/traits.txt";
bool ret = true;
@@ -815,12 +886,12 @@ bool Dataloader::load_defines(GameManager& game_manager) const {
ret = false;
}
if (!game_manager.get_country_manager().load_countries(
- game_manager, *this, countries_file.parent_path(), parse_defines(lookup_file(countries_file)).get_file_node()
+ game_manager, *this, parse_defines(lookup_file(countries_file)).get_file_node()
)) {
Logger::error("Failed to load countries!");
ret = false;
}
- if (!_load_history(game_manager)) {
+ if (!_load_history(game_manager, false)) {
Logger::error("Failed to load history!");
ret = false;
}
@@ -828,14 +899,17 @@ bool Dataloader::load_defines(GameManager& game_manager) const {
return ret;
}
-bool Dataloader::load_pop_history(GameManager& game_manager, fs::path const& path) const {
- return apply_to_files_in_dir(path, ".txt", [&game_manager](fs::path const& file) -> bool {
- return game_manager.get_map().expect_province_dictionary(
- [&game_manager](Province& province, ast::NodeCPtr value) -> bool {
- return province.load_pop_list(game_manager.get_pop_manager(), value);
- }
- )(parse_defines(file).get_file_node());
- });
+bool Dataloader::load_pop_history(GameManager& game_manager, std::string_view path) const {
+ return apply_to_files(
+ lookup_files_in_dir(path, ".txt"),
+ [&game_manager](fs::path const& file) -> bool {
+ return game_manager.get_map().expect_province_dictionary(
+ [&game_manager](Province& province, ast::NodeCPtr value) -> bool {
+ return province.load_pop_list(game_manager.get_pop_manager(), value);
+ }
+ )(parse_defines(file).get_file_node());
+ }
+ );
}
static bool _load_localisation_file(Dataloader::localisation_callback_t callback, std::vector<csv::LineObject> const& lines) {
@@ -855,8 +929,11 @@ static bool _load_localisation_file(Dataloader::localisation_callback_t callback
return ret;
}
-bool Dataloader::load_localisation_files(localisation_callback_t callback, fs::path const& localisation_dir) const {
- return apply_to_files_in_dir(localisation_dir, ".csv", [callback](fs::path path) -> bool {
- return _load_localisation_file(callback, parse_csv(path).get_lines());
- });
+bool Dataloader::load_localisation_files(localisation_callback_t callback, std::string_view localisation_dir) const {
+ return apply_to_files(
+ lookup_files_in_dir(localisation_dir, ".csv"),
+ [callback](fs::path path) -> bool {
+ return _load_localisation_file(callback, parse_csv(path).get_lines());
+ }
+ );
}
diff --git a/src/openvic-simulation/dataloader/Dataloader.hpp b/src/openvic-simulation/dataloader/Dataloader.hpp
index a8b8bb1..2123469 100644
--- a/src/openvic-simulation/dataloader/Dataloader.hpp
+++ b/src/openvic-simulation/dataloader/Dataloader.hpp
@@ -26,7 +26,13 @@ namespace OpenVic {
bool _load_pop_types(PopManager& pop_manager, UnitManager const& unit_manager, GoodManager const& good_manager) const;
bool _load_units(UnitManager& unit_manager, GoodManager const& good_manager) const;
bool _load_map_dir(GameManager& game_manager) const;
- bool _load_history(GameManager& game_manager) const;
+ bool _load_history(GameManager& game_manager, bool unused_history_file_warnings) const;
+
+ /* _DirIterator is fs::directory_iterator or fs::recursive_directory_iterator.
+ * _Equiv is an equivalence relation with respect to which every found file shall be unique.
+ * If a file is equivalent to the empty path then it is not included. */
+ template<typename _DirIterator, std::predicate<fs::path const&, fs::path const&> _Equiv>
+ path_vector_t _lookup_files_in_dir(std::string_view path, fs::path const& extension) const;
public:
static ovdl::v2script::Parser parse_defines(fs::path const& path);
@@ -63,15 +69,17 @@ namespace OpenVic {
/* REQUIREMENTS:
* DAT-24
*/
- fs::path lookup_file(fs::path const& path, bool print_error = true) const;
- fs::path lookup_file_case_insensitive(fs::path const& path, bool print_error = true) const;
- path_vector_t lookup_files_in_dir(fs::path const& path, fs::path const& extension) const;
- bool apply_to_files_in_dir(
- fs::path const& path, fs::path const& extension, NodeTools::callback_t<fs::path const&> callback
+ fs::path lookup_file(std::string_view path, bool print_error = true) const;
+ path_vector_t lookup_files_in_dir(std::string_view path, fs::path const& extension) const;
+ path_vector_t lookup_files_in_dir_recursive(std::string_view path, fs::path const& extension) const;
+ path_vector_t lookup_basic_indentifier_prefixed_files_in_dir(std::string_view path, fs::path const& extension) const;
+ path_vector_t lookup_basic_indentifier_prefixed_files_in_dir_recursive(
+ std::string_view path, fs::path const& extension
) const;
+ bool apply_to_files(path_vector_t const& files, NodeTools::callback_t<fs::path const&> callback) const;
bool load_defines(GameManager& game_manager) const;
- bool load_pop_history(GameManager& game_manager, fs::path const& path) const;
+ bool load_pop_history(GameManager& game_manager, std::string_view path) const;
enum locale_t : size_t {
English, French, German, Polish, Spanish, Italian, Swedish,
@@ -85,7 +93,7 @@ namespace OpenVic {
/* Args: key, locale, localisation */
using localisation_callback_t = NodeTools::callback_t<std::string_view, locale_t, std::string_view>;
bool load_localisation_files(
- localisation_callback_t callback, fs::path const& localisation_dir = "localisation"
+ localisation_callback_t callback, std::string_view localisation_dir = "localisation"
) const;
private:
@@ -97,6 +105,6 @@ namespace OpenVic {
using hint_path_t = fs::path;
using game_path_t = fs::path;
- inline static std::unordered_map<hint_path_t, game_path_t, fshash> _cached_paths;
+ static inline std::unordered_map<hint_path_t, game_path_t, fshash> _cached_paths;
};
}
diff --git a/src/openvic-simulation/map/Map.cpp b/src/openvic-simulation/map/Map.cpp
index b1aea2c..7e2213e 100644
--- a/src/openvic-simulation/map/Map.cpp
+++ b/src/openvic-simulation/map/Map.cpp
@@ -42,6 +42,12 @@ bool Map::add_province(std::string_view identifier, colour_t colour) {
Logger::error("Invalid province identifier - empty!");
return false;
}
+ if (!valid_basic_identifier(identifier)) {
+ Logger::error(
+ "Invalid province identifier: ", identifier, " (can only contain alphanumeric characters and underscores)"
+ );
+ return false;
+ }
if (colour == NULL_COLOUR || colour > MAX_COLOUR_RGB) {
Logger::error("Invalid province colour for ", identifier, ": ", colour_to_hex_string(colour));
return false;
@@ -483,7 +489,7 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain
uint8_t const* terrain_data = terrain_bmp.get_pixel_data().data();
std::vector<bool> province_checklist(provinces.size());
- std::vector<distribution_t> terrain_type_pixels_list(provinces.size());
+ std::vector<decimal_map_t<TerrainType const*>> terrain_type_pixels_list(provinces.size());
bool ret = true;
std::unordered_set<colour_t> unrecognised_province_colours;
diff --git a/src/openvic-simulation/map/Province.cpp b/src/openvic-simulation/map/Province.cpp
index 66e4daf..77bf33f 100644
--- a/src/openvic-simulation/map/Province.cpp
+++ b/src/openvic-simulation/map/Province.cpp
@@ -131,31 +131,21 @@ Pop::pop_size_t Province::get_total_population() const {
return total_population;
}
-distribution_t const& Province::get_pop_type_distribution() const {
- return pop_types;
-}
-
-distribution_t const& Province::get_culture_distribution() const {
- return cultures;
-}
-
-distribution_t const& Province::get_religion_distribution() const {
- return religions;
-}
-
/* REQUIREMENTS:
* MAP-65, MAP-68, MAP-70, MAP-234
*/
void Province::update_pops() {
total_population = 0;
- pop_types.clear();
- cultures.clear();
- religions.clear();
+ pop_type_distribution.clear();
+ ideology_distribution.clear();
+ culture_distribution.clear();
+ religion_distribution.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();
+ pop_type_distribution[&pop.get_type()] += pop.get_size();
+ //ideology_distribution[&pop.get_???()] += pop.get_size();
+ culture_distribution[&pop.get_culture()] += pop.get_size();
+ religion_distribution[&pop.get_religion()] += pop.get_size();
}
}
diff --git a/src/openvic-simulation/map/Province.hpp b/src/openvic-simulation/map/Province.hpp
index 6c022b7..2fd15c9 100644
--- a/src/openvic-simulation/map/Province.hpp
+++ b/src/openvic-simulation/map/Province.hpp
@@ -3,6 +3,7 @@
#include <cassert>
#include "openvic-simulation/economy/Building.hpp"
+#include "openvic-simulation/politics/Ideology.hpp"
#include "openvic-simulation/pop/Pop.hpp"
namespace OpenVic {
@@ -72,7 +73,10 @@ namespace OpenVic {
std::vector<Pop> pops;
Pop::pop_size_t total_population;
- distribution_t pop_types, cultures, religions;
+ decimal_map_t<PopType const*> PROPERTY(pop_type_distribution);
+ decimal_map_t<Ideology const*> PROPERTY(ideology_distribution);
+ decimal_map_t<Culture const*> PROPERTY(culture_distribution);
+ decimal_map_t<Religion const*> PROPERTY(religion_distribution);
std::vector<adjacency_t> adjacencies;
province_positions_t positions;
@@ -109,9 +113,6 @@ namespace OpenVic {
size_t get_pop_count() const;
std::vector<Pop> const& get_pops() const;
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 today);
diff --git a/src/openvic-simulation/military/Deployment.cpp b/src/openvic-simulation/military/Deployment.cpp
index b5d335a..0530986 100644
--- a/src/openvic-simulation/military/Deployment.cpp
+++ b/src/openvic-simulation/military/Deployment.cpp
@@ -37,9 +37,6 @@ bool DeploymentManager::add_deployment(
Logger::error("Attemped to load order of battle with no path! Something is very wrong!");
return false;
}
- if (armies.empty() && navies.empty() && leaders.empty()) {
- Logger::warning("Loading redundant empty order of battle at ", path);
- }
return deployments.add_item(
std::make_unique<Deployment>(std::move(path), std::move(armies), std::move(navies), std::move(leaders))
@@ -47,8 +44,8 @@ bool DeploymentManager::add_deployment(
}
bool DeploymentManager::load_oob_file(
- GameManager const& game_manager, Dataloader const& dataloader, std::string_view history_path, Deployment const*& deployment,
- bool fail_on_missing
+ GameManager const& game_manager, Dataloader const& dataloader, std::string_view history_path,
+ Deployment const*& deployment, bool fail_on_missing
) {
deployment = get_deployment_by_identifier(history_path);
if (deployment != nullptr) {
@@ -57,14 +54,13 @@ bool DeploymentManager::load_oob_file(
if (missing_oob_files.contains(history_path)) {
return !fail_on_missing;
}
- static const fs::path oob_directory = "history/units/";
- fs::path full_path = oob_directory;
- full_path += history_path;
- const fs::path lookedup_path = dataloader.lookup_file(full_path, false);
+ static constexpr std::string_view oob_directory = "history/units/";
+ const fs::path lookedup_path =
+ dataloader.lookup_file(StringUtils::append_string_views(oob_directory, history_path), false);
if (lookedup_path.empty()) {
missing_oob_files.emplace(history_path);
if (fail_on_missing) {
- Logger::warning("Could not find OOB file ", full_path, "!");
+ Logger::warning("Could not find OOB file ", history_path, "!");
return false;
} else {
return true;
diff --git a/src/openvic-simulation/military/LeaderTrait.hpp b/src/openvic-simulation/military/LeaderTrait.hpp
index e61a1fc..16b4201 100644
--- a/src/openvic-simulation/military/LeaderTrait.hpp
+++ b/src/openvic-simulation/military/LeaderTrait.hpp
@@ -46,7 +46,7 @@ namespace OpenVic {
struct LeaderTraitManager {
private:
IdentifierRegistry<LeaderTrait> leader_traits;
- inline static const string_set_t allowed_modifiers = {
+ static inline const string_set_t allowed_modifiers = {
"attack", "defence", "morale", "organisation", "reconnaissance", "speed", "attrition", "experience", "reliability"
};
diff --git a/src/openvic-simulation/pop/Culture.cpp b/src/openvic-simulation/pop/Culture.cpp
index 9159fe1..47501e0 100644
--- a/src/openvic-simulation/pop/Culture.cpp
+++ b/src/openvic-simulation/pop/Culture.cpp
@@ -174,7 +174,7 @@ bool CultureManager::load_culture_file(ast::NodeCPtr root) {
return false;
}
- static const std::string default_unit_graphical_culture_type_identifier = "Generic";
+ static constexpr std::string_view default_unit_graphical_culture_type_identifier = "Generic";
GraphicalCultureType const* const default_unit_graphical_culture_type =
get_graphical_culture_type_by_identifier(default_unit_graphical_culture_type_identifier);
if (default_unit_graphical_culture_type == nullptr) {
diff --git a/src/openvic-simulation/types/IdentifierRegistry.hpp b/src/openvic-simulation/types/IdentifierRegistry.hpp
index e68f2a4..734421d 100644
--- a/src/openvic-simulation/types/IdentifierRegistry.hpp
+++ b/src/openvic-simulation/types/IdentifierRegistry.hpp
@@ -10,6 +10,21 @@
#include "openvic-simulation/utility/Logger.hpp"
namespace OpenVic {
+
+ constexpr bool valid_basic_identifier_char(char c) {
+ return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || c == '_';
+ }
+ constexpr bool valid_basic_identifier(std::string_view identifier) {
+ return std::all_of(identifier.begin(), identifier.end(), valid_basic_identifier_char);
+ }
+ constexpr std::string_view extract_basic_identifier_prefix(std::string_view identifier) {
+ size_t len = 0;
+ while (len < identifier.size() && valid_basic_identifier_char(identifier[len])) {
+ ++len;
+ }
+ return { identifier.data(), len };
+ }
+
/*
* Base class for objects with a non-empty string identifier. Uniquely named instances of a type derived from this class
* can be entered into an IdentifierRegistry instance.
@@ -80,8 +95,6 @@ namespace OpenVic {
}
}
- using distribution_t = decimal_map_t<HasIdentifierAndColour const*>;
-
/* Callbacks for trying to add duplicate keys via UniqueKeyRegistry::add_item */
static bool duplicate_fail_callback(std::string_view registry_name, std::string_view duplicate_identifier) {
Logger::error(
@@ -192,20 +205,20 @@ namespace OpenVic {
}
}
-#define GETTERS \
- value_type _const* get_item_by_identifier(std::string_view identifier) _const { \
+#define GETTERS(CONST) \
+ value_type CONST* get_item_by_identifier(std::string_view identifier) CONST { \
const typename decltype(identifier_index_map)::const_iterator it = identifier_index_map.find(identifier); \
if (it != identifier_index_map.end()) { \
return GetPointer(items[it->second]); \
} \
return nullptr; \
} \
- value_type _const* get_item_by_index(size_t index) _const { \
+ value_type CONST* get_item_by_index(size_t index) CONST { \
return index < items.size() ? &items[index] : nullptr; \
} \
- NodeTools::callback_t<std::string_view> expect_item_str(NodeTools::callback_t<value_type _const&> callback) _const { \
+ NodeTools::callback_t<std::string_view> expect_item_str(NodeTools::callback_t<value_type CONST&> callback) CONST { \
return [this, callback](std::string_view identifier) -> bool { \
- value_type _const* item = get_item_by_identifier(identifier); \
+ value_type CONST* item = get_item_by_identifier(identifier); \
if (item != nullptr) { \
return callback(*item); \
} \
@@ -213,22 +226,19 @@ namespace OpenVic {
return false; \
}; \
} \
- NodeTools::node_callback_t expect_item_identifier(NodeTools::callback_t<value_type _const&> callback) _const { \
+ NodeTools::node_callback_t expect_item_identifier(NodeTools::callback_t<value_type CONST&> callback) CONST { \
return NodeTools::expect_identifier(expect_item_str(callback)); \
} \
- NodeTools::node_callback_t expect_item_dictionary(NodeTools::callback_t<value_type _const&, ast::NodeCPtr> callback) \
- _const { \
+ NodeTools::node_callback_t expect_item_dictionary( \
+ NodeTools::callback_t<value_type CONST&, ast::NodeCPtr> callback \
+ ) CONST { \
return NodeTools::expect_dictionary([this, callback](std::string_view key, ast::NodeCPtr value) -> bool { \
return expect_item_str(std::bind(callback, std::placeholders::_1, value))(key); \
}); \
}
-#define _const
- GETTERS
-#undef _const
-#define _const const
- GETTERS
-#undef _const
+ GETTERS()
+ GETTERS(const)
#undef GETTERS
diff --git a/src/openvic-simulation/utility/Logger.cpp b/src/openvic-simulation/utility/Logger.cpp
deleted file mode 100644
index 63dfd6c..0000000
--- a/src/openvic-simulation/utility/Logger.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#include "Logger.hpp"
-
-#include <iostream>
-
-using namespace OpenVic;
-
-void Logger::set_logger_funcs() {
- Logger::set_info_func([](std::string&& str) {
- std::cout << "[INFO] " << str;
- });
- Logger::set_warning_func([](std::string&& str) {
- std::cerr << "[WARNING] " << str;
- });
- Logger::set_error_func([](std::string&& str) {
- std::cerr << "[ERROR] " << str;
- });
-}
-
-char const* Logger::get_filename(char const* filepath, char const* default_path) {
- if (filepath == nullptr) {
- return default_path;
- }
- char const* last_slash = filepath;
- while (*filepath != '\0') {
- if (*filepath == '\\' || *filepath == '/') {
- last_slash = filepath + 1;
- }
- filepath++;
- }
- if (*last_slash == '\0') {
- return default_path;
- }
- return last_slash;
-}
diff --git a/src/openvic-simulation/utility/Logger.hpp b/src/openvic-simulation/utility/Logger.hpp
index 04306f7..20c7fdd 100644
--- a/src/openvic-simulation/utility/Logger.hpp
+++ b/src/openvic-simulation/utility/Logger.hpp
@@ -1,12 +1,16 @@
#pragma once
#include <functional>
+#include <iostream>
#include <queue>
#include <sstream>
+
#ifdef __cpp_lib_source_location
#include <source_location>
#endif
+#include "openvic-simulation/utility/StringUtils.hpp"
+
namespace OpenVic {
#ifndef __cpp_lib_source_location
@@ -52,8 +56,17 @@ namespace OpenVic {
#endif
public:
- static void set_logger_funcs();
- static char const* get_filename(char const* filepath, char const* default_path = nullptr);
+ static void set_logger_funcs() {
+ set_info_func([](std::string&& str) {
+ std::cout << "[INFO] " << str;
+ });
+ set_warning_func([](std::string&& str) {
+ std::cerr << "[WARNING] " << str;
+ });
+ set_error_func([](std::string&& str) {
+ std::cerr << "[ERROR] " << str;
+ });
+ }
private:
struct log_channel_t {
@@ -65,13 +78,13 @@ namespace OpenVic {
struct log {
log(log_channel_t& log_channel, Ts&&... ts, source_location const& location) {
std::stringstream stream;
- stream << get_filename(location.file_name()) << "("
+ stream << StringUtils::get_filename(location.file_name()) << "("
/* Function name removed to reduce clutter. It is already included
* in Godot's print functions, so this was repeating it. */
//<< location.line() << ") `" << location.function_name() << "`: ";
<< location.line() << "): ";
((stream << std::forward<Ts>(ts)), ...);
- stream << "\n" << std::endl;
+ stream << std::endl;
log_channel.queue.push(stream.str());
if (log_channel.func) {
do {
diff --git a/src/openvic-simulation/utility/StringUtils.hpp b/src/openvic-simulation/utility/StringUtils.hpp
index d968bf6..ede1d6b 100644
--- a/src/openvic-simulation/utility/StringUtils.hpp
+++ b/src/openvic-simulation/utility/StringUtils.hpp
@@ -1,4 +1,7 @@
+#pragma once
+
#include <cstdint>
+#include <cstring>
#include <limits>
#include <string_view>
@@ -90,7 +93,7 @@ namespace OpenVic::StringUtils {
return result;
}
- constexpr uint64_t string_to_uint64(char const* str, size_t length, bool* successful = nullptr, int base = 10) {
+ inline 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);
}
@@ -135,11 +138,72 @@ namespace OpenVic::StringUtils {
}
}
- constexpr int64_t string_to_int64(char const* str, size_t length, bool* successful = nullptr, int base = 10) {
+ inline 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(std::string_view str, bool* successful = nullptr, int base = 10) {
return string_to_int64(str.data(), str.length(), successful, base);
}
+
+ inline constexpr std::string_view get_filename(std::string_view path) {
+ size_t pos = path.size();
+ while (pos > 0 && path[pos - 1] != '/' && path[pos - 1] != '\\') {
+ --pos;
+ }
+ path.remove_prefix(pos);
+ return path;
+ }
+
+ inline constexpr char const* get_filename(char const* filepath, char const* default_path = nullptr) {
+ const std::string_view filename { get_filename(std::string_view { filepath }) };
+ if (!filename.empty()) {
+ return filename.data();
+ }
+ return default_path;
+ }
+
+ inline std::string make_forward_slash_path(std::string_view path) {
+ std::string ret { path };
+ std::replace(ret.begin(), ret.end(), '\\', '/');
+ for (char& c : ret) {
+ if (c == '\\') {
+ c = '/';
+ }
+ }
+ return ret;
+ }
+
+ inline constexpr std::string_view remove_leading_slashes(std::string_view path) {
+ size_t count = 0;
+ while (count < path.size() && (path[count] == '/' || path[count] == '\\')) {
+ ++count;
+ }
+ path.remove_prefix(count);
+ return path;
+ }
+
+ template<typename... Args>
+ requires (std::is_same_v<std::string_view, Args> && ...)
+ inline std::string _append_string_views(Args... args) {
+ std::string ret;
+ ret.reserve((args.size() + ...));
+ (ret.append(args), ...);
+ return ret;
+ }
+
+ template<typename... Args>
+ requires (std::is_convertible_v<Args, std::string_view> && ...)
+ inline std::string append_string_views(Args... args) {
+ return _append_string_views(std::string_view { args }...);
+ }
+
+ inline constexpr std::string_view remove_extension(std::string_view path) {
+ size_t pos = path.size();
+ while (pos > 0 && path[--pos] != '.') {}
+ if (path[pos] == '.') {
+ path.remove_suffix(path.size() - pos);
+ }
+ return path;
+ }
}