30 #include <boost/algorithm/string/replace.hpp> 31 #include <boost/iostreams/filter/gzip.hpp> 34 #define LOG_SAVE LOG_STREAM(info, log_engine) 35 #define ERR_SAVE LOG_STREAM(err, log_engine) 38 #define LOG_RG LOG_STREAM(info, log_enginerefac) 63 summary[
"corrupt"] =
true;
66 summary[
"mod_time"] = std::to_string(static_cast<int>(modified));
104 std::vector<std::string> filenames;
110 return std::find(filenames.begin(), filenames.end(), d[
"save"]) == filenames.end();
121 LOG_SAVE <<
"no-op: read_only instance";
140 ERR_SAVE <<
"error writing to save index file: '" << e.
what() <<
"'";
183 }
catch(
const boost::iostreams::gzip_error&) {
188 ERR_SAVE <<
"error reading save index: '" << e.
what() <<
"'";
203 std::string leader_image = leader[
"leader_image"];
204 boost::algorithm::replace_all(leader_image,
"\\",
"/");
206 leader[
"leader_image"] = leader_image;
221 std::vector<std::string> filenames;
224 const auto should_remove = [filter](
const std::string& filename) {
227 static const std::vector<std::string> to_ignore {
"steam_autocloud.vdf"};
229 if(std::find(to_ignore.begin(), to_ignore.end(), filename) != to_ignore.end()) {
232 return filename.end() == std::search(filename.begin(), filename.end(), filter->begin(), filter->end());
238 filenames.erase(std::remove_if(filenames.begin(), filenames.end(), should_remove), filenames.end());
240 std::vector<save_info> result;
241 std::transform(filenames.begin(), filenames.end(), std::back_inserter(result), creator);
249 return save_index_->
get(name());
254 if(std::tm* tm_l = std::localtime(&modified())) {
257 ?
_(
"%a %b %d %Y, %I:%M %p")
259 :
_(
"%a %b %d %Y, %H:%M");
264 LOG_SAVE <<
"localtime() returned null for time " << this->modified() <<
", save " << name();
270 std::time_t
t = modified();
279 const std::string replay_str =
" " +
_(
"replay");
284 }
else if(a.
name().find(replay_str) == std::string::npos && b.
name().find(replay_str) != std::string::npos) {
291 }
else if(a.
name().find(replay_str) != std::string::npos && b.
name().find(replay_str) == std::string::npos) {
299 const std::string& name,
const std::vector<std::string>& suffixes)
301 for(
const std::string& suf : suffixes) {
305 if(!file_stream->fail()) {
310 LOG_SAVE <<
"Could not open supplied filename '" << name <<
"'";
316 static const std::vector<std::string> suffixes{
"",
".gz",
".bz2"};
330 read(cfg, *file_stream);
332 }
catch(
const std::ios_base::failure&
e) {
336 *error_log += e.what();
350 LOG_SAVE <<
"Could not parse file data into config";
359 LOG_SAVE <<
"no-op: read_only instance";
363 const std::string auto_save =
_(
"Auto-Save");
366 if(countdown == infinite_auto_saves) {
372 if(countdown-- <= 0) {
373 LOG_SAVE <<
"Deleting savegame '" <<
i->name() <<
"'";
383 LOG_SAVE <<
"no-op: read_only instance";
399 manager_->set_modified(filename, modified);
405 const config& cfg_snapshot = cfg_save.
child(
"snapshot");
408 const config& cfg_replay_start = cfg_save.
child(
"replay_start")
409 ? cfg_save.
child(
"replay_start")
410 : cfg_save.
child(
"scenario");
412 const config& cfg_replay = cfg_save.
child(
"replay");
413 const bool has_replay = cfg_replay && !cfg_replay.
empty();
414 const bool has_snapshot = cfg_snapshot && cfg_snapshot.
has_child(
"side");
416 cfg_summary[
"replay"] = has_replay;
417 cfg_summary[
"snapshot"] = has_snapshot;
419 cfg_summary[
"label"] = cfg_save[
"label"];
420 cfg_summary[
"campaign_type"] = cfg_save[
"campaign_type"];
422 if(cfg_save.
has_child(
"carryover_sides_start")) {
423 cfg_summary[
"scenario"] = cfg_save.
child(
"carryover_sides_start")[
"next_scenario"];
425 cfg_summary[
"scenario"] = cfg_save[
"scenario"];
428 cfg_summary[
"difficulty"] = cfg_save[
"difficulty"];
429 cfg_summary[
"random_mode"] = cfg_save[
"random_mode"];
431 cfg_summary[
"active_mods"] = cfg_save.
child_or_empty(
"multiplayer")[
"active_mods"];
432 cfg_summary[
"campaign"] = cfg_save[
"campaign"];
433 cfg_summary[
"version"] = cfg_save[
"version"];
434 cfg_summary[
"corrupt"] =
"";
437 cfg_summary[
"turn"] = cfg_snapshot[
"turn_at"];
438 if(cfg_snapshot[
"turns"] !=
"-1") {
439 cfg_summary[
"turn"] = cfg_summary[
"turn"].str() +
"/" + cfg_snapshot[
"turns"].str();
449 bool shrouded =
false;
451 if(
const config& snapshot = *(has_snapshot ? &cfg_snapshot : &cfg_replay_start)) {
454 std::string leader_image;
455 std::string leader_image_tc_modifier;
456 std::string leader_name;
457 int gold = side[
"gold"];
458 int units = 0, recall_units = 0;
460 if(side[
"controller"] != side_controller::human) {
464 if(side[
"shroud"].to_bool()) {
469 if(u.has_attribute(
"x") && u.has_attribute(
"y")) {
476 if(!leader.empty() || !u[
"canrecruit"].to_bool()) {
484 leader = u[
"id"].str();
485 leader_name = u[
"name"].str();
486 leader_image = u[
"image"].str();
487 leader_image_tc_modifier =
"~RC(" + u[
"flag_rgb"].str() +
">" + tc_color +
")";
498 if(!leader_image_path.empty()) {
499 leader_image_path += leader_image_tc_modifier;
501 leader_image = leader_image_path;
504 leader_config[
"leader"] = leader;
505 leader_config[
"leader_name"] = leader_name;
506 leader_config[
"leader_image"] = leader_image;
507 leader_config[
"leader_image_tc_modifier"] = leader_image_tc_modifier;
508 leader_config[
"gold"] = gold;
509 leader_config[
"units"] = units;
510 leader_config[
"recall_units"] = recall_units;
512 cfg_summary.
add_child(
"leader", leader_config);
519 cfg_summary[
"map_data"] = cfg_snapshot[
"map_data"].str();
521 ERR_SAVE <<
"Not saving map because there is shroud";
523 }
else if(has_replay) {
524 if(!cfg_replay_start.find_child(
"side",
"shroud",
"yes") && cfg_replay_start.has_attribute(
"map_data")) {
525 cfg_summary[
"map_data"] = cfg_replay_start[
"map_data"];
527 ERR_SAVE <<
"Not saving map because there is shroud";
bool clean_up_index_
Flag to only run the clean_up_index method once.
bool empty() const
Tests for an attribute that either was never set or was set to "".
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
void delete_game(const std::string &name)
Delete a savegame, including deleting the underlying file.
save_index_class(const std::string &dir)
Constructor for a read-only instance.
void rebuild(const std::string &name)
void clear_children(T... keys)
bool delete_file(const std::string &filename)
static std::string get_side_color_id_from_config(const config &cfg)
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.
Variant for storing WML attributes.
bool operator()(const save_info &a, const save_info &b) const
void extract_summary_from_config(config &, config &)
void read_bz2(config &cfg, std::istream &file, abstract_validator *validator)
Might throw a std::ios_base::failure especially bzip2_error.
Error used when game loading fails.
bool has_attribute(config_key_type key) const
static std::shared_ptr< save_index_class > default_saves_dir()
Returns an instance for managing saves in filesystem::get_saves_dir()
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
void remove(const std::string &name)
Delete a savegame from the index, without deleting the underlying file.
static bool file_exists(const bfs::path &fpath)
void write_save_index()
Sync to disk, no-op if read_only_ is set.
child_itors child_range(config_key_type key)
static lg::log_domain log_engine("engine")
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
const attribute_value * get(config_key_type key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
std::vector< save_info > get_saves_list(const std::string *filter=nullptr)
Get a list of available saves.
void set_modified(const std::string &name, const std::time_t &modified)
Contains the exception interfaces used to signal completion of a scenario, campaign or turn...
void read_gz(config &cfg, std::istream &file, abstract_validator *validator)
Might throw a std::ios_base::failure especially a gzip_error.
static std::string _(const char *str)
std::string get_saves_dir()
Definitions for the interface to Wesnoth Markup Language (WML).
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.
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
const config & summary() const
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. ...
std::shared_ptr< save_index_class > manager_
std::string strftime(const std::string &format, const std::tm *time)
std::string format_time_local() const
create_for_default_saves_dir
Syntatic sugar for choosing which constructor to use.
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
void clean_up_index()
Deletes non-existent save files from the index.
void write_gz(std::ostream &out, const configr_of &cfg)
void read(config &cfg, std::istream &in, abstract_validator *validator)
bool read_only_
The instance for default_saves_dir() writes a cache file.
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.
unsigned all_children_count() const
std::unique_ptr< std::istream > scoped_istream
std::time_t to_time_t(std::time_t def=0) const
const char * what() const noexcept
const std::string & name() const
static filesystem::scoped_istream find_save_file(const std::string &dir, const std::string &name, const std::vector< std::string > &suffixes)
const std::time_t & modified() const
std::unique_ptr< std::ostream > scoped_ostream
bool is_gzip_file(const std::string &filename)
Returns true if the file ends with '.gz'.
config & get(const std::string &name)
const std::string & dir() const
An exception object used when an IO error occurs.
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)...
#define log_scope(description)
static void fix_leader_image_path(config &data)
Declarations for File-IO.
save_info operator()(const std::string &filename) const
config & add_child(config_key_type key)
compression::format save_compression_format()
bool is_bzip2_file(const std::string &filename)
Returns true if the file ends with '.bz2'.
Filename and modification date for a file list.
create_save_info(const std::shared_ptr< save_index_class > &)
std::time_t file_modified_time(const std::string &fname)
Get the modification time of a file.
std::map< std::string, std::time_t > modified_
std::string format_time_summary() const
Standard logging facilities (interface).
void remove_children(config_key_type key, std::function< bool(const config &)> p=[](config){return true;})
Removes all children with tag key for which p returns true.
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.
A config object defines a single node in a WML file, with access to child nodes.
static lg::log_domain log_enginerefac("enginerefac")
std::string get_save_index_file()
A structure for comparing to save_info objects based on their modified time.
std::string format_time_summary(std::time_t t)
bool use_twelve_hour_clock_format()
std::string::const_iterator iterator