Savegame reorganization Step 1: a simpler interface to saving and loading.
Move save_game_exists, open_save_game, delete_game, and get_saves_list from gamestatus.cpp to savegame.cpp. Setup a savegame_manager class to collect all those homeless functions. Fix bug #13364.
This commit is contained in:
parent
ba07627fb6
commit
c221e1b5d0
8 changed files with 156 additions and 165 deletions
|
@ -298,7 +298,7 @@ int get_save_name(display & disp,const std::string& message, const std::string&
|
|||
continue;
|
||||
}
|
||||
|
||||
if (res == 0 && save_game_exists(*fname)) {
|
||||
if (res == 0 && savegame_manager::save_game_exists(*fname)) {
|
||||
std::stringstream s;
|
||||
s << _("Save already exists. Do you want to overwrite it?")
|
||||
<< std::endl << _("Name: ") << *fname;
|
||||
|
@ -357,7 +357,7 @@ gui::dialog_button_action::RESULT delete_save::button_pressed(int menu_selection
|
|||
filter_.delete_item(menu_selection);
|
||||
|
||||
// Delete the file
|
||||
delete_game(saves_[index].name);
|
||||
savegame_manager::delete_game(saves_[index].name);
|
||||
|
||||
// Remove it from the list of saves
|
||||
saves_.erase(saves_.begin() + index);
|
||||
|
@ -421,7 +421,7 @@ void save_preview_pane::draw_contents()
|
|||
config& summary = *(*summaries_)[index_];
|
||||
if (summary["label"] == ""){
|
||||
try {
|
||||
save_summary::load_summary((*info_)[index_].name, summary, &dummy);
|
||||
savegame_manager::load_summary((*info_)[index_].name, summary, &dummy);
|
||||
*(*summaries_)[index_] = summary;
|
||||
} catch(game::load_game_failed&) {
|
||||
summary["corrupt"] = "yes";
|
||||
|
@ -631,7 +631,7 @@ std::string load_game_dialog(display& disp, const config& game_config, bool* sho
|
|||
std::vector<save_info> games;
|
||||
{
|
||||
cursor::setter cur(cursor::WAIT);
|
||||
games = get_saves_list();
|
||||
games = savegame_manager::get_saves_list();
|
||||
}
|
||||
|
||||
if(games.empty()) {
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include "log.hpp"
|
||||
#include "game_preferences.hpp"
|
||||
#include "replay.hpp"
|
||||
#include "savegame.hpp" //FIXME: only because of replace_space2underbar
|
||||
#include "statistics.hpp"
|
||||
#include "unit_id.hpp"
|
||||
#include "wesconfig.h"
|
||||
|
@ -588,90 +587,6 @@ void game_state::write_snapshot(config& cfg) const
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<save_info> 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;
|
||||
}
|
||||
|
||||
bool save_game_exists(const std::string& name)
|
||||
{
|
||||
std::string fname = name;
|
||||
replace_space2underbar(fname);
|
||||
|
||||
if(preferences::compress_saves()) {
|
||||
fname += ".gz";
|
||||
}
|
||||
|
||||
return file_exists(get_saves_dir() + "/" + fname);
|
||||
}
|
||||
|
||||
void delete_game(const std::string& name)
|
||||
{
|
||||
std::string modified_name = name;
|
||||
replace_space2underbar(modified_name);
|
||||
|
||||
remove((get_saves_dir() + "/" + name).c_str());
|
||||
remove((get_saves_dir() + "/" + modified_name).c_str());
|
||||
}
|
||||
|
||||
// Throws game::save_game_failed
|
||||
scoped_ostream open_save_game(const std::string &label)
|
||||
{
|
||||
std::string name = label;
|
||||
replace_space2underbar(name);
|
||||
|
||||
try {
|
||||
return scoped_ostream(ostream_file(get_saves_dir() + "/" + name));
|
||||
} catch(io_exception& e) {
|
||||
throw game::save_game_failed(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool save_index_loaded = false;
|
||||
config save_index_cfg;
|
||||
|
|
|
@ -300,37 +300,12 @@ private:
|
|||
|
||||
std::string generate_game_uuid();
|
||||
|
||||
/**
|
||||
* Holds all the data needed to start a scenario.
|
||||
*
|
||||
* I.e. this is the object serialized to disk when saving/loading a game.
|
||||
* It is also the object which needs to be created to start a new game.
|
||||
*/
|
||||
struct save_info {
|
||||
save_info(const std::string& n, time_t t) : name(n), time_modified(t) {}
|
||||
std::string name;
|
||||
time_t time_modified;
|
||||
};
|
||||
|
||||
/** Get a list of available saves. */
|
||||
std::vector<save_info> get_saves_list(const std::string* dir = NULL, const std::string* filter = NULL);
|
||||
|
||||
void write_players(game_state& gamestate, config& cfg);
|
||||
|
||||
/** Returns true iff there is already a savegame with that name. */
|
||||
bool save_game_exists(const std::string & name);
|
||||
|
||||
/** Throws game::save_game_failed. */
|
||||
scoped_ostream open_save_game(const std::string &label);
|
||||
|
||||
/** Delete a savegame. */
|
||||
void delete_game(const std::string& name);
|
||||
|
||||
config& save_summary(std::string save);
|
||||
|
||||
void write_save_index();
|
||||
|
||||
void replace_underbar2space(std::string &name);
|
||||
void extract_summary_from_config(config& cfg_save, config& cfg_summary);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -51,26 +51,6 @@
|
|||
#define LOG_NG LOG_STREAM(info, engine)
|
||||
#define DBG_NG LOG_STREAM(info, engine)
|
||||
|
||||
namespace {
|
||||
|
||||
void remove_old_auto_saves()
|
||||
{
|
||||
const std::string auto_save = _("Auto-Save");
|
||||
int countdown = preferences::autosavemax();
|
||||
if (countdown == preferences::INFINITE_AUTO_SAVES)
|
||||
return;
|
||||
|
||||
std::vector<save_info> games = get_saves_list(NULL, &auto_save);
|
||||
for (std::vector<save_info>::iterator i = games.begin(); i != games.end(); i++) {
|
||||
if (countdown-- <= 0) {
|
||||
LOG_NG << "Deleting savegame '" << i->name << "'\n";
|
||||
delete_game(i->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
namespace events{
|
||||
|
||||
class delete_recall_unit : public gui::dialog_button_action
|
||||
|
@ -629,7 +609,7 @@ private:
|
|||
|
||||
end = SDL_GetTicks();
|
||||
LOG_NG << "Milliseconds to save " << save.filename() << ": " << end - start << "\n";
|
||||
remove_old_auto_saves();
|
||||
savegame_manager::remove_old_auto_saves();
|
||||
}
|
||||
|
||||
void menu_handler::preferences()
|
||||
|
|
|
@ -808,7 +808,7 @@ void play_controller::expand_autosaves(std::vector<std::string>& items)
|
|||
std::vector<std::string> newsaves;
|
||||
for (unsigned int turn = status_.turn(); turn != 0; turn--) {
|
||||
std::string name = gamestate_.label + "-" + _("Auto-Save") + lexical_cast<std::string>(turn);
|
||||
if (save_game_exists(name)) {
|
||||
if (savegame_manager::save_game_exists(name)) {
|
||||
if(preferences::compress_saves()) {
|
||||
newsaves.push_back(name + ".gz");
|
||||
} else {
|
||||
|
|
|
@ -102,19 +102,6 @@ void play_replay(display& disp, game_state& gamestate, const config& game_config
|
|||
}
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static LEVEL_RESULT playsingle_scenario(const config& game_config,
|
||||
const config* level, display& disp, game_state& state_of_game,
|
||||
const config::const_child_itors &story, upload_log &log,
|
||||
|
@ -395,12 +382,11 @@ 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())
|
||||
clean_saves(gamestate.label);
|
||||
savegame_manager::clean_saves(gamestate.label);
|
||||
|
||||
if (preferences::save_replays()) {
|
||||
replay_savegame save(gamestate);
|
||||
save.save_game(""); //string is not used, noninteractive save
|
||||
//::save_replay(gamestate);
|
||||
save.save_game_interactive(disp, "", gui::OK_CANCEL, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -595,7 +581,7 @@ LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_
|
|||
|
||||
if (gamestate.campaign_type == "scenario"){
|
||||
if (preferences::delete_saves())
|
||||
clean_saves(gamestate.label);
|
||||
savegame_manager::clean_saves(gamestate.label);
|
||||
}
|
||||
return VICTORY;
|
||||
}
|
||||
|
|
121
src/savegame.cpp
121
src/savegame.cpp
|
@ -106,7 +106,32 @@
|
|||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
static void read_save_file(const std::string& name, config& cfg, std::string* error_log)
|
||||
/**
|
||||
* 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;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void savegame_manager::read_save_file(const std::string& name, config& cfg, std::string* error_log)
|
||||
{
|
||||
std::string modified_name = name;
|
||||
replace_space2underbar(modified_name);
|
||||
|
@ -135,7 +160,7 @@ static void read_save_file(const std::string& name, config& cfg, std::string* er
|
|||
}
|
||||
}
|
||||
|
||||
void save_summary::load_summary(const std::string& name, config& cfg_summary, std::string* error_log){
|
||||
void savegame_manager::load_summary(const std::string& name, config& cfg_summary, std::string* error_log){
|
||||
log_scope("load_game_summary");
|
||||
|
||||
config cfg;
|
||||
|
@ -144,6 +169,81 @@ void save_summary::load_summary(const std::string& name, config& cfg_summary, st
|
|||
::extract_summary_from_config(cfg, cfg_summary);
|
||||
}
|
||||
|
||||
bool savegame_manager::save_game_exists(const std::string& name)
|
||||
{
|
||||
std::string fname = name;
|
||||
replace_space2underbar(fname);
|
||||
|
||||
if(preferences::compress_saves()) {
|
||||
fname += ".gz";
|
||||
}
|
||||
|
||||
return file_exists(get_saves_dir() + "/" + fname);
|
||||
}
|
||||
|
||||
std::vector<save_info> savegame_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 savegame_manager::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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void savegame_manager::remove_old_auto_saves()
|
||||
{
|
||||
const std::string auto_save = _("Auto-Save");
|
||||
int countdown = preferences::autosavemax();
|
||||
if (countdown == preferences::INFINITE_AUTO_SAVES)
|
||||
return;
|
||||
|
||||
std::vector<save_info> games = get_saves_list(NULL, &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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void savegame_manager::delete_game(const std::string& name)
|
||||
{
|
||||
std::string modified_name = name;
|
||||
replace_space2underbar(modified_name);
|
||||
|
||||
remove((get_saves_dir() + "/" + name).c_str());
|
||||
remove((get_saves_dir() + "/" + modified_name).c_str());
|
||||
}
|
||||
|
||||
loadgame::loadgame(display& gui, const config& game_config, game_state& gamestate)
|
||||
: game_config_(game_config)
|
||||
, gui_(gui)
|
||||
|
@ -188,7 +288,7 @@ void loadgame::load_game(std::string& filename, bool show_replay, bool cancel_or
|
|||
throw load_game_cancelled_exception();
|
||||
|
||||
std::string error_log;
|
||||
read_save_file(filename_, load_config_, &error_log);
|
||||
savegame_manager::read_save_file(filename_, load_config_, &error_log);
|
||||
|
||||
if(!error_log.empty()) {
|
||||
try {
|
||||
|
@ -265,7 +365,7 @@ void loadgame::load_multiplayer_game()
|
|||
cursor::setter cur(cursor::WAIT);
|
||||
log_scope("load_game");
|
||||
|
||||
read_save_file(filename_, load_config_, &error_log);
|
||||
savegame_manager::read_save_file(filename_, load_config_, &error_log);
|
||||
copy_era(load_config_);
|
||||
|
||||
gamestate_ = game_state(load_config_);
|
||||
|
@ -455,6 +555,19 @@ void savegame::finish_save_game(const config_writer &out)
|
|||
}
|
||||
}
|
||||
|
||||
// Throws game::save_game_failed
|
||||
scoped_ostream savegame::open_save_game(const std::string &label)
|
||||
{
|
||||
std::string name = label;
|
||||
replace_space2underbar(name);
|
||||
|
||||
try {
|
||||
return scoped_ostream(ostream_file(get_saves_dir() + "/" + name));
|
||||
} catch(io_exception& e) {
|
||||
throw game::save_game_failed(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void savegame::extract_summary_data_from_save(config& out)
|
||||
{
|
||||
const bool has_replay = gamestate_.replay_data.empty() == false;
|
||||
|
|
|
@ -26,13 +26,37 @@ struct load_game_cancelled_exception
|
|||
{
|
||||
};
|
||||
|
||||
class save_summary
|
||||
/**
|
||||
* Holds all the data needed to start a scenario. YogiHH: really??
|
||||
*
|
||||
* I.e. this is the object serialized to disk when saving/loading a game.
|
||||
* It is also the object which needs to be created to start a new game.
|
||||
*/
|
||||
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 savegame_manager
|
||||
{
|
||||
public:
|
||||
save_summary() {}
|
||||
virtual ~save_summary() {}
|
||||
|
||||
static void load_summary(const std::string& name, config& cfg_summary, std::string* error_log);
|
||||
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);
|
||||
/** Get a list of available saves. */
|
||||
static std::vector<save_info> get_saves_list(const std::string *dir = NULL, const std::string* filter = NULL);
|
||||
|
||||
static void clean_saves(const std::string &label);
|
||||
static void remove_old_auto_saves();
|
||||
/** Delete a savegame. */
|
||||
static void delete_game(const std::string& name);
|
||||
|
||||
private:
|
||||
/** Default-Konstruktor (don't instantiate this class) */
|
||||
savegame_manager() {}
|
||||
};
|
||||
|
||||
class loadgame
|
||||
|
@ -117,6 +141,8 @@ private:
|
|||
|
||||
void write_game(config_writer &out) const;
|
||||
void finish_save_game(const config_writer &out);
|
||||
/** Throws game::save_game_failed. */
|
||||
scoped_ostream open_save_game(const std::string &label);
|
||||
void extract_summary_data_from_save(config& out);
|
||||
|
||||
game_state& gamestate_;
|
||||
|
@ -199,10 +225,6 @@ private:
|
|||
virtual void before_save();
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
void conv_ansi_utf8(std::string &name, bool a2u);
|
||||
#endif
|
||||
|
||||
void replace_underbar2space(std::string &name);
|
||||
void replace_space2underbar(std::string &name);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue