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)
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 ***********************************************************************/
15 #include <fc_config.h>
24 #include "capability.h"
30 #include "savegame2.h"
31 #include "savegame3.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
);
47 struct timer
*loadtimer
= timer_new(TIMER_CPU
, TIMER_DEBUG
);
48 timer_start(loadtimer
);
51 savefile_options
= secfile_lookup_str(sfile
, "savefile.options");
53 if (!savefile_options
) {
54 log_error("Missing savefile options. Can not load the savegame.");
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
);
67 log_error("Too old savegame format not supported any more.");
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
,
84 savegame3_save(sfile
, save_reason
, scenario
);
87 struct save_thread_data
89 struct section_file
*sfile
;
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());
107 con_write(C_OK
, _("Game saved as %s"), stdata
->filepath
);
110 secfile_destroy(stdata
->sfile
);
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
,
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
;
134 sz_strlcpy(stdata
->filepath
, orig_filename
);
135 if ((filename
= strrchr(stdata
->filepath
, '/'))) {
138 filename
= stdata
->filepath
;
141 /* Ignores the dot at the start of the filename. */
142 for (dot
= filename
; '.' == *dot
; dot
++) {
146 /* Only dots in this file name, consider it as empty. */
150 char *strip_extensions
[] = { ".sav", ".gz", ".bz2", ".xz", NULL
};
151 bool stripped
= TRUE
;
153 while ((end_dot
= strrchr(dot
, '.')) && stripped
) {
158 for (i
= 0; strip_extensions
[i
] != NULL
&& !stripped
; i
++) {
159 if (!strcmp(end_dot
, strip_extensions
[i
])) {
168 /* If orig_filename is NULL or empty, use a generated default name. */
169 if (filename
[0] == '\0'){
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
197 /* Append ".gz" to filename. */
198 sz_strlcat(stdata
->filepath
, ".gz");
201 #ifdef FREECIV_HAVE_LIBBZ2
203 /* Append ".bz2" to filename. */
204 sz_strlcat(stdata
->filepath
, ".bz2");
207 #ifdef FREECIV_HAVE_LIBLZMA
209 /* Append ".xz" to filename. */
210 sz_strlcat(stdata
->filepath
, ".xz");
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
);
225 if (!path_is_absolute(stdata
->filepath
)) {
229 /* Ensure the saves directory exists. */
230 make_dir(srvarg
.saves_pathname
);
232 sz_strlcpy(tmpname
, srvarg
.saves_pathname
);
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 */
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
);
262 save_thread_run(stdata
);
266 log_verbose("Save time: %g seconds (%g apparent)",
267 timer_read_seconds(timer_cpu
), timer_read_seconds(timer_user
));
270 timer_destroy(timer_cpu
);
271 timer_destroy(timer_user
);
274 /**************************************************************************
276 **************************************************************************/
277 void save_system_close(void)
279 if (save_thread
!= NULL
) {
280 fc_thread_wait(save_thread
);