preferences: Merge compress_saves and bzip2_savegame_compression options

Now that we have combo box options in Preferences -> Advanced, we can
finally present the saved game compression options in a user-friendly
fashion instead of having two separate options with an unclear
relationship.

However, in order to achieve this I had to move far more code than I am
normally comfortable with:

 * Moved the config_writer::compressor enum type to a separate header
   file, serialization/compression.hpp, in order to make it available to
   more units without pulling a bunch of heavy crap (such as the boost
   iostreams headers) that's normally unneeded. The new type is
   compression::format.

 * Added a compression::format_extension() function to determine a file
   extension string according to a compression type. This is needed more
   often than one would think, see below.

 * Changed all savegame-related classes to use the compression::format
   type instead of a single boolean flag and bzip2_savegame_compression
   queries. This is a code-heavy task by design.

 * Changed all code scattered around deciding whether to use ".gz" or
   ".bz2" for a save extension to use compression::format_extension()
   instead. This affects both saved game implementation and interface
   code for some reason (read: poor design).

 * Removed the bzip2_savegame_compression preferences option, reusing
   the previous compress_saves option for this feature, turning it from
   a boolean option to a string option ("none", "gzip", "bzip2",
   defaults to "gzip" per advanced_preferences.cfg).

 * preferences::compress_saves() became
   preferences::save_compression_format().

 * Without bzip2_savegame_compression, people who previously enabled
   bzip2 compression will revert to gzip compression. This seems to be a
   rare enough case to me to bother with providing backwards
   compatibility with a feature that only existed for [1.11.0, 1.11.8).

 * For preferences files from previous versions where compress_saves was
   set to either "yes" or "no", there will be a conspicuous value in the
   second column of the advanced preferences list, especially if they
   are using a translation; it'll be either "yes" or "no" as an
   untranslatable string. A value of "yes" is equivalent to "gzip", and
   a value of "no" is equivalent to "none". Any other unrecognized value
   is equivalent to "gzip". As soon as the user chooses a compression
   format again, one of the three supported values ("none", "gzip", or
   "bzip2") will be written to the compressed_saves attribute in the
   preferences file.

All in all, a ridiculously messy commit that's not particularly feasible
to break down into separate pieces because all of the involved code is
very closely related to the overarching feature that's being
implemented in it.

Note that the save_index currently only uses gzip regardless of whether
bzip2 is the current compression format choice. This was the case even
before this commit, and I'm not sure it's worth it to change it given
the potential CPU usage and the fact that save_index tends to be broken
most of the time anyway.
This commit is contained in:
Ignacio R. Morelle 2013-12-20 23:18:40 -03:00
parent f1b4d2dc45
commit 553c56b2bd
15 changed files with 101 additions and 78 deletions

View file

@ -67,6 +67,11 @@ Version 1.11.7+dev:
on the configuration screen.
* Removed the MP modifications dialog; modifications are now displayed
directly on the creation screen.
* The "Compressed saves" and "Compress savegames using bzip2" options in
Preferences -> Advanced have been replaced by a single option,
"Compressed saved games", that lets the user pick between gzip (default),
bzip2, and no compression. Users who previously enabled bzip2 compression
will need to do so again.
* WML engine:
* WML variable turn_number is set correctly (to 1) in prestart and start
events. Previously, it retained its last value from the previous scenario

View file

@ -1,9 +1,30 @@
#textdomain wesnoth
[advanced_preference]
field=compress_saves
name= _ "Compressed saves"
type=boolean
default=yes
# po: Associated save_compression_short^ entries need to be as short as
# po: possible to avoid stretching the advanced preferences list too
# po: much.
name= _ "Compress saved games"
type=combo
default=gzip
[option]
id=gzip
name= _ "save_compression^Gzip"
name_short= _ "save_compression_short^gzip"
description= _ "save_compression_desc^Default compression, faster"
[/option]
[option]
id=bzip2
name= _ "save_compression^Bzip2"
name_short= _ "save_compression_short^bzip2"
description= _ "save_compression_desc^Best compression, slower"
[/option]
[option]
id=none
name= _ "save_compression^No"
name_short= _ "save_compression_short^no"
description= _ "save_compression_desc^Large plain text files"
[/option]
[/advanced_preference]
[advanced_preference]
@ -159,13 +180,6 @@
default=no
[/advanced_preference]
[advanced_preference]
field=bzip2_savegame_compression
name= _ "Compress savegames using bzip2"
type=boolean
default=no
[/advanced_preference]
[advanced_preference]
field=new_lobby
name= _ "Experimental multiplayer lobby"

View file

@ -46,6 +46,11 @@ Version 1.11.7+dev:
* Added a party full bell to the MP game configuration screen, played once
all human player slots have been taken.
* Change layout for advertized games in the MP lobby and add map icon.
* The "Compressed saves" and "Compress savegames using bzip2" options in
Preferences -> Advanced have been replaced by a single option,
"Compressed saved games", that lets the user pick between gzip (default),
bzip2, and no compression. Users who previously enabled bzip2 compression
will need to do so again.
Version 1.11.7:

View file

@ -913,9 +913,25 @@ void set_flip_time(bool value)
preferences::set("flip_time", value);
}
bool compress_saves()
compression::format save_compression_format()
{
return preferences::get("compress_saves", true);
const std::string& choice =
preferences::get("compress_saves");
// "yes" was used in 1.11.7 and earlier; the compress_saves
// option used to be a toggle for gzip in those versions.
if(choice.empty() || choice == "gzip" || choice == "yes") {
return compression::GZIP;
} else if(choice == "bzip2") {
return compression::BZIP2;
} else if(choice == "none" || choice == "no") { // see above
return compression::NONE;
} /*else*/
// In case the preferences file was created by a later version
// supporting some algorithm we don't; although why would anyone
// playing a game need more algorithms, really...
return compression::GZIP;
}
bool startup_effect()

View file

@ -21,6 +21,8 @@ class unit_map;
#include "preferences.hpp"
#include "serialization/compression.hpp"
#include <set>
#include <vector>
@ -230,7 +232,7 @@ class acquaintance;
bool show_all_units_in_help();
void set_show_all_units_in_help(bool value);
bool compress_saves();
compression::format save_compression_format();
bool startup_effect();

View file

@ -2825,7 +2825,7 @@ void console_handler::do_benchmark() {
void console_handler::do_save() {
savegame::ingame_savegame save(menu_handler_.gamestate_, *menu_handler_.gui_,
resources::controller->to_config(),
preferences::compress_saves());
preferences::save_compression_format());
save.save_game_automatic(menu_handler_.gui_->video(), true, get_data());
}
void console_handler::do_save_quit() {

View file

@ -410,7 +410,7 @@ void play_controller::status_table(){
void play_controller::save_game(){
if(save_blocker::try_block()) {
save_blocker::save_unblocker unblocker;
savegame::ingame_savegame save(gamestate_, *gui_, to_config(), preferences::compress_saves());
savegame::ingame_savegame save(gamestate_, *gui_, to_config(), preferences::save_compression_format());
save.save_game_interactive(gui_->video(), "", gui::OK_CANCEL);
} else {
save_blocker::on_unblock(this,&play_controller::save_game);
@ -420,7 +420,7 @@ void play_controller::save_game(){
void play_controller::save_replay(){
if(save_blocker::try_block()) {
save_blocker::save_unblocker unblocker;
savegame::replay_savegame save(gamestate_, preferences::compress_saves());
savegame::replay_savegame save(gamestate_, preferences::save_compression_format());
save.save_game_interactive(gui_->video(), "", gui::OK_CANCEL);
} else {
save_blocker::on_unblock(this,&play_controller::save_replay);
@ -1160,6 +1160,9 @@ static void trim_items(std::vector<std::string>& newitems) {
void play_controller::expand_autosaves(std::vector<std::string>& items)
{
const compression::format comp_format =
preferences::save_compression_format();
savenames_.clear();
for (unsigned int i = 0; i < items.size(); ++i) {
if (items[i] == "AUTOSAVES") {
@ -1168,23 +1171,17 @@ 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::save_game_exists(name, preferences::compress_saves())) {
if(preferences::compress_saves()) {
newsaves.push_back(name + (preferences::bzip2_savegame_compression() ? ".bz2" : ".gz"));
} else {
newsaves.push_back(name);
}
if (savegame::save_game_exists(name, comp_format)) {
newsaves.push_back(
name + compression::format_extension(comp_format));
newitems.push_back(_("Back to Turn ") + lexical_cast<std::string>(turn));
}
}
const std::string& start_name = gamestate_.classification().label;
if(savegame::save_game_exists(start_name, preferences::compress_saves())) {
if(preferences::compress_saves()) {
newsaves.push_back(start_name + (preferences::bzip2_savegame_compression() ? ".bz2" : ".gz"));
} else {
newsaves.push_back(start_name);
}
if(savegame::save_game_exists(start_name, comp_format)) {
newsaves.push_back(
start_name + compression::format_extension(comp_format));
newitems.push_back(_("Back to Start"));
}

View file

@ -495,7 +495,7 @@ LEVEL_RESULT play_game(game_display& disp, game_state& gamestate,
savegame::clean_saves(gamestate.classification().label);
if (preferences::save_replays() && end_level.replay_save) {
savegame::replay_savegame save(gamestate, preferences::compress_saves());
savegame::replay_savegame save(gamestate, preferences::save_compression_format());
save.save_game_automatic(disp.video(), true);
}
}
@ -642,7 +642,7 @@ LEVEL_RESULT play_game(game_display& disp, game_state& gamestate,
// starting position needs to be empty,
// to force a reload of the scenario config.
savegame::scenariostart_savegame save(gamestate, preferences::compress_saves());
savegame::scenariostart_savegame save(gamestate, preferences::save_compression_format());
save.save_game_automatic(disp.video());
}

View file

@ -544,7 +544,7 @@ LEVEL_RESULT playsingle_controller::play_scenario(
disconnect = true;
}
savegame::ingame_savegame save(gamestate_, *gui_, to_config(), preferences::compress_saves());
savegame::ingame_savegame save(gamestate_, *gui_, to_config(), preferences::save_compression_format());
save.save_game_interactive(gui_->video(), _("A network disconnection has occurred, and the game cannot continue. Do you want to save the game?"), gui::YES_NO);
if(disconnect) {
throw network::error();
@ -700,7 +700,7 @@ void playsingle_controller::before_human_turn(bool save)
ai::manager::raise_turn_started();
if(save && level_result_ == NONE) {
savegame::autosave_savegame save(gamestate_, *gui_, to_config(), preferences::compress_saves());
savegame::autosave_savegame save(gamestate_, *gui_, to_config(), preferences::save_compression_format());
save.autosave(game_config::disable_autosave, preferences::autosavemax(), preferences::INFINITE_AUTO_SAVES);
}

View file

@ -824,15 +824,5 @@ bool use_twelve_hour_clock_format()
return get("use_twelve_hour_clock_format", false);
}
bool bzip2_savegame_compression()
{
return get("bzip2_savegame_compression", false);
}
void set_bzip2_savegame_compression(bool ison)
{
preferences::set("bzip2_savegame_compression", ison);
}
} // end namespace preferences

View file

@ -209,9 +209,6 @@ namespace preferences {
bool use_twelve_hour_clock_format();
void set_use_twelve_hour_clock_format(bool value);
bool bzip2_savegame_compression();
void set_bzip2_savegame_compression(bool value);
} // end namespace preferences
#endif

View file

@ -182,10 +182,11 @@ public:
log_scope("write_save_index()");
try {
scoped_ostream stream = ostream_file(get_save_index_file());
if (preferences::compress_saves()) {
write_gz(*stream, data());
if (preferences::save_compression_format() != compression::NONE) {
// TODO: maybe allow writing this using bz2 too?
write_gz(*stream, data());
} else {
write(*stream, data());
write(*stream, data());
}
} catch(io_exception& e) {
ERR_SAVE << "error writing to save index file: '" << e.what() << "'\n";
@ -398,14 +399,12 @@ void read_save_file(const std::string& name, config& cfg, std::string* error_log
}
}
bool save_game_exists(const std::string& name, bool compress_saves)
bool save_game_exists(const std::string& name, compression::format compressed)
{
std::string fname = name;
replace_space2underbar(fname);
if(compress_saves) {
fname += preferences::bzip2_savegame_compression() ? ".bz2" : ".gz";
}
fname += compression::format_extension(compressed);
return file_exists(get_saves_dir() + "/" + fname);
}
@ -762,7 +761,7 @@ void loadgame::copy_era(config &cfg)
snapshot.add_child("era", era);
}
savegame::savegame(game_state& gamestate, const bool compress_saves, const std::string& title)
savegame::savegame(game_state& gamestate, const compression::format compress_saves, const std::string& title)
: gamestate_(gamestate)
, snapshot_()
, filename_()
@ -945,14 +944,11 @@ void savegame::write_game_to_disk(const std::string& filename)
LOG_SAVE << "savegame::save_game";
filename_ = filename;
if (compress_saves_) {
filename_ += preferences::bzip2_savegame_compression() ? ".bz2" : ".gz";
}
filename_ += compression::format_extension(compress_saves_);
std::stringstream ss;
{
config_writer out(ss, compress_saves_ ? preferences::bzip2_savegame_compression() ? config_writer::BZIP2 : config_writer::GZIP : config_writer::NONE);
config_writer out(ss, compress_saves_);
write_game(out);
finish_save_game(out);
}
@ -1002,7 +998,7 @@ scoped_ostream savegame::open_save_game(const std::string &label)
}
}
scenariostart_savegame::scenariostart_savegame(game_state &gamestate, const bool compress_saves)
scenariostart_savegame::scenariostart_savegame(game_state &gamestate, const compression::format compress_saves)
: savegame(gamestate, compress_saves)
{
set_filename(gamestate.classification().label);
@ -1014,7 +1010,7 @@ void scenariostart_savegame::write_game(config_writer &out){
out.write_child("carryover_sides_start", gamestate().carryover_sides_start);
}
replay_savegame::replay_savegame(game_state &gamestate, const bool compress_saves)
replay_savegame::replay_savegame(game_state &gamestate, const compression::format compress_saves)
: savegame(gamestate, compress_saves, _("Save Replay"))
{}
@ -1039,7 +1035,7 @@ void replay_savegame::write_game(config_writer &out) {
}
autosave_savegame::autosave_savegame(game_state &gamestate,
game_display& gui, const config& snapshot_cfg, const bool compress_saves)
game_display& gui, const config& snapshot_cfg, const compression::format compress_saves)
: ingame_savegame(gamestate, gui, snapshot_cfg, compress_saves)
{
set_error_message(_("Could not auto save the game. Please save the game manually."));
@ -1067,7 +1063,7 @@ void autosave_savegame::create_filename()
}
oos_savegame::oos_savegame(const config& snapshot_cfg)
: ingame_savegame(*resources::state_of_game, *resources::screen, snapshot_cfg, preferences::compress_saves())
: ingame_savegame(*resources::state_of_game, *resources::screen, snapshot_cfg, preferences::save_compression_format())
{}
int oos_savegame::show_save_dialog(CVideo& video, const std::string& message, const gui::DIALOG_TYPE /*dialog_type*/)
@ -1090,7 +1086,7 @@ int oos_savegame::show_save_dialog(CVideo& video, const std::string& message, co
}
ingame_savegame::ingame_savegame(game_state &gamestate,
game_display& gui, const config& snapshot_cfg, const bool compress_saves)
game_display& gui, const config& snapshot_cfg, const compression::format compress_saves)
: savegame(gamestate, compress_saves, _("Save Game")),
gui_(gui)
{

View file

@ -20,6 +20,7 @@
#include "gamestatus.hpp"
#include "tod_manager.hpp"
#include "show_dialog.hpp"
#include "serialization/compression.hpp"
class config_writer;
class game_display;
@ -64,7 +65,7 @@ std::vector<save_info> get_saves_list(const std::string* dir = NULL, const std::
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, bool compress_saves);
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);
@ -141,7 +142,7 @@ class savegame
protected:
/** The only constructor of savegame. The title parameter is only necessary if you
intend to do interactive saves. */
savegame(game_state& gamestate, const bool compress_saves, const std::string& title = "Save");
savegame(game_state& gamestate, const compression::format compress_saves, const std::string& title = "Save");
public:
virtual ~savegame() {}
@ -223,7 +224,7 @@ private:
bool show_confirmation_; /** Determines if a confirmation of successful saving the game is shown. */
bool compress_saves_; /** Determines, if compression is used for the savegame file */
compression::format compress_saves_; /** Determines, what compression format is used for the savegame file */
};
/** Class for "normal" midgame saves. The additional members are needed for creating the snapshot
@ -232,7 +233,7 @@ class ingame_savegame : public savegame
{
public:
ingame_savegame(game_state& gamestate,
game_display& gui, const config& snapshot_cfg, const bool compress_saves);
game_display& gui, const config& snapshot_cfg, const compression::format compress_saves);
private:
/** Create a filename for automatic saves */
@ -251,7 +252,7 @@ protected:
class replay_savegame : public savegame
{
public:
replay_savegame(game_state& gamestate, const bool compress_saves);
replay_savegame(game_state& gamestate, const compression::format compress_saves);
private:
/** Create a filename for automatic saves */
@ -265,7 +266,7 @@ class autosave_savegame : public ingame_savegame
{
public:
autosave_savegame(game_state &gamestate,
game_display& gui, const config& snapshot_cfg, const bool compress_saves);
game_display& gui, const config& snapshot_cfg, const compression::format compress_saves);
void autosave(const bool disable_autosave, const int autosave_max, const int infinite_autosaves);
private:
@ -287,7 +288,7 @@ private:
class scenariostart_savegame : public savegame
{
public:
scenariostart_savegame(game_state& gamestate, const bool compress_saves);
scenariostart_savegame(game_state& gamestate, const compression::format compress_saves);
private:
void write_game(config_writer &out);

View file

@ -34,7 +34,7 @@ static lg::log_domain log_config("config");
#define ERR_CF LOG_STREAM(err, log_config)
config_writer::config_writer(
std::ostream &out, compressor compress) :
std::ostream &out, compression::format compress) :
filter_(),
out_ptr_(compress ? &filter_ : &out), //ternary indirection creates a temporary
out_(*out_ptr_), //now MSVC will allow binding to the reference member
@ -42,11 +42,11 @@ config_writer::config_writer(
level_(0),
textdomain_(PACKAGE)
{
if(compress_ == GZIP) {
if(compress_ == compression::GZIP) {
filter_.push(boost::iostreams::gzip_compressor(boost::iostreams::gzip_params(9)));
filter_.push(out);
} else if(compress_ == BZIP2) {
} else if(compress_ == compression::BZIP2) {
filter_.push(boost::iostreams::bzip2_compressor(boost::iostreams::bzip2_params()));
filter_.push(out);
}
@ -56,7 +56,7 @@ config_writer::config_writer(
filter_(),
out_ptr_(compress ? &filter_ : &out), //ternary indirection creates a temporary
out_(*out_ptr_), //now MSVC will allow binding to the reference member
compress_(compress ? GZIP : NONE),
compress_(compress ? compression::GZIP : compression::NONE),
level_(0),
textdomain_(PACKAGE)
{
@ -73,7 +73,7 @@ config_writer::config_writer(
config_writer::~config_writer()
{
//we only need this for gzip but we also do it for bz2 for unification.
if(compress_ == GZIP || compress_ == BZIP2)
if(compress_ == compression::GZIP || compress_ == compression::BZIP2)
{
// prevent empty gz files because of https://svn.boost.org/trac/boost/ticket/5237
out_ << "\n";

View file

@ -20,6 +20,7 @@
#include "config.hpp"
#include "preprocessor.hpp"
#include "serialization/compression.hpp"
#include "serialization/parser.hpp"
#include <boost/iostreams/filtering_stream.hpp>
@ -28,8 +29,7 @@
class config_writer
{
public:
enum compressor { NONE, GZIP, BZIP2 };
config_writer(std::ostream &out, compressor compress);
config_writer(std::ostream &out, compression::format compress);
config_writer(std::ostream &out, bool compress, int level = -1);
/** Default implementation, but defined out-of-line for efficiency reasons. */
~config_writer();
@ -55,7 +55,7 @@ private:
boost::iostreams::filtering_stream<boost::iostreams::output> filter_;
std::ostream *out_ptr_;
std::ostream &out_;
compressor compress_;
compression::format compress_;
unsigned int level_;
std::string textdomain_;
};