The Battle for Wesnoth  1.15.9+dev
savegame.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by Jörg Hinrichs, refactored from various
3  places formerly created by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include <boost/iostreams/filter/gzip.hpp>
17 
18 #include "savegame.hpp"
19 
20 #include "carryover.hpp"
21 #include "cursor.hpp"
22 #include "format_time_summary.hpp"
23 #include "formatter.hpp"
24 #include "formula/string_utils.hpp"
25 #include "game_config_manager.hpp"
26 #include "game_config_view.hpp"
27 #include "game_end_exceptions.hpp"
28 #include "game_errors.hpp"
29 #include "game_version.hpp"
30 #include "gettext.hpp"
34 #include "gui/dialogs/message.hpp"
36 #include "gui/widgets/retval.hpp"
37 #include "gui/widgets/settings.hpp"
38 #include "log.hpp"
39 #include "persist_manager.hpp"
40 #include "preferences/game.hpp"
41 #include "resources.hpp"
42 #include "save_index.hpp"
43 #include "saved_game.hpp"
45 #include "serialization/parser.hpp"
47 #include "statistics.hpp"
48 #include "video.hpp"
49 
50 #include <algorithm>
51 #include <iomanip>
52 
53 static lg::log_domain log_engine("engine");
54 #define LOG_SAVE LOG_STREAM(info, log_engine)
55 #define ERR_SAVE LOG_STREAM(err, log_engine)
56 
57 static lg::log_domain log_enginerefac("enginerefac");
58 #define LOG_RG LOG_STREAM(info, log_enginerefac)
59 
60 namespace savegame
61 {
62 bool save_game_exists(std::string name, compression::format compressed)
63 {
64  name += compression::format_extension(compressed);
65  auto manager = save_index_class::default_saves_dir();
66  return filesystem::file_exists(manager->dir() + "/" + name);
67 }
68 
69 void clean_saves(const std::string& label)
70 {
71  const std::string prefix = label + "-" + _("Auto-Save");
72  LOG_SAVE << "Cleaning saves with prefix '" << prefix << "'\n";
73 
74  auto manager = save_index_class::default_saves_dir();
75  for(const auto& save : manager->get_saves_list()) {
76  if(save.name().compare(0, prefix.length(), prefix) == 0) {
77  LOG_SAVE << "Deleting savegame '" << save.name() << "'\n";
78  manager->delete_game(save.name());
79  }
80  }
81 }
82 
83 loadgame::loadgame(const std::shared_ptr<save_index_class>& index, saved_game& gamestate)
84  : game_config_(game_config_manager::get()->game_config())
85  , gamestate_(gamestate)
86  , load_data_{index}
87 {
88 }
89 
91 {
92  if(load_data_.summary["corrupt"].to_bool()) {
93  return false;
94  }
95 
96  std::string campaign_id = load_data_.summary["campaign"];
97 
98  for(const config& campaign : game_config_.child_range("campaign")) {
99  if(campaign["id"] != campaign_id) {
100  continue;
101  }
102 
103  gui2::dialogs::campaign_difficulty difficulty_dlg(campaign);
104 
105  // Return if canceled, since otherwise load_data_.difficulty will be set to 'CANCEL'
106  if(!difficulty_dlg.show()) {
107  return false;
108  }
109 
110  load_data_.difficulty = difficulty_dlg.selected_difficulty();
112 
113  // Exit loop
114  break;
115  }
116 
117  return true;
118 }
119 
120 // Called only by play_controller to handle in-game attempts to load. Instead of returning true,
121 // throws a "load_game_exception" to signal a resulting load game request.
123 {
124  if(CVideo::get_singleton().faked()) {
125  return false;
126  }
127 
129  return false;
130  }
131 
132  if(load_data_.filename.empty()) {
133  return false;
134  }
135 
137  if(!show_difficulty_dialog()) {
138  return false;
139  }
140  }
141 
142  if(!load_data_.manager) {
143  ERR_SAVE << "Null pointer in save index" << std::endl;
144  return false;
145  }
146 
148 
149  // Confirm the integrity of the file before throwing the exception.
150  // Use the summary in the save_index for this.
151  const config& summary = load_data_.manager->get(load_data_.filename);
152 
153  if(summary["corrupt"].to_bool(false)) {
154  gui2::show_error_message(_("The file you have tried to load is corrupt: '"));
155  return false;
156  }
157 
158  if(!loadgame::check_version_compatibility(summary["version"].str())) {
159  return false;
160  }
161 
162  throw load_game_exception(std::move(load_data_));
163 }
164 
166 {
167  bool skip_version_check = true;
168 
169  if(load_data_.filename.empty()) {
171  return false;
172  }
173 
174  skip_version_check = false;
176  }
177 
178  if(load_data_.filename.empty()) {
179  return false;
180  }
181 
183  if(!show_difficulty_dialog()) {
184  return false;
185  }
186  }
187 
188  if(!load_data_.manager) {
189  ERR_SAVE << "Null pointer in save index" << std::endl;
190  return false;
191  }
192 
193  std::string error_log;
195 
197 
198  for(config& side : load_data_.load_config.child_range("side")) {
199  side.remove_attribute("is_local");
200  }
201 
202  if(!error_log.empty()) {
203  try {
205  _("Warning: The file you have tried to load is corrupt. Loading anyway.\n") + error_log);
206  } catch(const utf8::invalid_utf8_exception&) {
207  gui2::show_error_message(_("Warning: The file you have tried to load is corrupt. Loading anyway.\n")
208  + std::string("(UTF-8 ERROR)"));
209  }
210  }
211 
212  if(!load_data_.difficulty.empty()) {
214  }
215  // read classification to for loading the game_config config object.
217 
218  if(skip_version_check) {
219  return true;
220  }
221 
223 }
224 
226 {
228 }
229 
231 {
232  if(save_version == game_config::wesnoth_version) {
233  return true;
234  }
235 
237 
238  // Even minor version numbers indicate stable releases which are
239  // compatible with each other.
240  if(wesnoth_version.minor_version() % 2 == 0 && wesnoth_version.major_version() == save_version.major_version()
241  && wesnoth_version.minor_version() == save_version.minor_version()) {
242  return true;
243  }
244 
245  // Do not load if too old. If either the savegame or the current
246  // game has the version 'test', load. This 'test' version is never
247  // supposed to occur, except when Soliton is testing MP servers.
248  if(save_version < game_config::min_savegame_version && save_version != game_config::test_version
249  && wesnoth_version != game_config::test_version) {
250  const std::string message
251  = _("This save is from an old, unsupported version ($version_number|) and cannot be loaded.");
252  utils::string_map symbols;
253  symbols["version_number"] = save_version.str();
255  return false;
256  }
257 
259  const std::string message
260  = _("This save is from a different version of the game ($version_number|). Do you wish to try to load it?");
261  utils::string_map symbols;
262  symbols["version_number"] = save_version.str();
263  const int res = gui2::show_message(_("Load Game"), utils::interpolate_variables_into_string(message, &symbols),
265  return res == gui2::retval::OK;
266  }
267 
268  return true;
269 }
270 
272 {
274 }
275 
277 {
279  return false;
280  }
281 
283  if(load_data_.filename.empty()) {
284  return false;
285  }
286 
287  if(!load_data_.manager) {
288  ERR_SAVE << "Null pointer in save index" << std::endl;
289  return false;
290  }
291 
292  // read_save_file needs to be called before we can verify the classification so the data has
293  // been populated. Since we do that, we report any errors in that process first.
294  std::string error_log;
295  {
297  log_scope("load_game");
298 
301  }
302 
303  if(!error_log.empty()) {
304  gui2::show_error_message(_("The file you have tried to load is corrupt: '") + error_log);
305  return false;
306  }
307 
309  gui2::show_transient_message(_("Load Game"), _("Replays are not supported in multiplayer mode."));
310  return false;
311  }
312 
313  // We want to verify the game classification before setting the data, so we don't check on
314  // gamestate_.classification() and instead construct a game_classification object manually.
315  if(game_classification(load_data_.load_config).campaign_type != game_classification::CAMPAIGN_TYPE::MULTIPLAYER) {
316  gui2::show_transient_error_message(_("This is not a multiplayer save."));
317  return false;
318  }
319 
320  set_gamestate();
321 
323 }
324 
326 {
327  const config& replay_start = cfg.child("replay_start");
328  if(!replay_start) {
329  return;
330  }
331 
332  const config& era = replay_start.child("era");
333  if(!era) {
334  return;
335  }
336 
337  config& snapshot = cfg.child("snapshot");
338  if(!snapshot) {
339  return;
340  }
341 
342  snapshot.add_child("era", era);
343 }
344 
345 savegame::savegame(saved_game& gamestate, const compression::format compress_saves, const std::string& title)
346  : filename_()
347  , title_(title)
348  , save_index_manager_(save_index_class::default_saves_dir())
349  , gamestate_(gamestate)
350  , error_message_(_("The game could not be saved: "))
351  , show_confirmation_(false)
352  , compress_saves_(compress_saves)
353 {
354 }
355 
356 bool savegame::save_game_automatic(bool ask_for_overwrite, const std::string& filename)
357 {
358  if(filename.empty()) {
359  filename_ = create_filename();
360  } else {
361  filename_ = filename;
362  }
363 
364  if(ask_for_overwrite) {
365  if(!check_overwrite()) {
366  return save_game_interactive("", savegame::OK_CANCEL);
367  }
368  }
369 
370  return save_game();
371 }
372 
373 bool savegame::save_game_interactive(const std::string& message, DIALOG_TYPE dialog_type)
374 {
375  show_confirmation_ = true;
376  filename_ = create_filename();
377 
378  const int res = show_save_dialog(message, dialog_type);
379 
380  if(res == 2) {
381  throw_quit_game_exception(); // Quit game
382  }
383 
384  if(res == gui2::retval::OK && check_overwrite()) {
385  return save_game();
386  }
387 
388  return false;
389 }
390 
391 int savegame::show_save_dialog(const std::string& message, DIALOG_TYPE dialog_type)
392 {
393  int res = 0;
394 
395  if(dialog_type == OK_CANCEL) {
396  gui2::dialogs::game_save dlg(filename_, title_);
397  dlg.show();
398  res = dlg.get_retval();
399  } else if(dialog_type == YES_NO) {
400  gui2::dialogs::game_save_message dlg(filename_, title_, message);
401  dlg.show();
402  res = dlg.get_retval();
403  }
404 
405  if(!check_filename(filename_)) {
406  res = gui2::retval::CANCEL;
407  }
408 
409  return res;
410 }
411 
412 bool savegame::check_overwrite()
413 {
414  if(!save_game_exists(filename_, compress_saves_)) {
415  return true;
416  }
417 
418  std::ostringstream message;
419  message << _("Save already exists. Do you want to overwrite it?") << "\n" << _("Name: ") << filename_;
420  const int res = gui2::show_message(_("Overwrite?"), message.str(), gui2::dialogs::message::yes_no_buttons);
421  return res == gui2::retval::OK;
422 }
423 
424 bool savegame::check_filename(const std::string& filename)
425 {
426  if(filesystem::is_compressed_file(filename)) {
427  gui2::show_error_message(_("Save names should not end on '.gz' or '.bz2'. Please remove the extension."));
428  return false;
429  }
430 
431  return true;
432 }
433 
434 std::string savegame::create_filename(unsigned int turn_number) const
435 {
436  return create_initial_filename(turn_number);
437 }
438 
439 void savegame::before_save()
440 {
441 }
442 
443 bool savegame::save_game(const std::string& filename)
444 {
445  try {
446  uint32_t start, end;
447  start = SDL_GetTicks();
448 
449  if(filename_.empty()) {
450  filename_ = filename;
451  }
452 
453  before_save();
454 
455  write_game_to_disk(filename_);
456  if(resources::persist != nullptr) {
459  }
460 
461  // Create an entry in the save_index. Doing this here ensures all leader image paths
462  // sre expanded in a context-independent fashion and can appear in the Load Game dialog
463  // even if a campaign-specific sprite is used. This is because the image's full path is
464  // only available if the binary-path context its a part of is loaded. Without this, if
465  // a player saves a game and exits the game or reloads the cache, the leader image will
466  // only be available within that specific binary context (when playing another game from
467  // the came campaign, for example).
468  save_index_manager_->rebuild(filename_);
469 
470  end = SDL_GetTicks();
471  LOG_SAVE << "Milliseconds to save " << filename_ << ": " << end - start << std::endl;
472 
473  if(show_confirmation_) {
474  gui2::show_transient_message(_("Saved"), _("The game has been saved."));
475  }
476 
477  return true;
478  } catch(const game::save_game_failed& e) {
479  ERR_SAVE << error_message_ << e.message << std::endl;
480  gui2::show_error_message(error_message_ + e.message);
481 
482  // do not bother retrying, since the user can just try to save the game again
483  // maybe show a yes-no dialog for "disable autosaves now"?
484  return false;
485  };
486 }
487 
488 void savegame::write_game_to_disk(const std::string& filename)
489 {
490  LOG_SAVE << "savegame::save_game" << std::endl;
491 
492  filename_ = filename;
493  filename_ += compression::format_extension(compress_saves_);
494 
495  std::stringstream ss;
496  {
497  config_writer out(ss, compress_saves_);
498  write_game(out);
499  finish_save_game(out);
500  }
501 
502  filesystem::scoped_ostream os(open_save_game(filename_));
503  (*os) << ss.str();
504 
505  if(!os->good()) {
506  throw game::save_game_failed(_("Could not write to file"));
507  }
508 }
509 
510 void savegame::write_game(config_writer& out)
511 {
512  log_scope("write_game");
513 
514  out.write_key_val("version", game_config::wesnoth_version.str());
515 
516  gamestate_.write_general_info(out);
517  out.open_child("statistics");
519  out.close_child("statistics");
520 }
521 
522 void savegame::finish_save_game(const config_writer& out)
523 {
524  try {
525  if(!out.good()) {
526  throw game::save_game_failed(_("Could not write to file"));
527  }
528 
529  save_index_manager_->remove(gamestate_.classification().label);
530  } catch(const filesystem::io_exception& e) {
531  throw game::save_game_failed(e.what());
532  }
533 }
534 
535 // Throws game::save_game_failed
536 filesystem::scoped_ostream savegame::open_save_game(const std::string& label)
537 {
538  try {
539  return filesystem::ostream_file(save_index_manager_->dir() + "/" + label);
540  } catch(const filesystem::io_exception& e) {
541  throw game::save_game_failed(e.what());
542  }
543 }
544 
546  : savegame(gamestate, compress_saves)
547 {
549 }
550 
551 std::string scenariostart_savegame::create_initial_filename(unsigned int) const
552 {
553  return gamestate().classification().label;
554 }
555 
557 {
559  gamestate().write_carryover(out);
560 }
561 
563  : savegame(gamestate, compress_saves, _("Save Replay"))
564 {
565 }
566 
567 std::string replay_savegame::create_initial_filename(unsigned int) const
568 {
569  time_t t = std::time(nullptr);
570  tm tm = *std::localtime(&t);
571  auto time = std::put_time(&tm, "%Y%m%d-%H%M%S");
572 
573  // TRANSLATORS: This string is used as part of a filename, as in, "HttT-The Elves Besieged replay.gz"
574  return formatter() << gamestate().classification().label << " " << _("replay") << " " << time;
575 }
576 
578 {
580 
581  gamestate().write_carryover(out);
582  out.write_child("replay_start", gamestate().replay_start());
583 
584  out.open_child("replay");
585  gamestate().get_replay().write(out);
586  out.close_child("replay");
587 }
588 
590  : ingame_savegame(gamestate, compress_saves)
591 {
592  set_error_message(_("Could not auto save the game. Please save the game manually."));
593 }
594 
595 void autosave_savegame::autosave(const bool disable_autosave, const int autosave_max, const int infinite_autosaves)
596 {
597  if(disable_autosave)
598  return;
599 
601 
602  auto manager = save_index_class::default_saves_dir();
603  manager->delete_old_auto_saves(autosave_max, infinite_autosaves);
604 }
605 
606 std::string autosave_savegame::create_initial_filename(unsigned int turn_number) const
607 {
608  std::string filename;
609  if(gamestate().classification().label.empty()) {
610  filename = _("Auto-Save");
611  } else {
612  filename = gamestate().classification().label + "-" + _("Auto-Save") + std::to_string(turn_number);
613  }
614 
615  return filename;
616 }
617 
620  , ignore_(ignore)
621 {
622 }
623 
624 int oos_savegame::show_save_dialog(const std::string& message, DIALOG_TYPE /*dialog_type*/)
625 {
626  int res = 0;
627 
628  if(!ignore_) {
630  dlg.show();
631  res = dlg.get_retval();
632  }
633 
634  if(!check_filename(filename_)) {
635  res = gui2::retval::CANCEL;
636  }
637 
638  return res;
639 }
640 
642  : savegame(gamestate, compress_saves, _("Save Game"))
643 {
644 }
645 
646 std::string ingame_savegame::create_initial_filename(unsigned int turn_number) const
647 {
648  return formatter() << gamestate().classification().label << " " << _("Turn") << " " << turn_number;
649 }
650 
652 {
653  log_scope("write_game");
654 
655  if(!gamestate().get_starting_point().validate_wml()) {
656  throw game::save_game_failed(_("Game state is corrupted"));
657  }
658 
660 
661  gamestate().write_carryover(out);
662  out.write_child("snapshot", gamestate().get_starting_point());
663  out.write_child("replay_start", gamestate().replay_start());
664  out.open_child("replay");
665  gamestate().get_replay().write(out);
666  out.close_child("replay");
667 }
668 
669 // changes done during 1.11.0-dev
671 {
672  if(!cfg.has_child("snapshot")) {
673  return;
674  }
675 
676  const config& snapshot = cfg.child("snapshot");
677  const config& replay_start = cfg.child("replay_start");
678  const config& replay = cfg.child("replay");
679 
680  if(!cfg.has_child("carryover_sides") && !cfg.has_child("carryover_sides_start")) {
682  // copy rng and menu items from toplevel to new carryover_sides
683  carryover["random_seed"] = cfg["random_seed"];
684  carryover["random_calls"] = cfg["random_calls"];
685 
686  for(const config& menu_item : cfg.child_range("menu_item")) {
687  carryover.add_child("menu_item", menu_item);
688  }
689 
690  carryover["difficulty"] = cfg["difficulty"];
691  carryover["random_mode"] = cfg["random_mode"];
692  // the scenario to be played is always stored as next_scenario in carryover_sides_start
693  carryover["next_scenario"] = cfg["scenario"];
694 
695  config carryover_start = carryover;
696 
697  // copy sides from either snapshot or replay_start to new carryover_sides
698  if(!snapshot.empty()) {
699  for(const config& side : snapshot.child_range("side")) {
700  carryover.add_child("side", side);
701  }
702  // for compatibility with old savegames that use player instead of side
703  for(const config& side : snapshot.child_range("player")) {
704  carryover.add_child("side", side);
705  }
706  // save the sides from replay_start in carryover_sides_start
707  for(const config& side : replay_start.child_range("side")) {
708  carryover_start.add_child("side", side);
709  }
710  // for compatibility with old savegames that use player instead of side
711  for(const config& side : replay_start.child_range("player")) {
712  carryover_start.add_child("side", side);
713  }
714  } else if(!replay_start.empty()) {
715  for(const config& side : replay_start.child_range("side")) {
716  carryover.add_child("side", side);
717  carryover_start.add_child("side", side);
718  }
719  // for compatibility with old savegames that use player instead of side
720  for(const config& side : replay_start.child_range("player")) {
721  carryover.add_child("side", side);
722  carryover_start.add_child("side", side);
723  }
724  }
725 
726  // get variables according to old hierarchy and copy them to new carryover_sides
727  if(!snapshot.empty()) {
728  if(const config& variables_from_snapshot = snapshot.child("variables")) {
729  carryover.add_child("variables", variables_from_snapshot);
730  carryover_start.add_child("variables", replay_start.child_or_empty("variables"));
731  } else if(const config& variables_from_cfg = cfg.child("variables")) {
732  carryover.add_child("variables", variables_from_cfg);
733  carryover_start.add_child("variables", variables_from_cfg);
734  }
735  } else if(!replay_start.empty()) {
736  if(const config& variables = replay_start.child("variables")) {
737  carryover.add_child("variables", variables);
738  carryover_start.add_child("variables", variables);
739  }
740  } else {
741  carryover.add_child("variables", cfg.child("variables"));
742  carryover_start.add_child("variables", cfg.child("variables"));
743  }
744 
745  cfg.add_child("carryover_sides", carryover);
746  cfg.add_child("carryover_sides_start", carryover_start);
747  }
748 
749  // if replay and snapshot are empty we've got a start of scenario save and don't want replay_start either
750  if(replay.empty() && snapshot.empty()) {
751  LOG_RG << "removing replay_start \n";
752  cfg.clear_children("replay_start");
753  }
754 
755  // remove empty replay or snapshot so type of save can be detected more easily
756  if(replay.empty()) {
757  LOG_RG << "removing replay \n";
758  cfg.clear_children("replay");
759  }
760 
761  if(snapshot.empty()) {
762  LOG_RG << "removing snapshot \n";
763  cfg.clear_children("snapshot");
764  }
765 }
766 // changes done during 1.13.0-dev
768 {
769  if(config& carryover_sides_start = cfg.child("carryover_sides_start")) {
770  if(!carryover_sides_start.has_attribute("next_underlying_unit_id")) {
771  carryover_sides_start["next_underlying_unit_id"] = cfg["next_underlying_unit_id"];
772  }
773  }
774 
775  if(cfg.child_or_empty("snapshot").empty()) {
776  cfg.clear_children("snapshot");
777  }
778 
779  if(cfg.child_or_empty("replay_start").empty()) {
780  cfg.clear_children("replay_start");
781  }
782 
783  if(config& snapshot = cfg.child("snapshot")) {
784  // make [end_level] -> [end_level_data] since its alo called [end_level_data] in the carryover.
785  if(config& end_level = cfg.child("end_level")) {
786  snapshot.add_child("end_level_data", end_level);
787  snapshot.clear_children("end_level");
788  }
789  // if we have a snapshot then we already applied carryover so there is no reason to keep this data.
790  if(cfg.has_child("carryover_sides_start")) {
791  cfg.clear_children("carryover_sides_start");
792  }
793  }
794 
795  if(!cfg.has_child("snapshot") && !cfg.has_child("replay_start")) {
796  cfg.clear_children("carryover_sides");
797  }
798 
799  // This code is needed because for example otherwise it won't find the (empty) era
800  if(!cfg.has_child("multiplayer")) {
801  cfg.add_child("multiplayer",
802  config{
803  "mp_era",
804  "era_blank",
805  "mp_use_map_settings",
806  true,
807  });
808  }
809 }
810 
811 // changes done during 1.13.0+dev
813 {
814  if(config& multiplayer = cfg.child("multiplayer")) {
815  if(multiplayer["mp_era"] == "era_blank") {
816  multiplayer["mp_era"] = "era_default";
817  }
818  }
819 
820  // This currently only fixes start-of-scenario saves.
821  if(config& carryover_sides_start = cfg.child("carryover_sides_start")) {
822  for(config& side : carryover_sides_start.child_range("side")) {
823  for(config& unit : side.child_range("unit")) {
824  if(config& modifications = unit.child("modifications")) {
825  for(config& advancement : modifications.child_range("advance")) {
826  modifications.add_child("advancement", advancement);
827  }
828  modifications.clear_children("advance");
829  }
830  }
831  }
832  }
833 
834  for(config& snapshot : cfg.child_range("snapshot")) {
835  if(snapshot.has_attribute("used_items")) {
836  config used_items;
837  for(const std::string& item : utils::split(snapshot["used_items"])) {
838  used_items[item] = true;
839  }
840 
841  snapshot.remove_attribute("used_items");
842  snapshot.add_child("used_items", used_items);
843  }
844  }
845 }
846 
847 // changes done during 1.15.3+dev
849 {
850  if(cfg["era_id"].empty()) {
851  cfg["era_id"] = cfg.child_or_empty("multiplayer")["mp_era"];
852  }
853 
854  if(cfg["active_mods"].empty()) {
855  cfg["active_mods"] = cfg.child_or_empty("multiplayer")["active_mods"];
856  }
857 }
858 
860 {
861  version_info loaded_version(cfg["version"]);
862  if(loaded_version < version_info("1.12.0")) {
864  }
865 
866  // '<= version_info("1.13.0")' doesn't work
867  // because version_info cannot handle 1.13.0-dev versions correctly.
868  if(loaded_version < version_info("1.13.1")) {
870  }
871 
872  if(loaded_version <= version_info("1.13.1")) {
874  }
875 
876  if(loaded_version < version_info("1.15.4")) {
878  }
879 
880  LOG_RG << "cfg after conversion " << cfg << "\n";
881 }
882 
883 } // namespace savegame
oos_savegame(saved_game &gamestate, bool &ignore)
Definition: savegame.cpp:618
bool good() const
Dialog was closed with the CANCEL button.
Definition: retval.hpp:37
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:152
bool load_game()
Load a game with pre-setting information for the load-game dialog.
Definition: savegame.cpp:165
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:420
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with &#39;$&#39; in the string &#39;str&#39; with the equivalent ...
static void convert_old_saves_1_13_1(config &cfg)
Definition: savegame.cpp:812
std::map< std::string, t_string > string_map
void clear_children(T... keys)
Definition: config.hpp:490
std::string label
Name of the game (e.g.
std::string era()
Definition: game.cpp:689
This class represents a single unit of a specific type.
Definition: unit.hpp:119
std::string filename
Name of the savefile to be loaded (not including the directory).
Definition: savegame.hpp:58
Interfaces for manipulating version numbers of engine, add-ons, etc.
const game_config_view & game_config_
Definition: savegame.hpp:139
game_classification * classification
Definition: resources.cpp:34
loadgame(const std::shared_ptr< save_index_class > &index, saved_game &gamestate)
Definition: savegame.cpp:83
virtual void write_game(config_writer &out)
Writing the savegame config to a file.
Definition: savegame.cpp:510
static void convert_old_saves_1_13_0(config &cfg)
Definition: savegame.cpp:767
void set_gamestate()
Generate the gamestate out of the loaded game config.
Definition: savegame.cpp:271
replay_savegame(saved_game &gamestate, const compression::format compress_saves)
Definition: savegame.cpp:562
void write_game(config_writer &out) override
Writing the savegame config to a file.
Definition: savegame.cpp:556
virtual std::string create_initial_filename(unsigned int turn_number) const override
Create a filename for automatic saves.
Definition: savegame.cpp:606
const std::string & filename() const
Definition: savegame.hpp:177
config_array_view child_range(config_key_type key) const
static std::shared_ptr< save_index_class > default_saves_dir()
Returns an instance for managing saves in filesystem::get_saves_dir()
Definition: save_index.cpp:188
replay_recorder_base & get_replay()
Definition: saved_game.hpp:134
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:412
#define ERR_SAVE
Definition: savegame.cpp:55
void write_carryover(config_writer &out) const
Definition: saved_game.cpp:203
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:263
child_itors child_range(config_key_type key)
Definition: config.cpp:362
ingame_savegame(saved_game &gamestate, const compression::format compress_saves)
Definition: savegame.cpp:641
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup, const bool restore_background)
Shows a transient message to the user.
Error used when game saving fails.
Definition: game_errors.hpp:38
autosave_savegame(saved_game &gamestate, const compression::format compress_saves)
Definition: savegame.cpp:589
static void convert_old_saves_1_15_3(config &cfg)
Definition: savegame.cpp:848
load_game_metadata load_data_
Primary output information.
Definition: savegame.hpp:143
static CVideo & get_singleton()
Definition: video.hpp:48
Class for "normal" midgame saves.
Definition: savegame.hpp:252
persist_manager * persist
Definition: resources.cpp:26
bool confirm_load_save_from_different_version()
Definition: general.cpp:908
Contains the exception interfaces used to signal completion of a scenario, campaign or turn...
static std::string _(const char *str)
Definition: gettext.hpp:92
bool show(const unsigned auto_close_time=0)
Shows the window.
void remove_attribute(config_key_type key)
Definition: config.cpp:235
scenariostart_savegame(saved_game &gamestate, const compression::format compress_saves)
Definition: savegame.cpp:545
void read_save_file(const std::string &dir, const std::string &name, config &cfg, std::string *error_log)
Read the complete config information out of a savefile.
Definition: save_index.cpp:296
static bool execute(const game_config_view &cache_config, savegame::load_game_metadata &data)
Definition: game_load.hpp:53
std::string filename_
Definition: action_wml.cpp:562
void write_child(const std::string &key, const config &cfg)
const saved_game & gamestate() const
Definition: savegame.hpp:202
void throw_quit_game_exception()
void write_key_val(const std::string &key, const T &value)
This template function will work with any type that can be assigned to an attribute_value.
virtual std::string create_initial_filename(unsigned int turn_number) const override
Create a filename for automatic saves.
Definition: savegame.cpp:567
void close_child(const std::string &key)
This file contains the settings handling of the widget library.
unsigned int major_version() const
Retrieves the major version number (x1 in "x1.x2.x3").
std::ostringstream wrapper.
Definition: formatter.hpp:38
Class for writing a config out to a file in pieces.
bool disable_autosave
const version_info test_version("test")
void open_child(const std::string &key)
saved_game & gamestate_
Definition: savegame.hpp:141
std::string create_filename() const
Build the filename according to the specific savegame&#39;s needs.
Definition: savegame.hpp:180
unsigned int minor_version() const
Retrieves the minor version number (x2 in "x1.x2.x3").
static bool is_replay_save(const config &cfg)
Definition: savegame.hpp:126
Modify, read and display user preferences.
void write(config_writer &out) const
Shows a yes and no button.
Definition: message.hpp:79
bool load_game_ingame()
Load a game without providing any information.
Definition: savegame.cpp:122
virtual int show_save_dialog(const std::string &message, DIALOG_TYPE dialog_type) override
Display the save game dialog.
Definition: savegame.cpp:624
virtual std::string create_initial_filename(unsigned int turn_number) const override
Create a filename for automatic saves.
Definition: savegame.cpp:646
std::string selected_difficulty() const
Returns the selected difficulty define after displaying.
const char * what() const noexcept
Definition: exceptions.hpp:37
void set_data(config &cfg)
destroys the passed config.
Definition: saved_game.cpp:737
#define LOG_SAVE
Definition: savegame.cpp:54
void write_game(config_writer &out) override
Writing the savegame config to a file.
Definition: savegame.cpp:651
void write_game(config_writer &out) override
Writing the savegame config to a file.
Definition: savegame.cpp:577
void convert_old_saves(config &cfg)
converts saves from older versions of wesnoth
Definition: savegame.cpp:859
std::unique_ptr< std::ostream > scoped_ostream
Definition: filesystem.hpp:37
std::string format_extension(format compression_format)
Definition: compression.hpp:26
Exception used to signal that the user has decided to abortt a game, and to load another game instead...
Definition: savegame.hpp:83
Thrown by operations encountering invalid UTF-8 data.
void clean_saves(const std::string &label)
Delete all autosaves of a certain scenario from the default save directory.
Definition: savegame.cpp:69
Game configuration data as global variables.
Definition: build_info.cpp:56
An exception object used when an IO error occurs.
Definition: filesystem.hpp:45
bool check_filename(const std::string &filename)
Check, if the filename contains illegal constructs like ".gz".
Definition: savegame.cpp:424
std::string difficulty
The difficulty the save is meant to be loaded with.
Definition: savegame.hpp:61
std::string name
Definition: sdl_ttf.cpp:70
bool is_compressed_file(const std::string &filename)
Definition: filesystem.hpp:245
#define log_scope(description)
Definition: log.hpp:208
CURSOR_TYPE get()
Definition: cursor.cpp:215
#define LOG_RG
Definition: savegame.cpp:58
config summary
Summary config of the save selected in the load game dialog.
Definition: savegame.hpp:73
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
static void save(LexState *ls, int c)
Definition: llex.cpp:57
const version_info wesnoth_version(VERSION)
The campaign mode difficulty menu.
bool load_multiplayer_game()
Loading a game from within the multiplayer-create dialog.
Definition: savegame.cpp:276
This shows the dialog to create a savegame file.
Definition: game_save.hpp:33
Represents version numbers.
config & add_child(config_key_type key)
Definition: config.cpp:476
void copy_era(config &cfg)
Copy era information into the snapshot.
Definition: savegame.cpp:325
void set_error_message(const std::string &error_message)
Customize the standard error message.
Definition: savegame.hpp:199
compression::format save_compression_format()
Definition: game.cpp:851
static lg::log_domain log_enginerefac("enginerefac")
std::shared_ptr< save_index_class > manager
There may be different instances of the index for different directories.
Definition: savegame.hpp:55
void show_transient_error_message(const std::string &message, const std::string &image, const bool message_use_markup)
Shows a transient error message to the user.
bool show_replay
State of the "show_replay" checkbox in the load-game dialog.
Definition: savegame.hpp:64
int get_retval() const
Returns the cached window exit code.
const std::vector< std::string > & modifications(bool mp)
Definition: game.cpp:719
double t
Definition: astarsearch.cpp:64
std::vector< std::string > split(const config_attribute_value &val)
game_classification & classification()
Definition: saved_game.hpp:54
static void convert_old_saves_1_11_0(config &cfg)
Definition: savegame.cpp:670
config write_stats()
Definition: statistics.cpp:749
Standard logging facilities (interface).
void autosave(const bool disable_autosave, const int autosave_max, const int infinite_autosaves)
Definition: savegame.cpp:595
std::string str() const
Serializes the version number into string form.
std::string message
Definition: exceptions.hpp:31
bool select_difficulty
State of the "change_difficulty" checkbox in the load-game dialog.
Definition: savegame.hpp:70
EXIT_STATUS start(const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
Definition: editor_main.cpp:28
bool show_difficulty_dialog()
Display the difficulty dialog.
Definition: savegame.cpp:90
const std::string & title() const
Definition: savegame.hpp:201
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
Definition: message.cpp:205
std::string filename_
Filename of the savegame file on disk.
Definition: savegame.hpp:211
#define e
const config & child_or_empty(config_key_type key) const
Returns the first child with the given key, or an empty config if there is none.
Definition: config.cpp:453
Dialog was closed with the OK button.
Definition: retval.hpp:34
bool save_game_exists(std::string name, compression::format compressed)
Returns true if there is already a savegame with this name, looking only in the default save director...
Definition: savegame.cpp:62
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
const version_info min_savegame_version(MIN_SAVEGAME_VERSION)
savegame(saved_game &gamestate, const compression::format compress_saves, const std::string &title="Save")
The only constructor of savegame.
Definition: savegame.cpp:345
bool check_version_compatibility()
Call check_version_compatibility above, using the version of this savefile.
Definition: savegame.cpp:225
static lg::log_domain log_engine("engine")
virtual std::string create_initial_filename(unsigned int turn_number) const override
Create a filename for automatic saves.
Definition: savegame.cpp:551
bool empty() const
Definition: config.cpp:884
bool save_game_automatic(bool ask_for_overwrite=false, const std::string &filename="")
Saves a game without user interaction, unless the file exists and it should be asked to overwrite it...
Definition: savegame.cpp:356
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:407
config load_config
Config information of the savefile to be loaded.
Definition: savegame.hpp:76
std::string version
Version game was created with.
filesystem::scoped_ostream ostream_file(const std::string &fname, bool create_directory)