save_index refactoring patch #3125 by Oleg Tsarev

This commit is contained in:
Jérémy Rosen 2012-02-18 17:57:20 +00:00
parent e310f2e87e
commit 551946d6c7
7 changed files with 256 additions and 339 deletions

View file

@ -272,13 +272,13 @@ namespace {
class delete_save : public gui::dialog_button_action
{
public:
delete_save(display& disp, gui::filter_textbox& filter, std::vector<savegame::save_info>& saves, std::vector<config*>& save_summaries) : disp_(disp), saves_(saves), summaries_(save_summaries), filter_(filter) {}
delete_save(display& disp, gui::filter_textbox& filter, std::vector<savegame::save_info>& saves) :
disp_(disp), saves_(saves), filter_(filter) {}
private:
gui::dialog_button_action::RESULT button_pressed(int menu_selection);
display& disp_;
std::vector<savegame::save_info>& saves_;
std::vector<config*>& summaries_;
gui::filter_textbox& filter_;
};
@ -298,15 +298,11 @@ gui::dialog_button_action::RESULT delete_save::button_pressed(int menu_selection
filter_.delete_item(menu_selection);
// Delete the file
savegame::manager::delete_game(saves_[index].name);
savegame::delete_game(saves_[index].name());
// Remove it from the list of saves
saves_.erase(saves_.begin() + index);
if(index < summaries_.size()) {
summaries_.erase(summaries_.begin() + index);
}
return gui::DELETE_ITEM;
} else {
return gui::CONTINUE_DIALOG;
@ -320,12 +316,10 @@ class save_preview_pane : public gui::preview_pane
public:
save_preview_pane(CVideo &video, const config& game_config, gamemap* map,
const std::vector<savegame::save_info>& info,
const std::vector<config*>& summaries,
const gui::filter_textbox& textbox) :
gui::preview_pane(video),
game_config_(&game_config),
map_(map), info_(&info),
summaries_(summaries),
index_(0),
map_cache_(),
textbox_(textbox)
@ -346,7 +340,6 @@ private:
const config* game_config_;
gamemap* map_;
const std::vector<savegame::save_info>* info_;
const std::vector<config*>& summaries_;
int index_;
std::map<std::string,surface> map_cache_;
const gui::filter_textbox& textbox_;
@ -354,21 +347,6 @@ private:
void save_preview_pane::draw_contents()
{
if (size_t(index_) >= summaries_.size() || info_->size() != summaries_.size()) {
return;
}
std::string dummy;
const config &summary = *summaries_[index_];
if (summary["label"].empty())
{
try {
savegame::manager::load_summary((*info_)[index_].name, *summaries_[index_], &dummy);
} catch(game::load_game_failed&) {
(*summaries_[index_])["corrupt"] = true;
}
}
surface screen = video().getSurface();
SDL_Rect const &loc = location();
@ -382,6 +360,7 @@ void save_preview_pane::draw_contents()
int ypos = area.y;
bool have_leader_image = false;
const config& summary = ((*info_)[index_]).summary();
const std::string& leader_image = summary["leader_image"].str();
if(!leader_image.empty() && image::exists(leader_image))
@ -457,7 +436,7 @@ void save_preview_pane::draw_contents()
char time_buf[256] = {0};
const savegame::save_info& save = (*info_)[index_];
tm* tm_l = localtime(&save.time_modified);
tm* tm_l = localtime(&save.modified());
if (tm_l) {
const size_t res = strftime(time_buf,sizeof(time_buf),
(preferences::use_twelve_hour_clock_format() ? _("%a %b %d %I:%M %p %Y") : _("%a %b %d %H:%M %Y")),
@ -466,13 +445,13 @@ void save_preview_pane::draw_contents()
time_buf[0] = 0;
}
} else {
LOG_NG << "localtime() returned null for time " << save.time_modified << ", save " << save.name;
LOG_NG << "localtime() returned null for time " << save.modified() << ", save " << save.name();
}
std::stringstream str;
str << font::BOLD_TEXT << font::NULL_MARKUP
<< (*info_)[index_].name << '\n' << time_buf;
<< (*info_)[index_].name() << '\n' << time_buf;
const std::string& campaign_type = summary["campaign_type"];
if (summary["corrupt"].to_bool()) {
@ -591,7 +570,7 @@ std::string load_game_dialog(display& disp, const config& game_config, bool* sel
std::vector<savegame::save_info> games;
{
cursor::setter cur(cursor::WAIT);
games = savegame::manager::get_saves_list();
games = savegame::get_saves_list();
}
if(games.empty()) {
@ -601,29 +580,19 @@ std::string load_game_dialog(display& disp, const config& game_config, bool* sel
return "";
}
std::vector<config*> summaries;
std::vector<savegame::save_info>::const_iterator i;
//FIXME: parent_to_child is not used yet
std::map<std::string,std::string> parent_to_child;
for(i = games.begin(); i != games.end(); ++i) {
config& cfg = savegame::save_index::save_summary(i->name);
parent_to_child[cfg["parent"]] = i->name;
summaries.push_back(&cfg);
}
const events::event_context context;
std::vector<std::string> items;
std::ostringstream heading;
heading << HEADING_PREFIX << _("Name") << COLUMN_SEPARATOR << _("Date");
items.push_back(heading.str());
std::vector<savegame::save_info>::const_iterator i;
for(i = games.begin(); i != games.end(); ++i) {
std::string name = i->name;
std::string name = i->name();
utils::truncate_as_wstring(name, std::min<size_t>(name.size(), 40));
std::ostringstream str;
str << name << COLUMN_SEPARATOR << format_time_summary(i->time_modified);
str << name << COLUMN_SEPARATOR << format_time_summary(i->modified());
items.push_back(str.str());
}
@ -643,7 +612,7 @@ std::string load_game_dialog(display& disp, const config& game_config, bool* sel
gui::filter_textbox* filter = new gui::filter_textbox(disp.video(), _("Filter: "), items, items, 1, lmenu);
lmenu.set_textbox(filter);
save_preview_pane save_preview(disp.video(),game_config,&map_obj,games,summaries,*filter);
save_preview_pane save_preview(disp.video(),game_config,&map_obj,games,*filter);
lmenu.add_pane(&save_preview);
// create an option for whether the replay should be shown or not
if(show_replay != NULL) {
@ -671,7 +640,7 @@ std::string load_game_dialog(display& disp, const config& game_config, bool* sel
lmenu.add_button(new gui::standard_dialog_button(disp.video(),_("OK"),0,false), gui::dialog::BUTTON_STANDARD);
lmenu.add_button(new gui::standard_dialog_button(disp.video(),_("Cancel"),1,true), gui::dialog::BUTTON_STANDARD);
delete_save save_deleter(disp,*filter,games,summaries);
delete_save save_deleter(disp,*filter,games);
gui::dialog_button_info delete_button(&save_deleter,_("Delete Save"));
lmenu.add_button(delete_button,
@ -681,8 +650,6 @@ std::string load_game_dialog(display& disp, const config& game_config, bool* sel
int res = lmenu.show();
savegame::save_index::write_save_index();
if(res == -1)
return "";
@ -691,7 +658,7 @@ std::string load_game_dialog(display& disp, const config& game_config, bool* sel
if(show_replay != NULL) {
*show_replay = lmenu.option_checked(option_index++);
const config& summary = *summaries[res];
const config& summary = games[res].summary();
if (summary["replay"].to_bool() && !summary["snapshot"].to_bool(true)) {
*show_replay = true;
}
@ -703,7 +670,7 @@ std::string load_game_dialog(display& disp, const config& game_config, bool* sel
*select_difficulty = lmenu.option_checked(option_index++);
}
return games[res].name;
return games[res].name();
}
namespace {

View file

@ -83,7 +83,7 @@ void tdata_manage::pre_show(CVideo& /*video*/, twindow& window)
{
cursor::setter cur(cursor::WAIT);
games_ = savegame::manager::get_saves_list();
games_ = savegame::get_saves_list();
}
fill_game_list(window, games_);
@ -105,7 +105,7 @@ void tdata_manage::fill_game_list(twindow& window
std::map<std::string, string_map> data;
string_map item;
item["label"] = game.name;
item["label"] = game.name();
data.insert(std::make_pair("filename", item));
item["label"] = game.format_time_summary();
@ -179,7 +179,7 @@ void tdata_manage::delete_button_callback(twindow& window)
}
// Delete the file
savegame::manager::delete_game(games_[index].name);
savegame::delete_game(games_[index].name());
// Remove it from the list of saves
games_.erase(games_.begin() + index);

View file

@ -133,7 +133,7 @@ void tgame_load::pre_show(CVideo& /*video*/, twindow& window)
{
cursor::setter cur(cursor::WAIT);
games_ = savegame::manager::get_saves_list();
games_ = savegame::get_saves_list();
}
fill_game_list(window, games_);
@ -157,7 +157,7 @@ void tgame_load::fill_game_list(twindow& window
std::map<std::string, string_map> data;
string_map item;
item["label"] = game.name;
item["label"] = game.name();
data.insert(std::make_pair("filename", item));
item["label"] = game.format_time_summary();
@ -239,28 +239,21 @@ void tgame_load::display_savegame(twindow& window)
preview_pane.set_visible(twidget::VISIBLE);
savegame::save_info& game = games_[selected_row];
filename_ = game.name;
filename_ = game.name();
config cfg_summary;
std::string dummy;
try {
savegame::manager::load_summary(game.name, cfg_summary, &dummy);
} catch(game::load_game_failed&) {
cfg_summary["corrupt"] = "yes";
}
const config& summary = game.summary();
find_widget<timage>(&window, "imgLeader", false).
set_label(cfg_summary["leader_image"]);
set_label(summary["leader_image"]);
find_widget<tminimap>(&window, "minimap", false).
set_map_data(cfg_summary["map_data"]);
set_map_data(summary["map_data"]);
find_widget<tlabel>(&window, "lblScenario", false).set_label(game.name);
find_widget<tlabel>(&window, "lblScenario", false).set_label(game.name());
std::stringstream str;
str << game.format_time_local();
evaluate_summary_string(str, cfg_summary);
evaluate_summary_string(str, summary);
find_widget<tlabel>(&window, "lblSummary", false).set_label(str.str());
@ -344,7 +337,7 @@ void tgame_load::delete_button_callback(twindow& window)
}
// Delete the file
savegame::manager::delete_game(games_[index].name);
savegame::delete_game(games_[index].name());
// Remove it from the list of saves
games_.erase(games_.begin() + index);

View file

@ -1103,7 +1103,7 @@ void play_controller::expand_autosaves(std::vector<std::string>& items)
std::vector<std::string> newsaves;
for (unsigned int turn = this->turn(); turn != 0; turn--) {
std::string name = gamestate_.classification().label + "-" + _("Auto-Save") + lexical_cast<std::string>(turn);
if (savegame::manager::save_game_exists(name, preferences::compress_saves())) {
if (savegame::save_game_exists(name, preferences::compress_saves())) {
if(preferences::compress_saves()) {
newsaves.push_back(name + ".gz");
} else {
@ -1114,7 +1114,7 @@ void play_controller::expand_autosaves(std::vector<std::string>& items)
}
const std::string& start_name = gamestate_.classification().label;
if(savegame::manager::save_game_exists(start_name, preferences::compress_saves())) {
if(savegame::save_game_exists(start_name, preferences::compress_saves())) {
if(preferences::compress_saves()) {
newsaves.push_back(start_name + ".gz");
} else {

View file

@ -396,7 +396,7 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
// need to change this test.
if (res == VICTORY || (io_type != IO_NONE && res == DEFEAT)) {
if (preferences::delete_saves())
savegame::manager::clean_saves(gamestate.classification().label);
savegame::clean_saves(gamestate.classification().label);
if (preferences::save_replays() && end_level.replay_save) {
savegame::replay_savegame save(gamestate, preferences::compress_saves());
@ -606,7 +606,7 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
if (gamestate.classification().campaign_type == "scenario"){
if (preferences::delete_saves())
savegame::manager::clean_saves(gamestate.classification().label);
savegame::clean_saves(gamestate.classification().label);
}
return VICTORY;
}

View file

@ -132,9 +132,168 @@ static lg::log_domain log_engine("engine");
namespace savegame {
class save_index_class
{
public:
void rebuild(const std::string& name) {
std::string filename = name;
replace_space2underbar(filename);
time_t modified = file_create_time(get_saves_dir() + "/" + filename);
rebuild(name, modified);
}
void rebuild(const std::string& name, const time_t& modified) {
log_scope("load_summary_from_file");
config& summary = data(name);
try {
config full;
std::string dummy;
read_save_file(name, full, &dummy);
::extract_summary_from_config(full, summary);
} catch(game::load_game_failed&) {
summary["corrupt"] = true;
}
summary["mod_time"] = str_cast(static_cast<int>(modified));
write_save_index();
}
void set_modified(const std::string& name, const time_t& modified) {
modified_[name] = modified;
}
config& get(const std::string& name) {
config& result = data(name);
time_t m = modified_[name];
config::attribute_value& mod_time = result["mod_time"];
if (mod_time.empty() || static_cast<time_t>(mod_time.to_int()) != m) {
rebuild(name, m);
}
return result;
}
public:
void write_save_index() {
log_scope("write_save_index()");
try {
scoped_ostream stream = ostream_file(get_save_index_file());
if (preferences::compress_saves()) {
write_gz(*stream, data());
} else {
write(*stream, data());
}
} catch(io_exception& e) {
ERR_SAVE << "error writing to save index file: '" << e.what() << "'\n";
}
}
public:
save_index_class() : loaded_(false) {}
private:
config& data(const std::string& name) {
std::string save = name;
/*
* All saves are .gz files now so make sure we use that name when opening
* a file. If not some parts of the code use the name with and some parts
* without the .gz suffix.
*/
if(save.length() < 3 || save.substr(save.length() - 3) != ".gz") {
save += ".gz";
}
config& cfg = data();
if (config& sv = cfg.find_child("save", "save", save)) {
return sv;
}
config& res = cfg.add_child("save");
res["save"] = save;
return res;
}
config& data() {
if(loaded_ == false) {
try {
scoped_istream stream = istream_file(get_save_index_file());
try {
read_gz(data_, *stream);
} catch (boost::iostreams::gzip_error&) {
stream->seekg(0);
read(data_, *stream);
}
} catch(io_exception& e) {
ERR_SAVE << "error reading save index: '" << e.what() << "'\n";
} catch(config::error&) {
ERR_SAVE << "error parsing save index config file\n";
data_.clear();
}
loaded_ = true;
}
return data_;
}
private:
bool loaded_;
config data_;
std::map< std::string, time_t > modified_;
} save_index_manager;
class filename_filter {
public:
filename_filter(const std::string& filter) : filter_(filter) {
}
bool operator()(const std::string& filename) const {
return filename.end() == std::search(filename.begin(), filename.end(),
filter_.begin(), filter_.end());
}
private:
std::string filter_;
};
class create_save_info {
public:
create_save_info(const std::string* d = NULL) : dir(d ? *d : get_saves_dir()) {
}
save_info operator()(const std::string& filename) const {
std::string name = filename;
replace_underbar2space(name);
time_t modified = file_create_time(dir + "/" + filename);
save_index_manager.set_modified(name, modified);
return save_info(name, modified);
}
const std::string dir;
};
/** Get a list of available saves. */
std::vector<save_info> get_saves_list(const std::string* dir, const std::string* filter)
{
create_save_info creator(dir);
std::vector<std::string> filenames;
get_files_in_dir(creator.dir,&filenames);
if (filter) {
filenames.erase(std::remove_if(filenames.begin(), filenames.end(),
filename_filter(*filter)));
}
std::vector<save_info> result;
std::transform(filenames.begin(), filenames.end(),
std::back_inserter(result), creator);
return result;
}
save_info::save_info(const std::string& n, const time_t& m) : name_(n), modified_(m) {
}
const std::string& save_info::name() const {
return name_;
}
const time_t& save_info::modified() const {
return modified_;
}
const config& save_info::summary() const {
return save_index_manager.get(name());
}
const std::string save_info::format_time_local() const{
char time_buf[256] = {0};
tm* tm_l = localtime(&time_modified);
tm* tm_l = localtime(&modified());
if (tm_l) {
const size_t res = strftime(time_buf,sizeof(time_buf),
(preferences::use_twelve_hour_clock_format() ? _("%a %b %d %I:%M %p %Y") : _("%a %b %d %H:%M %Y")),
@ -143,7 +302,7 @@ const std::string save_info::format_time_local() const{
time_buf[0] = 0;
}
} else {
LOG_SAVE << "localtime() returned null for time " << time_modified << ", save " << name;
LOG_SAVE << "localtime() returned null for time " << this->modified() << ", save " << name();
}
return time_buf;
@ -151,7 +310,7 @@ const std::string save_info::format_time_local() const{
const std::string save_info::format_time_summary() const
{
time_t t = time_modified;
time_t t = modified();
time_t curtime = time(NULL);
const struct tm* timeptr = localtime(&curtime);
if(timeptr == NULL) {
@ -205,32 +364,26 @@ const std::string save_info::format_time_summary() const
return buf;
}
/**
* A structure for comparing to save_info objects based on their modified time.
* If the times are equal, will order based on the name.
*/
struct save_info_less_time {
bool operator()(const save_info& a, const save_info& b) const {
if (a.time_modified > b.time_modified) {
return true;
} else if (a.time_modified < b.time_modified) {
return false;
bool save_info_less_time::operator() (const save_info& a, const save_info& b) const {
if (a.modified() > b.modified()) {
return true;
} else if (a.modified() < b.modified()) {
return false;
// Special funky case; for files created in the same second,
// a replay file sorts less than a non-replay file. Prevents
// a timing-dependent bug where it may look like, at the end
// of a scenario, the replay and the autosave for the next
// scenario are displayed in the wrong order.
} else if (a.name.find(_(" replay"))==std::string::npos && b.name.find(_(" replay"))!=std::string::npos) {
return true;
} else if (a.name.find(_(" replay"))!=std::string::npos && b.name.find(_(" replay"))==std::string::npos) {
return false;
} else {
return a.name > b.name;
}
} else if (a.name().find(_(" replay"))==std::string::npos && b.name().find(_(" replay"))!=std::string::npos) {
return true;
} else if (a.name().find(_(" replay"))!=std::string::npos && b.name().find(_(" replay"))==std::string::npos) {
return false;
} else {
return a.name() > b.name();
}
};
}
void manager::read_save_file(const std::string& name, config& cfg, std::string* error_log)
void read_save_file(const std::string& name, config& cfg, std::string* error_log)
{
std::string modified_name = name;
replace_space2underbar(modified_name);
@ -272,16 +425,7 @@ void manager::read_save_file(const std::string& name, config& cfg, std::string*
}
}
void manager::load_summary(const std::string& name, config& cfg_summary, std::string* error_log){
log_scope("load_game_summary");
config cfg;
read_save_file(name,cfg,error_log);
::extract_summary_from_config(cfg, cfg_summary);
}
bool manager::save_game_exists(const std::string& name, const bool compress_saves)
bool save_game_exists(const std::string& name, bool compress_saves)
{
std::string fname = name;
replace_space2underbar(fname);
@ -293,61 +437,36 @@ bool manager::save_game_exists(const std::string& name, const bool compress_save
return file_exists(get_saves_dir() + "/" + fname);
}
std::vector<save_info> manager::get_saves_list(const std::string *dir, const std::string* filter)
{
// Don't use a reference, it seems to break on arklinux with GCC-4.3.
const std::string saves_dir = (dir) ? *dir : get_saves_dir();
std::vector<std::string> saves;
get_files_in_dir(saves_dir,&saves);
std::vector<save_info> res;
for(std::vector<std::string>::iterator i = saves.begin(); i != saves.end(); ++i) {
if(filter && std::search(i->begin(), i->end(), filter->begin(), filter->end()) == i->end()) {
continue;
}
const time_t modified = file_create_time(saves_dir + "/" + *i);
replace_underbar2space(*i);
res.push_back(save_info(*i,modified));
}
std::sort(res.begin(),res.end(),save_info_less_time());
return res;
}
void manager::clean_saves(const std::string &label)
void clean_saves(const std::string& label)
{
std::vector<save_info> games = get_saves_list();
std::string prefix = label + "-" + _("Auto-Save");
std::cerr << "Cleaning saves with prefix '" << prefix << "'\n";
for (std::vector<save_info>::iterator i = games.begin(); i != games.end(); ++i) {
if (i->name.compare(0, prefix.length(), prefix) == 0) {
std::cerr << "Deleting savegame '" << i->name << "'\n";
delete_game(i->name);
if (i->name().compare(0, prefix.length(), prefix) == 0) {
std::cerr << "Deleting savegame '" << i->name() << "'\n";
delete_game(i->name());
}
}
}
void manager::remove_old_auto_saves(const int autosavemax, const int infinite_auto_saves)
void remove_old_auto_saves(const int autosavemax, const int infinite_auto_saves)
{
const std::string auto_save = _("Auto-Save");
int countdown = autosavemax;
if (countdown == infinite_auto_saves)
return;
std::vector<save_info> games = get_saves_list(NULL, &auto_save);
std::vector<save_info> games = get_saves_list(&auto_save);
for (std::vector<save_info>::iterator i = games.begin(); i != games.end(); ++i) {
if (countdown-- <= 0) {
LOG_SAVE << "Deleting savegame '" << i->name << "'\n";
delete_game(i->name);
LOG_SAVE << "Deleting savegame '" << i->name() << "'\n";
delete_game(i->name());
}
}
}
void manager::delete_game(const std::string& name)
void delete_game(const std::string& name)
{
std::string modified_name = name;
replace_space2underbar(modified_name);
@ -356,68 +475,6 @@ void manager::delete_game(const std::string& name)
remove((get_saves_dir() + "/" + modified_name).c_str());
}
bool save_index::save_index_loaded = false;
config save_index::save_index_cfg;
config& save_index::load()
{
if(save_index_loaded == false) {
const std::string filename = get_save_index_file();
try {
scoped_istream stream = istream_file(filename);
try {
read_gz(save_index_cfg, *stream);
} catch (boost::iostreams::gzip_error&) {
stream->seekg(0);
read(save_index_cfg, *stream);
}
} catch(io_exception& e) {
ERR_SAVE << "error reading save index: '" << e.what() << "'\n";
} catch(config::error&) {
ERR_SAVE << "error parsing save index config file\n";
save_index_cfg.clear();
}
save_index_loaded = true;
}
return save_index_cfg;
}
config& save_index::save_summary(std::string save)
{
/*
* All saves are .gz files now so make sure we use that name when opening
* a file. If not some parts of the code use the name with and some parts
* without the .gz suffix.
*/
if(save.length() < 3 || save.substr(save.length() - 3) != ".gz") {
save += ".gz";
}
config& cfg = load();
if (config &sv = cfg.find_child("save", "save", save))
return sv;
config &res = cfg.add_child("save");
res["save"] = save;
return res;
}
void save_index::write_save_index()
{
log_scope("write_save_index()");
try {
scoped_ostream stream = ostream_file(get_save_index_file());
if (preferences::compress_saves()) {
write_gz(*stream, load());
} else {
write(*stream, load());
}
} catch(io_exception& e) {
ERR_SAVE << "error writing to save index file: '" << e.what() << "'\n";
}
}
loadgame::loadgame(display& gui, const config& game_config, game_state& gamestate)
: game_config_(game_config)
, gui_(gui)
@ -456,14 +513,9 @@ void loadgame::show_dialog(bool show_replay, bool cancel_orders)
void loadgame::show_difficulty_dialog()
{
config cfg_summary;
std::string dummy;
try {
manager::load_summary(filename_, cfg_summary, &dummy);
} catch(game::load_game_failed&) {
cfg_summary["corrupt"] = "yes";
}
create_save_info creator;
save_info info = creator(filename_);
const config& cfg_summary = info.summary();
if ( cfg_summary["corrupt"].to_bool() || (cfg_summary["replay"].to_bool() && !cfg_summary["snapshot"].to_bool(true))
|| (!cfg_summary["turn"].empty()) )
@ -540,7 +592,7 @@ void loadgame::load_game(
show_difficulty_dialog();
std::string error_log;
manager::read_save_file(filename_, load_config_, &error_log);
read_save_file(filename_, load_config_, &error_log);
if(!error_log.empty()) {
try {
@ -637,7 +689,7 @@ void loadgame::load_multiplayer_game()
cursor::setter cur(cursor::WAIT);
log_scope("load_game");
manager::read_save_file(filename_, load_config_, &error_log);
read_save_file(filename_, load_config_, &error_log);
copy_era(load_config_);
gamestate_ = game_state(load_config_);
@ -800,7 +852,7 @@ int savegame::show_save_dialog(CVideo& video, const std::string& message, const
bool savegame::check_overwrite(CVideo& video)
{
std::string filename = filename_;
if (manager::save_game_exists(filename, compress_saves_)) {
if (save_game_exists(filename, compress_saves_)) {
std::stringstream message;
message << _("Save already exists. Do you want to overwrite it?") << "\n" << _("Name: ") << filename;
int retval = gui2::show_message(video, _("Overwrite?"), message.str(), gui2::tmessage::yes_no_buttons);
@ -932,20 +984,11 @@ void savegame::write_game(config_writer &out) const
void savegame::finish_save_game(const config_writer &out)
{
std::string name = gamestate_.classification().label;
replace_space2underbar(name);
std::string fname(get_saves_dir() + "/" + name);
try {
if(!out.good()) {
throw game::save_game_failed(_("Could not write to file"));
}
config& summary = save_index::save_summary(gamestate_.classification().label);
extract_summary_data_from_save(summary);
const int mod_time = static_cast<int>(file_create_time(fname));
summary["mod_time"] = str_cast(mod_time);
save_index::write_save_index();
save_index_manager.rebuild(gamestate_.classification().label);
} catch(io_exception& e) {
throw game::save_game_failed(e.what());
}
@ -964,72 +1007,6 @@ scoped_ostream savegame::open_save_game(const std::string &label)
}
}
void savegame::extract_summary_data_from_save(config& out)
{
const bool has_replay = gamestate_.replay_data.empty() == false;
bool has_snapshot(gamestate_.snapshot.child("side"));
out["replay"] = has_replay;
out["snapshot"] = has_snapshot;
out["label"] = gamestate_.classification().label;
out["parent"] = gamestate_.classification().parent;
out["campaign"] = gamestate_.classification().campaign;
out["campaign_type"] = gamestate_.classification().campaign_type;
out["scenario"] = gamestate_.classification().scenario;
out["difficulty"] = gamestate_.classification().difficulty;
out["version"] = gamestate_.classification().version;
out["corrupt"] = "";
if(has_snapshot) {
out["turn"] = gamestate_.snapshot["turn_at"];
if(gamestate_.snapshot["turns"] != "-1") {
out["turn"] = out["turn"].str() + "/" + gamestate_.snapshot["turns"].str();
}
}
// Find the first human leader so we can display their icon in the load menu.
/** @todo Ideally we should grab all leaders if there's more than 1 human player? */
std::string leader;
bool shrouded = false;
const config& snapshot = has_snapshot ? gamestate_.snapshot : gamestate_.starting_pos;
foreach (const config &side, snapshot.child_range("side"))
{
if (side["controller"] != "human") {
continue;
}
if (side["shroud"].to_bool()) {
shrouded = true;
}
foreach (const config &u, side.child_range("unit"))
{
if (u["canrecruit"].to_bool()) {
leader = u["id"].str();
break;
}
}
}
out["leader"] = leader;
out["map_data"] = "";
if(!shrouded) {
if(has_snapshot) {
if (!gamestate_.snapshot.find_child("side", "shroud", "yes")) {
out["map_data"] = gamestate_.snapshot["map_data"];
}
} else if(has_replay) {
if (!gamestate_.starting_pos.find_child("side", "shroud", "yes")) {
out["map_data"] = gamestate_.starting_pos["map_data"];
}
}
}
}
scenariostart_savegame::scenariostart_savegame(game_state &gamestate, const bool compress_saves)
: savegame(gamestate, compress_saves)
{
@ -1079,7 +1056,7 @@ void autosave_savegame::autosave(const bool disable_autosave, const int autosave
save_game_automatic(gui_.video());
manager::remove_old_auto_saves(autosave_max, infinite_autosaves);
remove_old_auto_saves(autosave_max, infinite_autosaves);
}
void autosave_savegame::create_filename()

View file

@ -31,66 +31,47 @@ struct illegal_filename_exception {};
namespace savegame {
/** Filename and modification date for a file list */
struct save_info {
save_info(const std::string& n, time_t t) : name(n), time_modified(t) {}
std::string name;
time_t time_modified;
class save_info {
private:
friend class create_save_info;
private:
save_info(const std::string& name, const time_t& modified);
public:
const std::string& name() const;
const time_t& modified() const;
public:
const std::string format_time_summary() const;
const std::string format_time_local() const;
const config& summary() const;
private:
std::string name_;
time_t modified_;
};
/**
Container for a couple of savefile manipulating methods.
Note: You are not supposed to instantiate this class.
*/
class manager
{
public:
/** Read summary information out of an existing savefile. */
static void load_summary(const std::string& name, config& cfg_summary, std::string* error_log);
/** Read the complete config information out of a savefile. */
static void read_save_file(const std::string& name, config& cfg, std::string* error_log);
/** Returns true if there is already a savegame with that name. */
static bool save_game_exists(const std::string& name, const bool compress_saves);
/** Get a list of available saves. */
static std::vector<save_info> get_saves_list(const std::string *dir = NULL, const std::string* filter = NULL);
/** Delete all autosaves of a certain scenario. */
static void clean_saves(const std::string &label);
/** Remove autosaves that are no longer needed (according to the autosave policy in the preferences). */
static void remove_old_auto_saves(const int autosavemax, const int infinite_auto_saves);
/** Delete a savegame. */
static void delete_game(const std::string& name);
private:
/** Default-Constructor (don't instantiate this class) */
manager() {}
* A structure for comparing to save_info objects based on their modified time.
* If the times are equal, will order based on the name.
*/
struct save_info_less_time {
bool operator()(const save_info& a, const save_info& b) const;
};
/**
Container for methods dealing with the save_index file.
Note: You are not supposed to instantiate this class.
*/
class save_index
{
public:
/** Determines/Adds the "save"-child of the summary config. */
static config& save_summary(std::string save);
/** Update the save_index file with changed savegame information. */
static void write_save_index();
std::vector<save_info> get_saves_list(const std::string* dir = NULL, const std::string* filter = NULL);
private:
/** Default-Constructor (don't instantiate this class) */
save_index() {}
/** Read the complete config information out of a savefile. */
void read_save_file(const std::string& name, config& cfg, std::string* error_log);
/** Read the complete config information out of the save_index file. */
static config& load();
/** Returns true if there is already a savegame with that name. */
bool save_game_exists(const std::string& name, bool compress_saves);
static bool save_index_loaded; /** Tells, if the save_index config has been already loaded. */
static config save_index_cfg; /** save_index config (who would have guessed that ;-). */
};
/** Delete all autosaves of a certain scenario. */
void clean_saves(const std::string& label);
/** Remove autosaves that are no longer needed (according to the autosave policy in the preferences). */
void remove_old_auto_saves(const int autosavemax, const int infinite_auto_saves);
/** Delete a savegame. */
void delete_game(const std::string& name);
/** The class for loading a savefile. */
class loadgame
@ -215,8 +196,7 @@ private:
void finish_save_game(const config_writer &out);
/** Throws game::save_game_failed. */
scoped_ostream open_save_game(const std::string &label);
/** Read the summary data from a savegame to display this information in the load-game dialog. */
void extract_summary_data_from_save(config& out);
friend class save_info;
game_state& gamestate_;