29 #include <boost/algorithm/string/replace.hpp>
30 #include <boost/iostreams/filter/gzip.hpp>
33 #define LOG_SAVE LOG_STREAM(info, log_engine)
34 #define ERR_SAVE LOG_STREAM(err, log_engine)
37 #define LOG_RG LOG_STREAM(info, log_enginerefac)
62 summary[
"corrupt"] =
true;
65 summary[
"mod_time"] = std::to_string(
static_cast<int>(modified));
103 std::vector<std::string> filenames;
109 return std::find(filenames.begin(), filenames.end(),
d[
"save"]) == filenames.end();
120 LOG_SAVE <<
"no-op: read_only instance";
139 ERR_SAVE <<
"error writing to save index file: '" <<
e.what() <<
"'";
149 , clean_up_index_(true)
162 if(
auto sv = cfg.
find_child(
"save",
"save", name)) {
182 }
catch(
const boost::iostreams::gzip_error&) {
187 ERR_SAVE <<
"error reading save index: '" <<
e.what() <<
"'";
189 ERR_SAVE <<
"error parsing save index config file:\n" <<
e.message;
202 std::string leader_image = leader[
"leader_image"];
203 boost::algorithm::replace_all(leader_image,
"\\",
"/");
205 leader[
"leader_image"] = leader_image;
220 std::vector<std::string> filenames;
226 static const std::vector<std::string> to_ignore {
"steam_autocloud.vdf"};
228 if(std::find(to_ignore.begin(), to_ignore.end(),
filename) != to_ignore.end()) {
231 return filename.end() == std::search(filename.begin(), filename.end(), filter->begin(), filter->end());
237 std::vector<save_info> result;
238 std::transform(filenames.begin(), filenames.end(), std::back_inserter(result), creator);
251 if(std::tm* tm_l = std::localtime(&
modified())) {
254 ?
_(
"%a %b %d %Y, %I:%M %p")
256 :
_(
"%a %b %d %Y, %H:%M");
276 const std::string replay_str =
" " +
_(
"replay");
281 }
else if(a.
name().find(replay_str) == std::string::npos &&
b.name().find(replay_str) != std::string::npos) {
288 }
else if(a.
name().find(replay_str) != std::string::npos &&
b.name().find(replay_str) == std::string::npos) {
291 return a.
name() >
b.name();
296 const std::string& name,
const std::vector<std::string>& suffixes)
298 for(
const std::string& suf : suffixes) {
302 if(!file_stream->fail()) {
307 LOG_SAVE <<
"Could not open supplied filename '" << name <<
"'";
313 static const std::vector<std::string> suffixes{
"",
".gz",
".bz2"};
327 read(cfg, *file_stream);
329 }
catch(
const std::ios_base::failure&
e) {
333 *error_log +=
e.what();
340 *error_log +=
err.message;
347 LOG_SAVE <<
"Could not parse file data into config";
356 LOG_SAVE <<
"no-op: read_only instance";
360 const std::string auto_save =
_(
"Auto-Save");
362 int countdown = autosavemax;
363 if(countdown == infinite_auto_saves) {
369 if(countdown-- <= 0) {
370 LOG_SAVE <<
"Deleting savegame '" <<
i->name() <<
"'";
380 LOG_SAVE <<
"no-op: read_only instance";
405 auto cfg_replay_start = cfg_save.
has_child(
"replay_start")
410 const bool has_replay = cfg_replay && !cfg_replay->empty();
411 const bool has_snapshot = cfg_snapshot && cfg_snapshot->has_child(
"side");
413 cfg_summary[
"replay"] = has_replay;
414 cfg_summary[
"snapshot"] = has_snapshot;
416 cfg_summary[
"label"] = cfg_save[
"label"];
417 cfg_summary[
"campaign_type"] = cfg_save[
"campaign_type"];
419 if(cfg_save.
has_child(
"carryover_sides_start")) {
420 cfg_summary[
"scenario"] = cfg_save.
mandatory_child(
"carryover_sides_start")[
"next_scenario"];
422 cfg_summary[
"scenario"] = cfg_save[
"scenario"];
425 cfg_summary[
"difficulty"] = cfg_save[
"difficulty"];
426 cfg_summary[
"random_mode"] = cfg_save[
"random_mode"];
428 cfg_summary[
"active_mods"] = cfg_save.
child_or_empty(
"multiplayer")[
"active_mods"];
429 cfg_summary[
"campaign"] = cfg_save[
"campaign"];
430 cfg_summary[
"version"] = cfg_save[
"version"];
431 cfg_summary[
"corrupt"] =
"";
434 cfg_summary[
"turn"] = cfg_snapshot[
"turn_at"];
435 if(cfg_snapshot[
"turns"] !=
"-1") {
436 cfg_summary[
"turn"] = cfg_summary[
"turn"].str() +
"/" + cfg_snapshot[
"turns"].str();
446 bool shrouded =
false;
448 if(
auto snapshot = (has_snapshot ? cfg_snapshot : cfg_replay_start)) {
451 std::string leader_image;
452 std::string leader_image_tc_modifier;
453 std::string leader_name;
454 int gold = side[
"gold"].to_int();
455 int units = 0, recall_units = 0;
457 if(side[
"controller"] != side_controller::human) {
461 if(side[
"shroud"].to_bool()) {
466 if(u.has_attribute(
"x") && u.has_attribute(
"y")) {
473 if(!leader.empty() || !u[
"canrecruit"].to_bool()) {
481 leader = u[
"id"].str();
482 leader_name = u[
"name"].str();
483 leader_image = u[
"image"].str();
484 leader_image_tc_modifier =
"~RC(" + u[
"flag_rgb"].str() +
">" + tc_color +
")";
495 if(leader_image_path) {
496 leader_image = leader_image_path.value() + leader_image_tc_modifier;
499 leader_config[
"leader"] = leader;
500 leader_config[
"leader_name"] = leader_name;
501 leader_config[
"leader_image"] = leader_image;
502 leader_config[
"leader_image_tc_modifier"] = leader_image_tc_modifier;
503 leader_config[
"gold"] = gold;
504 leader_config[
"units"] = units;
505 leader_config[
"recall_units"] = recall_units;
507 cfg_summary.
add_child(
"leader", leader_config);
513 if(!cfg_snapshot->find_child(
"side",
"shroud",
"yes") && cfg_snapshot->has_attribute(
"map_data")) {
514 cfg_summary[
"map_data"] = cfg_snapshot[
"map_data"].str();
516 ERR_SAVE <<
"Not saving map because there is shroud";
518 }
else if(has_replay) {
519 if(!cfg_replay_start->find_child(
"side",
"shroud",
"yes") && cfg_replay_start->has_attribute(
"map_data")) {
520 cfg_summary[
"map_data"] = cfg_replay_start[
"map_data"];
522 ERR_SAVE <<
"Not saving map because there is shroud";
Variant for storing WML attributes.
std::time_t to_time_t(std::time_t def=0) const
bool empty() const
Tests for an attribute that either was never set or was set to "".
A config object defines a single node in a WML file, with access to child nodes.
const config & child_or_empty(config_key_type key) const
Returns the first child with the given key, or an empty config if there is none.
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
optional_config_impl< config > find_child(config_key_type key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
void clear_children(T... keys)
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
child_itors child_range(config_key_type key)
std::size_t all_children_count() const
void remove_children(config_key_type key, std::function< bool(const config &)> p={})
Removes all children with tag key for which p returns true.
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
config & add_child(config_key_type key)
bool use_twelve_hour_clock_format()
std::shared_ptr< save_index_class > manager_
save_info operator()(const std::string &filename) const
create_save_info(const std::shared_ptr< save_index_class > &)
void clean_up_index()
Deletes non-existent save files from the index.
create_for_default_saves_dir
Syntatic sugar for choosing which constructor to use.
static std::shared_ptr< save_index_class > default_saves_dir()
Returns an instance for managing saves in filesystem::get_saves_dir()
std::vector< save_info > get_saves_list(const std::string *filter=nullptr)
Get a list of available saves.
void remove(const std::string &name)
Delete a savegame from the index, without deleting the underlying file.
bool read_only_
The instance for default_saves_dir() writes a cache file.
void write_save_index()
Sync to disk, no-op if read_only_ is set.
const std::string & dir() const
void set_modified(const std::string &name, const std::time_t &modified)
config & get(const std::string &name)
void delete_old_auto_saves(const int autosavemax, const int infinite_auto_saves)
Delete autosaves that are no longer needed (according to the autosave policy in the preferences).
std::map< std::string, std::time_t > modified_
void delete_game(const std::string &name)
Delete a savegame, including deleting the underlying file.
void rebuild(const std::string &name)
save_index_class(const std::string &dir)
Constructor for a read-only instance.
bool clean_up_index_
Flag to only run the clean_up_index method once.
static void fix_leader_image_path(config &data)
Filename and modification date for a file list.
const std::string & name() const
const config & summary() const
std::string format_time_local() const
std::string format_time_summary() const
std::shared_ptr< save_index_class > save_index_
const std::time_t & modified() const
static std::string get_side_color_id_from_config(const config &cfg)
Definitions for the interface to Wesnoth Markup Language (WML).
Declarations for File-IO.
static std::string _(const char *str)
Standard logging facilities (interface).
#define log_scope(description)
std::time_t file_modified_time(const std::string &fname)
Get the modification time of a file.
bool is_bzip2_file(const std::string &filename)
Returns true if the file ends with '.bz2'.
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs, name_mode mode, filter_mode filter, reorder_mode reorder, file_tree_checksum *checksum)
Get a list of all files and/or directories in a given directory.
bool delete_file(const std::string &filename)
bool is_gzip_file(const std::string &filename)
Returns true if the file ends with '.gz'.
static bool file_exists(const bfs::path &fpath)
std::string get_saves_dir()
std::string get_save_index_file()
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
std::unique_ptr< std::istream > scoped_istream
std::unique_ptr< std::ostream > scoped_ostream
utils::optional< std::string > get_independent_binary_file_path(const std::string &type, const std::string &filename)
Returns an asset path to filename for binary path-independent use in saved games.
void extract_summary_from_config(config &, config &)
static filesystem::scoped_istream find_save_file(const std::string &dir, const std::string &name, const std::vector< std::string > &suffixes)
void read_save_file(const std::string &dir, const std::string &name, config &cfg, std::string *error_log)
Read the complete config information out of a savefile.
std::string strftime(const std::string &format, const std::tm *time)
void erase_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::remove_if on a container.
std::string format_time_summary(std::time_t t)
std::string::const_iterator iterator
static lg::log_domain log_engine("engine")
static lg::log_domain log_enginerefac("enginerefac")
void write_gz(std::ostream &out, const configr_of &cfg)
void read(config &cfg, std::istream &in, abstract_validator *validator)
void read_bz2(config &cfg, std::istream &file, abstract_validator *validator)
Might throw a std::ios_base::failure especially bzip2_error.
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
void read_gz(config &cfg, std::istream &file, abstract_validator *validator)
Might throw a std::ios_base::failure especially a gzip_error.
std::string filename
Filename.
An exception object used when an IO error occurs.
Error used when game loading fails.
A structure for comparing to save_info objects based on their modified time.
bool operator()(const save_info &a, const save_info &b) const