xref: /dragonfly/games/rogue/save.c (revision 8a0bcd56)
1 /*-
2  * Copyright (c) 1988, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Timothy C. Stoehr.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * @(#)save.c	8.1 (Berkeley) 5/31/93
33  * $FreeBSD: src/games/rogue/save.c,v 1.6 1999/11/30 03:49:27 billf Exp $
34  * $DragonFly: src/games/rogue/save.c,v 1.4 2006/09/02 19:31:07 pavalos Exp $
35  */
36 
37 /*
38  * save.c
39  *
40  * This source herein may be modified and/or distributed by anybody who
41  * so desires, with the following restrictions:
42  *    1.)  No portion of this notice shall be removed.
43  *    2.)  Credit shall not be taken for the creation of this source.
44  *    3.)  This code is not to be traded, sold, or used for personal
45  *         gain or profit.
46  *
47  */
48 
49 #include <stdio.h>
50 #include <unistd.h>
51 #include "rogue.h"
52 
53 extern short cur_level, max_level;
54 extern short party_room;
55 extern short foods;
56 extern boolean is_wood[];
57 extern short cur_room;
58 extern boolean being_held;
59 extern short bear_trap;
60 extern short halluc;
61 extern short blind;
62 extern short confused;
63 extern short levitate;
64 extern short haste_self;
65 extern boolean see_invisible;
66 extern boolean detect_monster;
67 extern boolean wizard;
68 extern boolean score_only;
69 extern short m_moves;
70 
71 extern boolean msg_cleared;
72 
73 static boolean	has_been_touched(const struct rogue_time *,
74 			 const struct rogue_time *);
75 static void	del_save_file(void);
76 static void	r_read(FILE *, char *, int);
77 static void	r_write(FILE *, const char *, int);
78 static void	read_pack(object *, FILE *, boolean);
79 static void	read_string(char *, FILE *, size_t);
80 static void	rw_dungeon(FILE *, boolean);
81 static void	rw_id(struct id *, FILE *, int, boolean);
82 static void	rw_rooms(FILE *, boolean);
83 static void	write_pack(const object *, FILE *);
84 static void	write_string(char *, FILE *);
85 
86 short write_failed = 0;
87 char *save_file = NULL;
88 static char save_name[80];
89 
90 void
91 save_game(void)
92 {
93 	char fname[64];
94 
95 	if (!get_input_line("file name?", save_file, fname,
96 			"game not saved", 0, 1)) {
97 		return;
98 	}
99 	check_message();
100 	message(fname, 0);
101 	save_into_file(fname);
102 }
103 
104 void
105 save_into_file(const char *sfile)
106 {
107 	FILE *fp;
108 	int file_id;
109 	char *name_buffer;
110 	size_t len;
111 	char *hptr;
112 	struct rogue_time rt_buf;
113 
114 	if (sfile[0] == '~') {
115 		if ((hptr = md_getenv("HOME")) != NULL) {
116 			len = strlen(hptr) + strlen(sfile);
117 			name_buffer = md_malloc(len);
118 			if (name_buffer == NULL) {
119 				message("out of memory for save file name", 0);
120 				sfile = error_file;
121 			} else {
122 				strcpy(name_buffer, hptr);
123 				strcat(name_buffer, sfile+1);
124 				sfile = name_buffer;
125 			}
126 
127 		}
128 	}
129 	/* revoke */
130 	setgid(getgid());
131 	if (	((fp = fopen(sfile, "w")) == NULL) ||
132 			((file_id = md_get_file_id(sfile)) == -1)) {
133 		message("problem accessing the save file", 0);
134 		return;
135 	}
136 	md_ignore_signals();
137 	write_failed = 0;
138 	xxx(1);
139 	r_write(fp, (char *) &detect_monster, sizeof(detect_monster));
140 	r_write(fp, (char *) &cur_level, sizeof(cur_level));
141 	r_write(fp, (char *) &max_level, sizeof(max_level));
142 	write_string(hunger_str, fp);
143 	write_string(login_name, fp);
144 	r_write(fp, (char *) &party_room, sizeof(party_room));
145 	write_pack(&level_monsters, fp);
146 	write_pack(&level_objects, fp);
147 	r_write(fp, (char *) &file_id, sizeof(file_id));
148 	rw_dungeon(fp, 1);
149 	r_write(fp, (char *) &foods, sizeof(foods));
150 	r_write(fp, (char *) &rogue, sizeof(fighter));
151 	write_pack(&rogue.pack, fp);
152 	rw_id(id_potions, fp, POTIONS, 1);
153 	rw_id(id_scrolls, fp, SCROLS, 1);
154 	rw_id(id_wands, fp, WANDS, 1);
155 	rw_id(id_rings, fp, RINGS, 1);
156 	r_write(fp, (char *) traps, (MAX_TRAPS * sizeof(trap)));
157 	r_write(fp, (char *) is_wood, (WANDS * sizeof(boolean)));
158 	r_write(fp, (char *) &cur_room, sizeof(cur_room));
159 	rw_rooms(fp, 1);
160 	r_write(fp, (char *) &being_held, sizeof(being_held));
161 	r_write(fp, (char *) &bear_trap, sizeof(bear_trap));
162 	r_write(fp, (char *) &halluc, sizeof(halluc));
163 	r_write(fp, (char *) &blind, sizeof(blind));
164 	r_write(fp, (char *) &confused, sizeof(confused));
165 	r_write(fp, (char *) &levitate, sizeof(levitate));
166 	r_write(fp, (char *) &haste_self, sizeof(haste_self));
167 	r_write(fp, (char *) &see_invisible, sizeof(see_invisible));
168 	r_write(fp, (char *) &detect_monster, sizeof(detect_monster));
169 	r_write(fp, (char *) &wizard, sizeof(wizard));
170 	r_write(fp, (char *) &score_only, sizeof(score_only));
171 	r_write(fp, (char *) &m_moves, sizeof(m_moves));
172 	md_gct(&rt_buf);
173 	rt_buf.second += 10;		/* allow for some processing time */
174 	r_write(fp, (char *) &rt_buf, sizeof(rt_buf));
175 	fclose(fp);
176 
177 	if (write_failed) {
178 		md_df(sfile);	/* delete file */
179 	} else {
180 		if (strcmp(sfile, save_name) == 0)
181 			save_name[0] = 0;
182 		clean_up("");
183 	}
184 }
185 
186 static void
187 del_save_file(void)
188 {
189 	if (!save_name[0])
190 		return;
191 	/* revoke */
192 	setgid(getgid());
193 	md_df(save_name);
194 }
195 
196 void
197 restore(const char *fname)
198 {
199 	FILE *fp = NULL;
200 	struct rogue_time saved_time, mod_time;
201 	char buf[4];
202 	char tbuf[40];
203 	int new_file_id, saved_file_id;
204 
205 	if (	((new_file_id = md_get_file_id(fname)) == -1) ||
206 			((fp = fopen(fname, "r")) == NULL)) {
207 		clean_up("cannot open file");
208 	}
209 	if (md_link_count(fname) > 1) {
210 		clean_up("file has link");
211 	}
212 	xxx(1);
213 	r_read(fp, (char *) &detect_monster, sizeof(detect_monster));
214 	r_read(fp, (char *) &cur_level, sizeof(cur_level));
215 	r_read(fp, (char *) &max_level, sizeof(max_level));
216 	read_string(hunger_str, fp, sizeof hunger_str);
217 
218 	strlcpy(tbuf, login_name, sizeof tbuf);
219 	read_string(login_name, fp, sizeof login_name);
220 	if (strcmp(tbuf, login_name)) {
221 		clean_up("you're not the original player");
222 	}
223 
224 	r_read(fp, (char *) &party_room, sizeof(party_room));
225 	read_pack(&level_monsters, fp, 0);
226 	read_pack(&level_objects, fp, 0);
227 	r_read(fp, (char *) &saved_file_id, sizeof(saved_file_id));
228 	if (new_file_id != saved_file_id) {
229 		clean_up("sorry, saved game is not in the same file");
230 	}
231 	rw_dungeon(fp, 0);
232 	r_read(fp, (char *) &foods, sizeof(foods));
233 	r_read(fp, (char *) &rogue, sizeof(fighter));
234 	read_pack(&rogue.pack, fp, 1);
235 	rw_id(id_potions, fp, POTIONS, 0);
236 	rw_id(id_scrolls, fp, SCROLS, 0);
237 	rw_id(id_wands, fp, WANDS, 0);
238 	rw_id(id_rings, fp, RINGS, 0);
239 	r_read(fp, (char *) traps, (MAX_TRAPS * sizeof(trap)));
240 	r_read(fp, (char *) is_wood, (WANDS * sizeof(boolean)));
241 	r_read(fp, (char *) &cur_room, sizeof(cur_room));
242 	rw_rooms(fp, 0);
243 	r_read(fp, (char *) &being_held, sizeof(being_held));
244 	r_read(fp, (char *) &bear_trap, sizeof(bear_trap));
245 	r_read(fp, (char *) &halluc, sizeof(halluc));
246 	r_read(fp, (char *) &blind, sizeof(blind));
247 	r_read(fp, (char *) &confused, sizeof(confused));
248 	r_read(fp, (char *) &levitate, sizeof(levitate));
249 	r_read(fp, (char *) &haste_self, sizeof(haste_self));
250 	r_read(fp, (char *) &see_invisible, sizeof(see_invisible));
251 	r_read(fp, (char *) &detect_monster, sizeof(detect_monster));
252 	r_read(fp, (char *) &wizard, sizeof(wizard));
253 	r_read(fp, (char *) &score_only, sizeof(score_only));
254 	r_read(fp, (char *) &m_moves, sizeof(m_moves));
255 	r_read(fp, (char *) &saved_time, sizeof(saved_time));
256 
257 	if (fread(buf, sizeof(char), 1, fp) > 0) {
258 		clear();
259 		clean_up("extra characters in file");
260 	}
261 
262 	md_gfmt(fname, &mod_time);	/* get file modification time */
263 
264 	if (has_been_touched(&saved_time, &mod_time)) {
265 		clear();
266 		clean_up("sorry, file has been touched");
267 	}
268 	if ((!wizard)) {
269 		strcpy(save_name, fname);
270 		atexit(del_save_file);
271 	}
272 	msg_cleared = 0;
273 	ring_stats(0);
274 	fclose(fp);
275 }
276 
277 static void
278 write_pack(const object *pack, FILE *fp)
279 {
280 	object t;
281 
282 	while ((pack = pack->next_object) != NULL) {
283 		r_write(fp, (const char *) pack, sizeof(object));
284 	}
285 	t.ichar = t.what_is = 0;
286 	r_write(fp, (const char *) &t, sizeof(object));
287 }
288 
289 static void
290 read_pack(object *pack, FILE *fp, boolean is_rogue)
291 {
292 	object read_obj, *new_obj;
293 
294 	for (;;) {
295 		r_read(fp, (char *) &read_obj, sizeof(object));
296 		if (read_obj.ichar == 0) {
297 			pack->next_object = NULL;
298 			break;
299 		}
300 		new_obj = alloc_object();
301 		*new_obj = read_obj;
302 		if (is_rogue) {
303 			if (new_obj->in_use_flags & BEING_WORN) {
304 				do_wear(new_obj);
305 			} else if (new_obj->in_use_flags & BEING_WIELDED) {
306 				do_wield(new_obj);
307 			} else if (new_obj->in_use_flags & (ON_EITHER_HAND)) {
308 				do_put_on(new_obj,
309 					((new_obj->in_use_flags & ON_LEFT_HAND) ? 1 : 0));
310 			}
311 		}
312 		pack->next_object = new_obj;
313 		pack = new_obj;
314 	}
315 }
316 
317 static void
318 rw_dungeon(FILE *fp, boolean rw)
319 {
320 	short i, j;
321 	char buf[DCOLS];
322 
323 	for (i = 0; i < DROWS; i++) {
324 		if (rw) {
325 			r_write(fp, (char *) dungeon[i], (DCOLS * sizeof(dungeon[0][0])));
326 			for (j = 0; j < DCOLS; j++) {
327 				buf[j] = mvinch(i, j);
328 			}
329 			r_write(fp, buf, DCOLS);
330 		} else {
331 			r_read(fp, (char *) dungeon[i], (DCOLS * sizeof(dungeon[0][0])));
332 			r_read(fp, buf, DCOLS);
333 			for (j = 0; j < DCOLS; j++) {
334 				mvaddch(i, j, buf[j]);
335 			}
336 		}
337 	}
338 }
339 
340 static void
341 rw_id(struct id id_table[], FILE *fp, int n, boolean wr)
342 {
343 	short i;
344 
345 	for (i = 0; i < n; i++) {
346 		if (wr) {
347 			r_write(fp, (const char *) &(id_table[i].value), sizeof(short));
348 			r_write(fp, (const char *) &(id_table[i].id_status),
349 				sizeof(unsigned short));
350 			write_string(id_table[i].title, fp);
351 		} else {
352 			r_read(fp, (char *) &(id_table[i].value), sizeof(short));
353 			r_read(fp, (char *) &(id_table[i].id_status),
354 				sizeof(unsigned short));
355 			read_string(id_table[i].title, fp, MAX_ID_TITLE_LEN);
356 		}
357 	}
358 }
359 
360 static void
361 write_string(char *s, FILE *fp)
362 {
363 	short n;
364 
365 	n = strlen(s) + 1;
366 	xxxx(s, n);
367 	r_write(fp, (char *) &n, sizeof(short));
368 	r_write(fp, s, n);
369 }
370 
371 static void
372 read_string(char *s, FILE *fp, size_t len)
373 {
374 	short n;
375 
376 	r_read(fp, (char *) &n, sizeof(short));
377 	if (n > (short)len)
378 		clean_up("read_string: corrupt game file");
379 	r_read(fp, s, n);
380 	xxxx(s, n);
381 }
382 
383 static void
384 rw_rooms(FILE *fp, boolean rw)
385 {
386 	short i;
387 
388 	for (i = 0; i < MAXROOMS; i++) {
389 		rw ? r_write(fp, (char *) (rooms + i), sizeof(room)) :
390 			r_read(fp, (char *) (rooms + i), sizeof(room));
391 	}
392 }
393 
394 static void
395 r_read(FILE *fp, char *buf, int n)
396 {
397 	if (fread(buf, sizeof(char), n, fp) != (unsigned)n) {
398 		clean_up("read() failed, don't know why");
399 	}
400 }
401 
402 static void
403 r_write(FILE *fp, const char *buf, int n)
404 {
405 	if (!write_failed) {
406 		if (fwrite(buf, sizeof(char), n, fp) != (unsigned)n) {
407 			message("write() failed, don't know why", 0);
408 			sound_bell();
409 			write_failed = 1;
410 		}
411 	}
412 }
413 
414 static boolean
415 has_been_touched(const struct rogue_time *saved_time,
416 		 const struct rogue_time *mod_time)
417 {
418 	if (saved_time->year < mod_time->year) {
419 		return(1);
420 	} else if (saved_time->year > mod_time->year) {
421 		return(0);
422 	}
423 	if (saved_time->month < mod_time->month) {
424 		return(1);
425 	} else if (saved_time->month > mod_time->month) {
426 		return(0);
427 	}
428 	if (saved_time->day < mod_time->day) {
429 		return(1);
430 	} else if (saved_time->day > mod_time->day) {
431 		return(0);
432 	}
433 	if (saved_time->hour < mod_time->hour) {
434 		return(1);
435 	} else if (saved_time->hour > mod_time->hour) {
436 		return(0);
437 	}
438 	if (saved_time->minute < mod_time->minute) {
439 		return(1);
440 	} else if (saved_time->minute > mod_time->minute) {
441 		return(0);
442 	}
443 	if (saved_time->second < mod_time->second) {
444 		return(1);
445 	}
446 	return(0);
447 }
448