Merge pull request #193 from gfgtdf/gamestate_refactor

Gamestate refactor part2
This commit is contained in:
gfgtdf 2014-06-06 03:36:13 +02:00
commit 516206a0ee
22 changed files with 525 additions and 437 deletions

View file

@ -860,6 +860,7 @@ set(wesnoth-main_SRC
replay_controller.cpp
resources.cpp
save_blocker.cpp
save_index.cpp
savegame.cpp
scripting/debug_lua.cpp
scripting/lua.cpp

View file

@ -492,6 +492,7 @@ wesnoth_sources = Split("""
replay_controller.cpp
resources.cpp
save_blocker.cpp
save_index.cpp
savegame.cpp
scripting/debug_lua.cpp
scripting/lua.cpp

View file

@ -29,7 +29,6 @@ namespace pathfind {
#include <map>
class game_display;
class game_state;
class gamemap;
class unit_map;
class team;

View file

@ -42,6 +42,7 @@
#include "replay_helper.hpp"
#include "resources.hpp"
#include "savegame.hpp"
#include "save_index.hpp"
#include "strftime.hpp"
#include "synced_context.hpp"
#include "thread.hpp"

View file

@ -460,7 +460,7 @@ void game_data::clear_variable(const std::string& varname)
}
}
void game_data::write_snapshot(config& cfg){
void game_data::write_snapshot(config& cfg) const {
cfg["scenario"] = scenario_;
cfg["next_scenario"] = next_scenario_;
@ -763,20 +763,6 @@ void convert_old_saves(config& cfg){
LOG_RG<<"cfg after conversion "<<cfg<<"\n";
}
void game_state::write_snapshot(config& cfg) const
{
log_scope("write_game");
cfg.merge_attributes(classification_.to_config());
//TODO: move id_manager handling to play_controller
cfg["next_underlying_unit_id"] = str_cast(n_unit::id_manager::instance().get_save_id());
if(resources::gamedata != NULL){
resources::gamedata->write_snapshot(cfg);
}
}
void extract_summary_from_config(config& cfg_save, config& cfg_summary)
{
const config &cfg_snapshot = cfg_save.child("snapshot");

View file

@ -106,7 +106,7 @@ public:
/** the last location where a select event fired. */
map_location last_selected;
void write_snapshot(config& cfg);
void write_snapshot(config& cfg) const ;
void write_config(config_writer& out);
const std::string& next_scenario() const { return next_scenario_; }
@ -173,8 +173,6 @@ public:
~game_state(){}
game_state& operator=(const game_state& state);
//write the gamestate into a config object
void write_snapshot(config& cfg) const;
//write the config information into a stream (file)
void write_config(config_writer& out) const;

View file

@ -38,6 +38,7 @@
#include "gui/widgets/window.hpp"
#include "language.hpp"
#include "preferences_display.hpp"
#include "savegame.hpp"
#include "utils/foreach.tpp"
#include <cctype>

View file

@ -18,7 +18,7 @@
#include "gui/dialogs/dialog.hpp"
#include "gui/widgets/listbox.hpp"
#include "gui/widgets/text.hpp"
#include "savegame.hpp"
#include "save_index.hpp"
#include "tstring.hpp"
namespace gui2

View file

@ -20,6 +20,7 @@
#include "gettext.hpp"
#include "game_config.hpp"
#include "game_preferences.hpp"
#include "gamestatus.hpp"
#include "gui/auxiliary/log.hpp"
#include "gui/dialogs/field.hpp"
#include "gui/dialogs/game_delete.hpp"

View file

@ -18,7 +18,7 @@
#include "gui/dialogs/dialog.hpp"
#include "gui/widgets/listbox.hpp"
#include "gui/widgets/text.hpp"
#include "savegame.hpp"
#include "save_index.hpp"
#include "tstring.hpp"
namespace gui2

View file

@ -60,6 +60,7 @@
#include "replay_helper.hpp"
#include "resources.hpp"
#include "savegame.hpp"
#include "save_index.hpp"
#include "sound.hpp"
#include "statistics_dialog.hpp"
#include "synced_context.hpp"

View file

@ -28,7 +28,6 @@
class display;
class game_display;
class config;
class game_state;
namespace mp {

View file

@ -779,6 +779,14 @@ config play_controller::to_config() const
gui_->labels().write(cfg);
sound::write_music_play_list(cfg);
}
//TODO: move id_manager handling to play_controller
cfg["next_underlying_unit_id"] = str_cast(n_unit::id_manager::instance().get_save_id());
gamedata_.write_snapshot(cfg);
cfg.merge_attributes(gamestate_.classification().to_config());
return cfg;
}

View file

@ -383,7 +383,6 @@ LEVEL_RESULT playsingle_controller::play_scenario(
// At the beginning of the scenario, save a snapshot as replay_start
if(gamestate_.snapshot.child_or_empty("variables")["turn_number"].to_int(-1)<1){
gamestate_.replay_start() = to_config();
gamestate_.write_snapshot(gamestate_.replay_start());
}
fire_preload();

396
src/save_index.cpp Normal file
View file

@ -0,0 +1,396 @@
/*
Copyright (C) 2003 - 2014 by Jörg Hinrichs, refactored from various
places formerly created by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#include <boost/assign/list_of.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include "save_index.hpp"
#include "format_time_summary.hpp"
#include "formula_string_utils.hpp"
#include "game_display.hpp"
#include "game_end_exceptions.hpp"
#include "game_preferences.hpp"
#include "gettext.hpp"
#include "log.hpp"
#include "serialization/binary_or_text.hpp"
#include "serialization/parser.hpp"
#include "gamestatus.hpp"
#include "filesystem.hpp"
#include "config.hpp"
#include <boost/foreach.hpp>
static lg::log_domain log_engine("engine");
#define LOG_SAVE LOG_STREAM(info, log_engine)
#define ERR_SAVE LOG_STREAM(err, log_engine)
static lg::log_domain log_enginerefac("enginerefac");
#define LOG_RG LOG_STREAM(info, log_enginerefac)
#ifdef _WIN32
#ifdef INADDR_ANY
#undef INADDR_ANY
#endif
#ifdef INADDR_BROADCAST
#undef INADDR_BROADCAST
#endif
#ifdef INADDR_NONE
#undef INADDR_NONE
#endif
#include <windows.h>
/**
* conv_ansi_utf8()
* - Convert a string between ANSI encoding (for Windows filename) and UTF-8
* string &name
* - filename to be converted
* bool a2u
* - if true, convert the string from ANSI to UTF-8.
* - if false, reverse. (convert it from UTF-8 to ANSI)
*/
void conv_ansi_utf8(std::string &name, bool a2u) {
int wlen = MultiByteToWideChar(a2u ? CP_ACP : CP_UTF8, 0,
name.c_str(), -1, NULL, 0);
if (wlen == 0) return;
WCHAR *wc = new WCHAR[wlen];
if (wc == NULL) return;
if (MultiByteToWideChar(a2u ? CP_ACP : CP_UTF8, 0, name.c_str(), -1,
wc, wlen) == 0) {
delete [] wc;
return;
}
int alen = WideCharToMultiByte(!a2u ? CP_ACP : CP_UTF8, 0, wc, wlen,
NULL, 0, NULL, NULL);
if (alen == 0) {
delete [] wc;
return;
}
CHAR *ac = new CHAR[alen];
if (ac == NULL) {
delete [] wc;
return;
}
WideCharToMultiByte(!a2u ? CP_ACP : CP_UTF8, 0, wc, wlen,
ac, alen, NULL, NULL);
delete [] wc;
if (ac == NULL) {
return;
}
name = ac;
delete [] ac;
return;
}
void replace_underbar2space(std::string &name) {
LOG_SAVE << "conv(A2U)-from:[" << name << "]" << std::endl;
conv_ansi_utf8(name, true);
LOG_SAVE << "conv(A2U)-to:[" << name << "]" << std::endl;
LOG_SAVE << "replace_underbar2space-from:[" << name << "]" << std::endl;
std::replace(name.begin(), name.end(), '_', ' ');
LOG_SAVE << "replace_underbar2space-to:[" << name << "]" << std::endl;
}
void replace_space2underbar(std::string &name) {
LOG_SAVE << "conv(U2A)-from:[" << name << "]" << std::endl;
conv_ansi_utf8(name, false);
LOG_SAVE << "conv(U2A)-to:[" << name << "]" << std::endl;
LOG_SAVE << "replace_space2underbar-from:[" << name << "]" << std::endl;
std::replace(name.begin(), name.end(), ' ', '_');
LOG_SAVE << "replace_space2underbar-to:[" << name << "]" << std::endl;
}
#else /* ! _WIN32 */
void replace_underbar2space(std::string &name) {
std::replace(name.begin(),name.end(),'_',' ');
}
void replace_space2underbar(std::string &name) {
std::replace(name.begin(),name.end(),' ','_');
}
#endif /* _WIN32 */
namespace savegame {
void save_index_class::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 save_index_class::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 save_index_class::remove(const std::string& name) {
config& root = data();
root.remove_attribute(name);
write_save_index();
}
void save_index_class::set_modified(const std::string& name, const time_t& modified) {
modified_[name] = modified;
}
config& save_index_class::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;
}
void save_index_class::write_save_index() {
log_scope("write_save_index()");
try {
scoped_ostream stream = ostream_file(get_save_index_file());
if (preferences::save_compression_format() != compression::NONE) {
// TODO: maybe allow writing this using bz2 too?
write_gz(*stream, data());
} else {
write(*stream, data());
}
} catch(io_exception& e) {
ERR_SAVE << "error writing to save index file: '" << e.what() << "'" << std::endl;
}
}
save_index_class::save_index_class()
: loaded_(false)
, data_()
, modified_()
{
}
config& save_index_class::data(const std::string& name) {
config& cfg = data();
if (config& sv = cfg.find_child("save", "save", name)) {
return sv;
}
config& res = cfg.add_child("save");
res["save"] = name;
return res;
}
config& save_index_class::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() << "'" << std::endl;
} catch(config::error& e) {
ERR_SAVE << "error parsing save index config file:\n" << e.message << std::endl;
data_.clear();
}
loaded_ = true;
}
return data_;
}
save_index_class 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_;
};
/** 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)),
filenames.end());
}
std::vector<save_info> result;
std::transform(filenames.begin(), filenames.end(),
std::back_inserter(result), creator);
std::sort(result.begin(),result.end(),save_info_less_time());
return result;
}
const config& save_info::summary() const {
return save_index_manager.get(name());
}
std::string save_info::format_time_local() const
{
char time_buf[256] = {0};
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")),
tm_l);
if(res == 0) {
time_buf[0] = 0;
}
} else {
LOG_SAVE << "localtime() returned null for time " << this->modified() << ", save " << name();
}
return time_buf;
}
std::string save_info::format_time_summary() const
{
time_t t = modified();
return util::format_time_summary(t);
}
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();
}
}
static std::istream* find_save_file(const std::string &name, const std::string &alt_name, const std::vector<std::string> &suffixes) {
BOOST_FOREACH(const std::string &suf, suffixes) {
std::istream *file_stream = istream_file(get_saves_dir() + "/" + name + suf);
if (file_stream->fail()) {
delete file_stream;
file_stream = istream_file(get_saves_dir() + "/" + alt_name + suf);
}
if (!file_stream->fail())
return file_stream;
else
delete file_stream;
}
LOG_SAVE << "Could not open supplied filename '" << name << "'\n";
throw game::load_game_failed();
}
void read_save_file(const std::string& name, config& cfg, std::string* error_log)
{
std::string modified_name = name;
replace_space2underbar(modified_name);
static const std::vector<std::string> suffixes = boost::assign::list_of("")(".gz")(".bz2");
scoped_istream file_stream = find_save_file(modified_name, name, suffixes);
cfg.clear();
try{
/*
* Test the modified name, since it might use a .gz
* file even when not requested.
*/
if(is_gzip_file(modified_name)) {
read_gz(cfg, *file_stream);
} else if(is_bzip2_file(modified_name)) {
read_bz2(cfg, *file_stream);
} else {
read(cfg, *file_stream);
}
} catch(const std::ios_base::failure& e) {
LOG_SAVE << e.what();
if(error_log) {
*error_log += e.what();
}
throw game::load_game_failed();
} catch(const config::error &err) {
LOG_SAVE << err.message;
if(error_log) {
*error_log += err.message;
}
throw game::load_game_failed();
}
if(cfg.empty()) {
LOG_SAVE << "Could not parse file data into config\n";
throw game::load_game_failed();
}
}
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);
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 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());
save_index_manager.remove(name);
}
create_save_info::create_save_info(const std::string* d)
: dir(d ? *d : get_saves_dir())
{
}
save_info create_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);
}
}

108
src/save_index.hpp Normal file
View file

@ -0,0 +1,108 @@
/*
Copyright (C) 2003 - 2014 by Jörg Hinrichs, refactored from various
places formerly created by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef SAVE_INDEX_H_INCLUDED
#define SAVE_INDEX_H_INCLUDED
//#include "filesystem.hpp"
//#include "gamestatus.hpp"
//#include "tod_manager.hpp"
//#include "show_dialog.hpp"
#include "config.hpp"
#include "serialization/compression.hpp"
class config_writer;
class game_display;
namespace savegame {
/** Filename and modification date for a file list */
class save_info {
private:
friend class create_save_info;
private:
save_info(const std::string& name, const time_t& modified) :
name_(name), modified_(modified)
{}
public:
const std::string& name() const { return name_; }
const time_t& modified() const { return modified_; }
public:
std::string format_time_summary() const;
std::string format_time_local() const;
const config& summary() const;
private:
std::string name_;
time_t modified_;
};
/**
* 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;
};
std::vector<save_info> get_saves_list(const std::string* dir = NULL, const std::string* filter = NULL);
/** Read the complete config information out of a savefile. */
void read_save_file(const std::string& name, config& cfg, std::string* error_log);
/** 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);
class create_save_info {
public:
create_save_info(const std::string* d = NULL) ;
save_info operator()(const std::string& filename) const ;
const std::string dir;
};
class save_index_class
{
public:
void rebuild(const std::string& name) ;
void rebuild(const std::string& name, const time_t& modified) ;
void remove(const std::string& name) ;
void set_modified(const std::string& name, const time_t& modified) ;
config& get(const std::string& name) ;
public:
void write_save_index() ;
public:
save_index_class();
private:
config& data(const std::string& name) ;
config& data() ;
private:
bool loaded_;
config data_;
std::map< std::string, time_t > modified_;
};
extern save_index_class save_index_manager;
} //end of namespace savegame
void replace_underbar2space(std::string &name);
void replace_space2underbar(std::string &name);
#endif

View file

@ -18,6 +18,7 @@
#include "savegame.hpp"
#include "save_index.hpp"
#include "carryover.hpp"
#include "dialogs.hpp" //FIXME: get rid of this as soon as the two remaining dialogs are moved to gui2
#include "format_time_summary.hpp"
@ -55,342 +56,9 @@ static lg::log_domain log_engine("engine");
static lg::log_domain log_enginerefac("enginerefac");
#define LOG_RG LOG_STREAM(info, log_enginerefac)
#ifdef _WIN32
#ifdef INADDR_ANY
#undef INADDR_ANY
#endif
#ifdef INADDR_BROADCAST
#undef INADDR_BROADCAST
#endif
#ifdef INADDR_NONE
#undef INADDR_NONE
#endif
#include <windows.h>
/**
* conv_ansi_utf8()
* - Convert a string between ANSI encoding (for Windows filename) and UTF-8
* string &name
* - filename to be converted
* bool a2u
* - if true, convert the string from ANSI to UTF-8.
* - if false, reverse. (convert it from UTF-8 to ANSI)
*/
void conv_ansi_utf8(std::string &name, bool a2u) {
int wlen = MultiByteToWideChar(a2u ? CP_ACP : CP_UTF8, 0,
name.c_str(), -1, NULL, 0);
if (wlen == 0) return;
WCHAR *wc = new WCHAR[wlen];
if (wc == NULL) return;
if (MultiByteToWideChar(a2u ? CP_ACP : CP_UTF8, 0, name.c_str(), -1,
wc, wlen) == 0) {
delete [] wc;
return;
}
int alen = WideCharToMultiByte(!a2u ? CP_ACP : CP_UTF8, 0, wc, wlen,
NULL, 0, NULL, NULL);
if (alen == 0) {
delete [] wc;
return;
}
CHAR *ac = new CHAR[alen];
if (ac == NULL) {
delete [] wc;
return;
}
WideCharToMultiByte(!a2u ? CP_ACP : CP_UTF8, 0, wc, wlen,
ac, alen, NULL, NULL);
delete [] wc;
if (ac == NULL) {
return;
}
name = ac;
delete [] ac;
return;
}
void replace_underbar2space(std::string &name) {
LOG_SAVE << "conv(A2U)-from:[" << name << "]" << std::endl;
conv_ansi_utf8(name, true);
LOG_SAVE << "conv(A2U)-to:[" << name << "]" << std::endl;
LOG_SAVE << "replace_underbar2space-from:[" << name << "]" << std::endl;
std::replace(name.begin(), name.end(), '_', ' ');
LOG_SAVE << "replace_underbar2space-to:[" << name << "]" << std::endl;
}
void replace_space2underbar(std::string &name) {
LOG_SAVE << "conv(U2A)-from:[" << name << "]" << std::endl;
conv_ansi_utf8(name, false);
LOG_SAVE << "conv(U2A)-to:[" << name << "]" << std::endl;
LOG_SAVE << "replace_space2underbar-from:[" << name << "]" << std::endl;
std::replace(name.begin(), name.end(), ' ', '_');
LOG_SAVE << "replace_space2underbar-to:[" << name << "]" << std::endl;
}
#else /* ! _WIN32 */
void replace_underbar2space(std::string &name) {
std::replace(name.begin(),name.end(),'_',' ');
}
void replace_space2underbar(std::string &name) {
std::replace(name.begin(),name.end(),' ','_');
}
#endif /* _WIN32 */
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 remove(const std::string& name) {
config& root = data();
root.remove_attribute(name);
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::save_compression_format() != compression::NONE) {
// TODO: maybe allow writing this using bz2 too?
write_gz(*stream, data());
} else {
write(*stream, data());
}
} catch(io_exception& e) {
ERR_SAVE << "error writing to save index file: '" << e.what() << "'" << std::endl;
}
}
public:
save_index_class()
: loaded_(false)
, data_()
, modified_()
{
}
private:
config& data(const std::string& name) {
config& cfg = data();
if (config& sv = cfg.find_child("save", "save", name)) {
return sv;
}
config& res = cfg.add_child("save");
res["save"] = name;
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() << "'" << std::endl;
} catch(config::error& e) {
ERR_SAVE << "error parsing save index config file:\n" << e.message << std::endl;
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)),
filenames.end());
}
std::vector<save_info> result;
std::transform(filenames.begin(), filenames.end(),
std::back_inserter(result), creator);
std::sort(result.begin(),result.end(),save_info_less_time());
return result;
}
const config& save_info::summary() const {
return save_index_manager.get(name());
}
std::string save_info::format_time_local() const
{
char time_buf[256] = {0};
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")),
tm_l);
if(res == 0) {
time_buf[0] = 0;
}
} else {
LOG_SAVE << "localtime() returned null for time " << this->modified() << ", save " << name();
}
return time_buf;
}
std::string save_info::format_time_summary() const
{
time_t t = modified();
return util::format_time_summary(t);
}
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();
}
}
static std::istream* find_save_file(const std::string &name, const std::string &alt_name, const std::vector<std::string> &suffixes) {
BOOST_FOREACH(const std::string &suf, suffixes) {
std::istream *file_stream = istream_file(get_saves_dir() + "/" + name + suf);
if (file_stream->fail()) {
delete file_stream;
file_stream = istream_file(get_saves_dir() + "/" + alt_name + suf);
}
if (!file_stream->fail())
return file_stream;
else
delete file_stream;
}
LOG_SAVE << "Could not open supplied filename '" << name << "'\n";
throw game::load_game_failed();
}
void read_save_file(const std::string& name, config& cfg, std::string* error_log)
{
std::string modified_name = name;
replace_space2underbar(modified_name);
static const std::vector<std::string> suffixes = boost::assign::list_of("")(".gz")(".bz2");
scoped_istream file_stream = find_save_file(modified_name, name, suffixes);
cfg.clear();
try{
/*
* Test the modified name, since it might use a .gz
* file even when not requested.
*/
if(is_gzip_file(modified_name)) {
read_gz(cfg, *file_stream);
} else if(is_bzip2_file(modified_name)) {
read_bz2(cfg, *file_stream);
} else {
read(cfg, *file_stream);
}
} catch(const std::ios_base::failure& e) {
LOG_SAVE << e.what();
if(error_log) {
*error_log += e.what();
}
throw game::load_game_failed();
} catch(const config::error &err) {
LOG_SAVE << err.message;
if(error_log) {
*error_log += err.message;
}
throw game::load_game_failed();
}
if(cfg.empty()) {
LOG_SAVE << "Could not parse file data into config\n";
throw game::load_game_failed();
}
}
bool save_game_exists(const std::string& name, compression::format compressed)
{
std::string fname = name;
@ -414,33 +82,6 @@ void clean_saves(const std::string& label)
}
}
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);
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 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());
save_index_manager.remove(name);
}
loadgame::loadgame(display& gui, const config& game_config, game_state& gamestate)
: game_config_(game_config)
, gui_(gui)
@ -1082,11 +723,6 @@ void ingame_savegame::create_filename()
set_filename(stream.str());
}
void ingame_savegame::before_save()
{
savegame::before_save();
gamestate().write_snapshot(snapshot());
}
void ingame_savegame::write_game(config_writer &out) {
log_scope("write_game");

View file

@ -18,10 +18,8 @@
#include "filesystem.hpp"
#include "gamestatus.hpp"
#include "tod_manager.hpp"
#include "show_dialog.hpp"
#include "serialization/compression.hpp"
class config_writer;
class game_display;
@ -30,52 +28,12 @@ struct illegal_filename_exception {};
namespace savegame {
/** Filename and modification date for a file list */
class save_info {
private:
friend class create_save_info;
private:
save_info(const std::string& name, const time_t& modified) :
name_(name), modified_(modified)
{}
public:
const std::string& name() const { return name_; }
const time_t& modified() const { return modified_; }
public:
std::string format_time_summary() const;
std::string format_time_local() const;
const config& summary() const;
private:
std::string name_;
time_t modified_;
};
/**
* 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;
};
std::vector<save_info> get_saves_list(const std::string* dir = NULL, const std::string* filter = NULL);
/** Read the complete config information out of a savefile. */
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. */
bool save_game_exists(const std::string& name, compression::format compressed);
/** 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
{
@ -186,7 +144,7 @@ protected:
config& snapshot() { return snapshot_; }
/** If there needs to be some data fiddling before saving the game, this is the place to go. */
virtual void before_save();
void before_save();
/** Writing the savegame config to a file. */
virtual void write_game(config_writer &out);
@ -242,8 +200,6 @@ private:
/** Create a filename for automatic saves */
virtual void create_filename();
/** Builds the snapshot config. */
virtual void before_save();
void write_game(config_writer &out);

View file

@ -27,7 +27,6 @@
#include <boost/shared_ptr.hpp>
class display;
class game_state;
class vconfig;
namespace storyscreen {

View file

@ -29,7 +29,6 @@
class config;
class vconfig;
class display;
class game_state;
namespace storyscreen {

View file

@ -19,7 +19,6 @@
#include "time_of_day.hpp"
#include "savegame_config.hpp"
class game_state;
class gamemap;
class unit_map;

View file

@ -231,7 +231,7 @@ struct variable_info
std::string key; //the name of the internal attribute or child
bool explicit_index; //true if query ended in [...] specifier
size_t index; //the index of the child
config *vars; //the containing node in game_state::variables
config *vars; //the containing node in game_data s variables
/**
* Results: after deciding the desired type, these methods can retrieve the result