webperimental: killstack decides stack protects.
[freeciv.git] / server / savegame.c
blob3f2ff640de8ab77f85201fb7978453b9b3774820
1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 /* utility */
19 #include "log.h"
20 #include "mem.h"
21 #include "registry.h"
23 /* common */
24 #include "capability.h"
25 #include "game.h"
27 /* server */
28 #include "console.h"
29 #include "notify.h"
30 #include "savegame2.h"
31 #include "savegame3.h"
33 #include "savegame.h"
35 static fc_thread *save_thread = NULL;
37 /****************************************************************************
38 Main entry point for loading a game.
39 ****************************************************************************/
40 void savegame_load(struct section_file *sfile)
42 const char *savefile_options;
44 fc_assert_ret(sfile != NULL);
46 #ifdef DEBUG_TIMERS
47 struct timer *loadtimer = timer_new(TIMER_CPU, TIMER_DEBUG);
48 timer_start(loadtimer);
49 #endif
51 savefile_options = secfile_lookup_str(sfile, "savefile.options");
53 if (!savefile_options) {
54 log_error("Missing savefile options. Can not load the savegame.");
55 return;
58 if (has_capabilities("+version3", savefile_options)) {
59 /* load new format (freeciv 3.0.x and newer) */
60 log_verbose("loading savefile in 3.0+ format ...");
61 savegame3_load(sfile);
62 } else if (has_capabilities("+version2", savefile_options)) {
63 /* load old format (freeciv 2.3 - 2.6) */
64 log_verbose("loading savefile in 2.3 - 2.6 format ...");
65 savegame2_load(sfile);
66 } else {
67 log_error("Too old savegame format not supported any more.");
68 return;
71 #ifdef DEBUG_TIMERS
72 timer_stop(loadtimer);
73 log_debug("Loading secfile in %.3f seconds.", timer_read_seconds(loadtimer));
74 timer_destroy(loadtimer);
75 #endif /* DEBUG_TIMERS */
78 /****************************************************************************
79 Main entry point for saving a game.
80 ****************************************************************************/
81 void savegame_save(struct section_file *sfile, const char *save_reason,
82 bool scenario)
84 savegame3_save(sfile, save_reason, scenario);
87 struct save_thread_data
89 struct section_file *sfile;
90 char filepath[600];
91 int save_compress_level;
92 enum fz_method save_compress_type;
95 /*************************************************************************
96 Run game saving thread.
97 *************************************************************************/
98 static void save_thread_run(void *arg)
100 struct save_thread_data *stdata = (struct save_thread_data *)arg;
102 if (!secfile_save(stdata->sfile, stdata->filepath, stdata->save_compress_level,
103 stdata->save_compress_type)) {
104 con_write(C_FAIL, _("Failed saving game as %s"), stdata->filepath);
105 log_error("Game saving failed: %s", secfile_error());
106 } else {
107 con_write(C_OK, _("Game saved as %s"), stdata->filepath);
110 secfile_destroy(stdata->sfile);
111 free(arg);
114 /**************************************************************************
115 Unconditionally save the game, with specified filename.
116 Always prints a message: either save ok, or failed.
117 **************************************************************************/
118 void save_game(const char *orig_filename, const char *save_reason,
119 bool scenario)
121 char *dot, *filename;
122 struct timer *timer_cpu, *timer_user;
123 struct save_thread_data *stdata;
125 stdata = fc_malloc(sizeof(*stdata));
127 stdata->save_compress_type = game.server.save_compress_type;
128 stdata->save_compress_level = game.server.save_compress_level;
130 if (!orig_filename) {
131 stdata->filepath[0] = '\0';
132 filename = stdata->filepath;
133 } else {
134 sz_strlcpy(stdata->filepath, orig_filename);
135 if ((filename = strrchr(stdata->filepath, '/'))) {
136 filename++;
137 } else {
138 filename = stdata->filepath;
141 /* Ignores the dot at the start of the filename. */
142 for (dot = filename; '.' == *dot; dot++) {
143 /* Nothing. */
145 if ('\0' == *dot) {
146 /* Only dots in this file name, consider it as empty. */
147 filename[0] = '\0';
148 } else {
149 char *end_dot;
150 char *strip_extensions[] = { ".sav", ".gz", ".bz2", ".xz", NULL };
151 bool stripped = TRUE;
153 while ((end_dot = strrchr(dot, '.')) && stripped) {
154 int i;
156 stripped = FALSE;
158 for (i = 0; strip_extensions[i] != NULL && !stripped; i++) {
159 if (!strcmp(end_dot, strip_extensions[i])) {
160 *end_dot = '\0';
161 stripped = TRUE;
168 /* If orig_filename is NULL or empty, use a generated default name. */
169 if (filename[0] == '\0'){
170 /* manual save */
171 generate_save_name(game.server.save_name, filename,
172 sizeof(stdata->filepath) + stdata->filepath - filename, "manual");
175 timer_cpu = timer_new(TIMER_CPU, TIMER_ACTIVE);
176 timer_start(timer_cpu);
177 timer_user = timer_new(TIMER_USER, TIMER_ACTIVE);
178 timer_start(timer_user);
180 /* Allowing duplicates shouldn't be allowed. However, it takes very too
181 * long time for huge game saving... */
182 stdata->sfile = secfile_new(TRUE);
183 savegame_save(stdata->sfile, save_reason, scenario);
185 /* We have consistent game state in stdata->sfile now, so
186 * we could pass it to the saving thread already. We want to
187 * handle below notify_conn() and directory creation in
188 * main thread, though. */
190 /* Append ".sav" to filename. */
191 sz_strlcat(stdata->filepath, ".sav");
193 if (stdata->save_compress_level > 0) {
194 switch (stdata->save_compress_type) {
195 #ifdef FREECIV_HAVE_LIBZ
196 case FZ_ZLIB:
197 /* Append ".gz" to filename. */
198 sz_strlcat(stdata->filepath, ".gz");
199 break;
200 #endif
201 #ifdef FREECIV_HAVE_LIBBZ2
202 case FZ_BZIP2:
203 /* Append ".bz2" to filename. */
204 sz_strlcat(stdata->filepath, ".bz2");
205 break;
206 #endif
207 #ifdef FREECIV_HAVE_LIBLZMA
208 case FZ_XZ:
209 /* Append ".xz" to filename. */
210 sz_strlcat(stdata->filepath, ".xz");
211 break;
212 #endif
213 case FZ_PLAIN:
214 break;
215 default:
216 log_error(_("Unsupported compression type %d."),
217 stdata->save_compress_type);
218 notify_conn(NULL, NULL, E_SETTING, ftc_warning,
219 _("Unsupported compression type %d."),
220 stdata->save_compress_type);
221 break;
225 if (!path_is_absolute(stdata->filepath)) {
226 char tmpname[600];
228 if (!scenario) {
229 /* Ensure the saves directory exists. */
230 make_dir(srvarg.saves_pathname);
232 sz_strlcpy(tmpname, srvarg.saves_pathname);
233 } else {
234 /* Make sure scenario directory exist */
235 make_dir(srvarg.scenarios_pathname);
237 sz_strlcpy(tmpname, srvarg.scenarios_pathname);
240 if (tmpname[0] != '\0') {
241 sz_strlcat(tmpname, "/");
243 sz_strlcat(tmpname, stdata->filepath);
244 sz_strlcpy(stdata->filepath, tmpname);
247 if (save_thread != NULL) {
248 /* Previously started thread */
249 fc_thread_wait(save_thread);
250 if (!game.server.threaded_save) {
251 /* Setting has changed since the last save */
252 free(save_thread);
253 save_thread = NULL;
255 } else if (game.server.threaded_save) {
256 save_thread = fc_malloc(sizeof(save_thread));
259 if (save_thread != NULL) {
260 fc_thread_start(save_thread, &save_thread_run, stdata);
261 } else {
262 save_thread_run(stdata);
265 #ifdef LOG_TIMERS
266 log_verbose("Save time: %g seconds (%g apparent)",
267 timer_read_seconds(timer_cpu), timer_read_seconds(timer_user));
268 #endif
270 timer_destroy(timer_cpu);
271 timer_destroy(timer_user);
274 /**************************************************************************
275 Close saving system.
276 **************************************************************************/
277 void save_system_close(void)
279 if (save_thread != NULL) {
280 fc_thread_wait(save_thread);
281 free(save_thread);
282 save_thread = NULL;