Skip to content

Commit

Permalink
Add other versions of Wesnoth to the Load Game dialog
Browse files Browse the repository at this point in the history
If a save directory from another version is found, a drop-down list of
directories is added below the filter box in the Load Game dialog.

Refactor save_index_class to allow instances for other directories.

Design idea: all new saves go in to the main save directory. Games can be
loaded from other directories, but those directories are treated as read-only.
When building an index for other directories, the index is kept in memory and
not written to disk - this results in some noticeable lag in the UI when
opening the other versions' directories, but not the usual save dir.
  • Loading branch information
stevecotton committed Oct 29, 2019
1 parent 66e2d53 commit c46070d
Show file tree
Hide file tree
Showing 15 changed files with 385 additions and 122 deletions.
17 changes: 17 additions & 0 deletions data/gui/window/game_load.cfg
Expand Up @@ -426,6 +426,23 @@

[/row]

[row]
[column]
grow_factor = 1
border = "all"
border_size = 5
horizontal_alignment = "left"

[menu_button]
id = "dirList"
definition = "default"
tooltip = _ "Show saves from a different version of Wesnoth"
[/menu_button]

[/column]

[/row]

[row]
grow_factor = 1

Expand Down
43 changes: 39 additions & 4 deletions src/filesystem.cpp
Expand Up @@ -23,11 +23,11 @@
#include "config.hpp"
#include "deprecation.hpp"
#include "game_config.hpp"
#include "game_version.hpp"
#include "gettext.hpp"
#include "log.hpp"
#include "serialization/unicode.hpp"
#include "serialization/unicode_cast.hpp"
#include "game_version.hpp"

#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
Expand Down Expand Up @@ -513,6 +513,13 @@ std::string get_next_filename(const std::string& name, const std::string& extens

static bfs::path user_data_dir, user_config_dir, cache_dir;

static const std::string get_version_path_suffix(const version_info& version)
{
std::ostringstream s;
s << version.major_version() << '.' << version.minor_version();
return s.str();
}

static const std::string& get_version_path_suffix()
{
static std::string suffix;
Expand All @@ -521,9 +528,7 @@ static const std::string& get_version_path_suffix()
// the version number cannot change during runtime.

if(suffix.empty()) {
std::ostringstream s;
s << game_config::wesnoth_version.major_version() << '.' << game_config::wesnoth_version.minor_version();
suffix = s.str();
suffix = get_version_path_suffix(game_config::wesnoth_version);
}

return suffix;
Expand Down Expand Up @@ -817,6 +822,36 @@ std::string get_cache_dir()
return cache_dir.string();
}

std::vector<other_version_dir> find_other_version_saves_dirs()
{
const auto& w_ver = game_config::wesnoth_version;
const auto& ms_ver = game_config::min_savegame_version;

if(w_ver.major_version() != 1 || ms_ver.major_version() != 1) {
// Unimplemented, assuming that version 2 won't use WML-based saves
return {};
}

std::vector<other_version_dir> result;

// For 1.16, check for saves from all versions up to 1.20.
for(auto minor = ms_ver.minor_version(); minor <= w_ver.minor_version() + 4; ++minor) {
if(minor == w_ver.minor_version())
continue;

auto version = version_info{};
version.set_major_version(w_ver.major_version());
version.set_minor_version(minor);
auto suffix = get_version_path_suffix(version);
auto path = get_user_data_path().parent_path() / suffix / "saves";
if(bfs::exists(path)) {
result.emplace_back(suffix, path.generic_string());
}
}

return result;
}

std::string get_cwd()
{
error_code ec;
Expand Down
33 changes: 30 additions & 3 deletions src/filesystem.hpp
Expand Up @@ -23,9 +23,9 @@
#include <ctime>
#include <functional>
#include <iosfwd>
#include <memory>
#include <string>
#include <vector>
#include <memory>

#include "exceptions.hpp"
#include "serialization/string_utils.hpp"
Expand Down Expand Up @@ -172,19 +172,46 @@ std::string get_user_config_dir();
std::string get_user_data_dir();
std::string get_cache_dir();

struct other_version_dir
{
/**
* Here the version is given as a string instead of a version_info, because the
* logic of how many components are significant ("1.16" rather than
* "1.16.0") is encapsulated in find_other_version_saves_dirs().
*/
std::string version;
std::string path;

// constructor because emplace_back() doesn't use aggregate initialization
other_version_dir(const std::string& v, const std::string& p)
: version(v)
, path(p)
{
}
};

/**
* Searches for directories containing saves created by other versions of Wesnoth.
*
* The directories returned will exist, but might not contain any saves. Changes to
* the filesystem (by running other versions or by deleting old directories) may
* change the results returned by the function.
*/
std::vector<other_version_dir> find_other_version_saves_dirs();

std::string get_cwd();
std::string get_exe_dir();

bool make_directory(const std::string& dirname);
bool delete_directory(const std::string& dirname, const bool keep_pbl = false);
bool delete_file(const std::string &filename);
bool delete_file(const std::string& filename);

bool looks_like_pbl(const std::string& file);

// Basic disk I/O:

/** Basic disk I/O - read file. */
std::string read_file(const std::string &fname);
std::string read_file(const std::string& fname);
filesystem::scoped_istream istream_file(const std::string& fname, bool treat_failure_as_error = true);
filesystem::scoped_ostream ostream_file(const std::string& fname, bool create_directory = true);
/** Throws io_exception if an error occurs. */
Expand Down
9 changes: 5 additions & 4 deletions src/game_launcher.cpp
Expand Up @@ -50,6 +50,7 @@
#include "preferences/general.hpp" // for disable_preferences_save, etc
#include "preferences/display.hpp"
#include "savegame.hpp" // for clean_saves, etc
#include "save_index.hpp"
#include "scripting/application_lua_kernel.hpp"
#include "sdl/surface.hpp" // for surface
#include "serialization/compression.hpp" // for format::NONE
Expand Down Expand Up @@ -185,14 +186,14 @@ game_launcher::game_launcher(const commandline_options& cmdline_opts, const char
{
jump_to_editor_ = true;
if (!cmdline_opts_.editor->empty())
load_data_.reset(new savegame::load_game_metadata{ *cmdline_opts_.editor });
load_data_.reset(new savegame::load_game_metadata{ savegame::save_index_class::default_saves_dir(), *cmdline_opts_.editor });
}
if (cmdline_opts_.fps)
preferences::set_show_fps(true);
if (cmdline_opts_.fullscreen)
video().set_fullscreen(true);
if (cmdline_opts_.load)
load_data_.reset(new savegame::load_game_metadata{ *cmdline_opts_.load });
load_data_.reset(new savegame::load_game_metadata{ savegame::save_index_class::default_saves_dir(), *cmdline_opts_.load });
if (cmdline_opts_.max_fps) {
int fps = utils::clamp(*cmdline_opts_.max_fps, 1, 1000);
fps = 1000 / fps;
Expand Down Expand Up @@ -522,7 +523,7 @@ int game_launcher::unit_test()
savegame::replay_savegame save(state_, compression::NONE);
save.save_game_automatic(false, "unit_test_replay"); //false means don't check for overwrite

load_data_.reset(new savegame::load_game_metadata{ "unit_test_replay" , "", true, true, false });
load_data_.reset(new savegame::load_game_metadata{ savegame::save_index_class::default_saves_dir(), "unit_test_replay" , "", true, true, false });

if (!load_game()) {
std::cerr << "Failed to load the replay!" << std::endl;
Expand Down Expand Up @@ -601,7 +602,7 @@ bool game_launcher::load_game()

DBG_GENERAL << "Current campaign type: " << state_.classification().campaign_type << std::endl;

savegame::loadgame load(game_config_manager::get()->game_config(), state_);
savegame::loadgame load(savegame::save_index_class::default_saves_dir(), game_config_manager::get()->game_config(), state_);
if (load_data_) {
std::unique_ptr<savegame::load_game_metadata> load_data = std::move(load_data_);
load.data() = std::move(*load_data);
Expand Down

0 comments on commit c46070d

Please sign in to comment.