1 #include "wui/savegameloader.h"
2
3 #include <boost/algorithm/string.hpp>
4 #include <boost/format.hpp>
5
6 #include "base/i18n.h"
7 #include "base/log.h"
8 #include "base/time_string.h"
9 #include "game_io/game_loader.h"
10 #include "io/filesystem/layered_filesystem.h"
11 #include "logic/filesystem_constants.h"
12
SavegameLoader(Widelands::Game & game)13 SavegameLoader::SavegameLoader(Widelands::Game& game) : game_(game) {
14 }
load_files(const std::string & directory)15 std::vector<SavegameData> SavegameLoader::load_files(const std::string& directory) {
16 std::vector<SavegameData> loaded_games;
17 FilenameSet gamefiles = g_fs->list_directory(directory);
18
19 for (const std::string& gamefilename : gamefiles) {
20 load(gamefilename, loaded_games);
21 }
22 return loaded_games;
23 }
24
get_savename(const std::string & gamefilename) const25 std::string SavegameLoader::get_savename(const std::string& gamefilename) const {
26 return gamefilename;
27 }
28
load(const std::string & to_be_loaded,std::vector<SavegameData> & loaded_games) const29 void SavegameLoader::load(const std::string& to_be_loaded,
30 std::vector<SavegameData>& loaded_games) const {
31 if (g_fs->is_directory(to_be_loaded)) {
32 try {
33 load_savegame_from_directory(to_be_loaded, loaded_games);
34 } catch (const std::exception&) {
35 // loading failed, so this is actually a normal directory
36 add_sub_dir(to_be_loaded, loaded_games);
37 }
38 } else {
39 load_savegame_from_file(to_be_loaded, loaded_games);
40 }
41 }
42
load_savegame_from_directory(const std::string & gamefilename,std::vector<SavegameData> & loaded_games) const43 void SavegameLoader::load_savegame_from_directory(const std::string& gamefilename,
44 std::vector<SavegameData>& loaded_games) const {
45
46 Widelands::GamePreloadPacket gpdp;
47 SavegameData gamedata(gamefilename);
48
49 Widelands::GameLoader gl(gamefilename.c_str(), game_);
50 gl.preload_game(gpdp);
51 gamedata.gametype = gpdp.get_gametype();
52 if (!is_valid_gametype(gamedata)) {
53 return;
54 }
55
56 add_general_information(gamedata, gpdp);
57 add_time_info(gamedata, gpdp);
58 loaded_games.push_back(gamedata);
59 }
60
load_savegame_from_file(const std::string & gamefilename,std::vector<SavegameData> & loaded_games) const61 void SavegameLoader::load_savegame_from_file(const std::string& gamefilename,
62 std::vector<SavegameData>& loaded_games) const {
63 std::string savename = get_savename(gamefilename);
64
65 if (!g_fs->file_exists(savename.c_str())) {
66 return;
67 }
68
69 Widelands::GamePreloadPacket gpdp;
70 SavegameData gamedata(gamefilename);
71 try {
72 Widelands::GameLoader gl(savename.c_str(), game_);
73 gl.preload_game(gpdp);
74 gamedata.gametype = gpdp.get_gametype();
75 if (!is_valid_gametype(gamedata)) {
76 return;
77 }
78
79 add_general_information(gamedata, gpdp);
80 add_time_info(gamedata, gpdp);
81
82 } catch (const std::exception& e) {
83 add_error_info(gamedata, e.what());
84 }
85
86 loaded_games.push_back(gamedata);
87 }
88
add_general_information(SavegameData & gamedata,const Widelands::GamePreloadPacket & gpdp) const89 void SavegameLoader::add_general_information(SavegameData& gamedata,
90 const Widelands::GamePreloadPacket& gpdp) const {
91 gamedata.set_mapname(gpdp.get_mapname());
92 gamedata.set_gametime(gpdp.get_gametime());
93 gamedata.set_nrplayers(gpdp.get_number_of_players());
94 gamedata.version = gpdp.get_version();
95 gamedata.wincondition = gpdp.get_localized_win_condition();
96 gamedata.minimap_path = gpdp.get_minimap_path();
97 }
98
add_error_info(SavegameData & gamedata,std::string errormessage) const99 void SavegameLoader::add_error_info(SavegameData& gamedata, std::string errormessage) const {
100 boost::replace_all(errormessage, "\n", "<br>");
101 gamedata.errormessage =
102 ((boost::format("<p>%s</p><p>%s</p><p>%s</p>"))
103 /** TRANSLATORS: Error message introduction for when an old savegame can't be loaded */
104 % _("This file has the wrong format and can’t be loaded."
105 " Maybe it was created with an older version of Widelands.")
106 /** TRANSLATORS: This text is on a separate line with an error message below */
107 % _("Error message:") % errormessage)
108 .str();
109
110 gamedata.mapname = FileSystem::filename_without_ext(gamedata.filename.c_str());
111 }
112
add_time_info(SavegameData & gamedata,const Widelands::GamePreloadPacket & gpdp) const113 void SavegameLoader::add_time_info(SavegameData& gamedata,
114 const Widelands::GamePreloadPacket& gpdp) const {
115 gamedata.savetimestamp = gpdp.get_savetimestamp();
116 time_t t;
117 time(&t);
118 struct tm* currenttime = localtime(&t);
119 // We need to put these into variables because of a sideeffect of the localtime function.
120 int8_t current_year = currenttime->tm_year;
121 int8_t current_month = currenttime->tm_mon;
122 int8_t current_day = currenttime->tm_mday;
123
124 struct tm* savedate = localtime(&gamedata.savetimestamp);
125
126 if (gamedata.savetimestamp > 0) {
127 if (savedate->tm_year == current_year && savedate->tm_mon == current_month &&
128 savedate->tm_mday == current_day) { // Today
129
130 // Adding the 0 padding in a separate statement so translators won't have to deal
131 // with it
132 const std::string minute = (boost::format("%02u") % savedate->tm_min).str();
133
134 gamedata.savedatestring =
135 /** TRANSLATORS: Display date for choosing a savegame/replay. Placeholders are:
136 hour:minute */
137 (boost::format(_("Today, %1%:%2%")) % savedate->tm_hour % minute).str();
138 gamedata.savedonstring =
139 /** TRANSLATORS: Display date for choosing a savegame/replay. Placeholders are:
140 hour:minute. This is part of a list.
141 */
142 (boost::format(_("saved today at %1%:%2%")) % savedate->tm_hour % minute).str();
143 } else if ((savedate->tm_year == current_year && savedate->tm_mon == current_month &&
144 savedate->tm_mday == current_day - 1) ||
145 (savedate->tm_year == current_year - 1 && savedate->tm_mon == 11 &&
146 current_month == 0 && savedate->tm_mday == 31 &&
147 current_day == 1)) { // Yesterday
148 // Adding the 0 padding in a separate statement so translators won't have to deal
149 // with it
150 const std::string minute = (boost::format("%02u") % savedate->tm_min).str();
151
152 gamedata.savedatestring =
153 /** TRANSLATORS: Display date for choosing a savegame/replay. Placeholders are:
154 hour:minute */
155 (boost::format(_("Yesterday, %1%:%2%")) % savedate->tm_hour % minute).str();
156 gamedata.savedonstring =
157 /** TRANSLATORS: Display date for choosing a savegame/replay. Placeholders are:
158 hour:minute. This is part of a list.
159 */
160 (boost::format(_("saved yesterday at %1%:%2%")) % savedate->tm_hour % minute).str();
161 } else { // Older
162 gamedata.savedatestring =
163 /** TRANSLATORS: Display date for choosing a savegame/replay. Placeholders are:
164 month day, year */
165 (boost::format(_("%1% %2%, %3%")) % localize_month(savedate->tm_mon) %
166 savedate->tm_mday % (1900 + savedate->tm_year))
167 .str();
168 gamedata.savedonstring =
169 /** TRANSLATORS: Display date for choosing a savegame/replay. Placeholders are:
170 month (short name) day (number),
171 year (number). This is part of a list. */
172 (boost::format(_("saved on %1% %2%, %3%")) % savedate->tm_mday %
173 localize_month(savedate->tm_mon) % (1900 + savedate->tm_year))
174 .str();
175 }
176 }
177 }
178
add_sub_dir(const std::string & gamefilename,std::vector<SavegameData> & loaded_games) const179 void SavegameLoader::add_sub_dir(const std::string& gamefilename,
180 std::vector<SavegameData>& loaded_games) const {
181 // Add subdirectory to the list
182 const char* fs_filename = FileSystem::fs_filename(gamefilename.c_str());
183 if (!strcmp(fs_filename, ".") || !strcmp(fs_filename, "..")) {
184 return;
185 }
186 loaded_games.push_back(SavegameData::create_sub_dir(gamefilename));
187 }
188
ReplayLoader(Widelands::Game & game)189 ReplayLoader::ReplayLoader(Widelands::Game& game) : SavegameLoader(game) {
190 }
191
is_valid_gametype(const SavegameData &) const192 bool ReplayLoader::is_valid_gametype(const SavegameData&) const {
193 return true; // TODO(jmoerschbach): why?? what is the purpose of
194 // GameController::GameType::kReplay? return gamedata.is_replay(); <-- should be
195 // this, right?!
196 }
197
get_savename(const std::string & gamefilename) const198 std::string ReplayLoader::get_savename(const std::string& gamefilename) const {
199 std::string savename = gamefilename;
200 savename += kSavegameExtension;
201 return savename;
202 }
203
MultiPlayerLoader(Widelands::Game & game)204 MultiPlayerLoader::MultiPlayerLoader(Widelands::Game& game) : SavegameLoader(game) {
205 }
206
is_valid_gametype(const SavegameData & gamedata) const207 bool MultiPlayerLoader::is_valid_gametype(const SavegameData& gamedata) const {
208 // TODO(jmoerschbach): workaround to be able to load replays in multiplayer loading screen
209 return gamedata.is_multiplayer() || gamedata.is_replay();
210 }
211
SinglePlayerLoader(Widelands::Game & game)212 SinglePlayerLoader::SinglePlayerLoader(Widelands::Game& game) : SavegameLoader(game) {
213 }
214
is_valid_gametype(const SavegameData & gamedata) const215 bool SinglePlayerLoader::is_valid_gametype(const SavegameData& gamedata) const {
216 return gamedata.is_singleplayer();
217 }
218
EverythingLoader(Widelands::Game & game)219 EverythingLoader::EverythingLoader(Widelands::Game& game) : SavegameLoader(game) {
220 }
221
is_valid_gametype(const SavegameData &) const222 bool EverythingLoader::is_valid_gametype(const SavegameData&) const {
223 return true;
224 }
225