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 */ 35 36 /* 37 * save.c 38 * 39 * This source herein may be modified and/or distributed by anybody who 40 * so desires, with the following restrictions: 41 * 1.) No portion of this notice shall be removed. 42 * 2.) Credit shall not be taken for the creation of this source. 43 * 3.) This code is not to be traded, sold, or used for personal 44 * gain or profit. 45 * 46 */ 47 48 #include <stdio.h> 49 #include <unistd.h> 50 #include "rogue.h" 51 52 static boolean has_been_touched(const struct rogue_time *, 53 const struct rogue_time *); 54 static void del_save_file(void); 55 static void r_read(FILE *, char *, int); 56 static void r_write(FILE *, const char *, int); 57 static void read_pack(object *, FILE *, boolean); 58 static void read_string(char *, FILE *, size_t); 59 static void rw_dungeon(FILE *, boolean); 60 static void rw_id(struct id *, FILE *, int, boolean); 61 static void rw_rooms(FILE *, boolean); 62 static void write_pack(const object *, FILE *); 63 static void write_string(char *, FILE *); 64 65 static short write_failed = 0; 66 static char save_name[80]; 67 68 char *save_file = NULL; 69 70 void 71 save_game(void) 72 { 73 char fname[64]; 74 75 if (!get_input_line("file name?", save_file, fname, 76 "game not saved", 0, 1)) { 77 return; 78 } 79 check_message(); 80 message(fname, 0); 81 save_into_file(fname); 82 } 83 84 void 85 save_into_file(const char *sfile) 86 { 87 FILE *fp; 88 int file_id; 89 char *name_buffer; 90 size_t len; 91 char *hptr; 92 struct rogue_time rt_buf; 93 94 if (sfile[0] == '~') { 95 if ((hptr = md_getenv("HOME")) != NULL) { 96 len = strlen(hptr) + strlen(sfile); 97 name_buffer = md_malloc(len); 98 if (name_buffer == NULL) { 99 message("out of memory for save file name", 0); 100 sfile = error_file; 101 } else { 102 strcpy(name_buffer, hptr); 103 strcat(name_buffer, sfile+1); 104 sfile = name_buffer; 105 } 106 107 } 108 } 109 /* revoke */ 110 setgid(getgid()); 111 if ( ((fp = fopen(sfile, "w")) == NULL) || 112 ((file_id = md_get_file_id(sfile)) == -1)) { 113 message("problem accessing the save file", 0); 114 return; 115 } 116 md_ignore_signals(); 117 write_failed = 0; 118 xxx(1); 119 r_write(fp, (char *) &detect_monster, sizeof(detect_monster)); 120 r_write(fp, (char *) &cur_level, sizeof(cur_level)); 121 r_write(fp, (char *) &max_level, sizeof(max_level)); 122 write_string(hunger_str, fp); 123 write_string(login_name, fp); 124 r_write(fp, (char *) &party_room, sizeof(party_room)); 125 write_pack(&level_monsters, fp); 126 write_pack(&level_objects, fp); 127 r_write(fp, (char *) &file_id, sizeof(file_id)); 128 rw_dungeon(fp, 1); 129 r_write(fp, (char *) &foods, sizeof(foods)); 130 r_write(fp, (char *) &rogue, sizeof(fighter)); 131 write_pack(&rogue.pack, fp); 132 rw_id(id_potions, fp, POTIONS, 1); 133 rw_id(id_scrolls, fp, SCROLS, 1); 134 rw_id(id_wands, fp, WANDS, 1); 135 rw_id(id_rings, fp, RINGS, 1); 136 r_write(fp, (char *) traps, (MAX_TRAPS * sizeof(trap))); 137 r_write(fp, (char *) is_wood, (WANDS * sizeof(boolean))); 138 r_write(fp, (char *) &cur_room, sizeof(cur_room)); 139 rw_rooms(fp, 1); 140 r_write(fp, (char *) &being_held, sizeof(being_held)); 141 r_write(fp, (char *) &bear_trap, sizeof(bear_trap)); 142 r_write(fp, (char *) &halluc, sizeof(halluc)); 143 r_write(fp, (char *) &blind, sizeof(blind)); 144 r_write(fp, (char *) &confused, sizeof(confused)); 145 r_write(fp, (char *) &levitate, sizeof(levitate)); 146 r_write(fp, (char *) &haste_self, sizeof(haste_self)); 147 r_write(fp, (char *) &see_invisible, sizeof(see_invisible)); 148 r_write(fp, (char *) &detect_monster, sizeof(detect_monster)); 149 r_write(fp, (char *) &wizard, sizeof(wizard)); 150 r_write(fp, (char *) &score_only, sizeof(score_only)); 151 r_write(fp, (char *) &m_moves, sizeof(m_moves)); 152 md_gct(&rt_buf); 153 rt_buf.second += 10; /* allow for some processing time */ 154 r_write(fp, (char *) &rt_buf, sizeof(rt_buf)); 155 fclose(fp); 156 157 if (write_failed) { 158 md_df(sfile); /* delete file */ 159 } else { 160 if (strcmp(sfile, save_name) == 0) 161 save_name[0] = 0; 162 clean_up(""); 163 } 164 } 165 166 static void 167 del_save_file(void) 168 { 169 if (!save_name[0]) 170 return; 171 /* revoke */ 172 setgid(getgid()); 173 md_df(save_name); 174 } 175 176 void 177 restore(const char *fname) 178 { 179 FILE *fp = NULL; 180 struct rogue_time saved_time, mod_time; 181 char buf[4]; 182 char tbuf[40]; 183 int new_file_id, saved_file_id; 184 185 if ( ((new_file_id = md_get_file_id(fname)) == -1) || 186 ((fp = fopen(fname, "r")) == NULL)) { 187 clean_up("cannot open file"); 188 } 189 if (md_link_count(fname) > 1) { 190 clean_up("file has link"); 191 } 192 xxx(1); 193 r_read(fp, (char *) &detect_monster, sizeof(detect_monster)); 194 r_read(fp, (char *) &cur_level, sizeof(cur_level)); 195 r_read(fp, (char *) &max_level, sizeof(max_level)); 196 read_string(hunger_str, fp, sizeof hunger_str); 197 198 strlcpy(tbuf, login_name, sizeof tbuf); 199 read_string(login_name, fp, sizeof login_name); 200 if (strcmp(tbuf, login_name)) { 201 clean_up("you're not the original player"); 202 } 203 204 r_read(fp, (char *) &party_room, sizeof(party_room)); 205 read_pack(&level_monsters, fp, 0); 206 read_pack(&level_objects, fp, 0); 207 r_read(fp, (char *) &saved_file_id, sizeof(saved_file_id)); 208 if (new_file_id != saved_file_id) { 209 clean_up("sorry, saved game is not in the same file"); 210 } 211 rw_dungeon(fp, 0); 212 r_read(fp, (char *) &foods, sizeof(foods)); 213 r_read(fp, (char *) &rogue, sizeof(fighter)); 214 read_pack(&rogue.pack, fp, 1); 215 rw_id(id_potions, fp, POTIONS, 0); 216 rw_id(id_scrolls, fp, SCROLS, 0); 217 rw_id(id_wands, fp, WANDS, 0); 218 rw_id(id_rings, fp, RINGS, 0); 219 r_read(fp, (char *) traps, (MAX_TRAPS * sizeof(trap))); 220 r_read(fp, (char *) is_wood, (WANDS * sizeof(boolean))); 221 r_read(fp, (char *) &cur_room, sizeof(cur_room)); 222 rw_rooms(fp, 0); 223 r_read(fp, (char *) &being_held, sizeof(being_held)); 224 r_read(fp, (char *) &bear_trap, sizeof(bear_trap)); 225 r_read(fp, (char *) &halluc, sizeof(halluc)); 226 r_read(fp, (char *) &blind, sizeof(blind)); 227 r_read(fp, (char *) &confused, sizeof(confused)); 228 r_read(fp, (char *) &levitate, sizeof(levitate)); 229 r_read(fp, (char *) &haste_self, sizeof(haste_self)); 230 r_read(fp, (char *) &see_invisible, sizeof(see_invisible)); 231 r_read(fp, (char *) &detect_monster, sizeof(detect_monster)); 232 r_read(fp, (char *) &wizard, sizeof(wizard)); 233 r_read(fp, (char *) &score_only, sizeof(score_only)); 234 r_read(fp, (char *) &m_moves, sizeof(m_moves)); 235 r_read(fp, (char *) &saved_time, sizeof(saved_time)); 236 237 if (fread(buf, sizeof(char), 1, fp) > 0) { 238 clear(); 239 clean_up("extra characters in file"); 240 } 241 242 md_gfmt(fname, &mod_time); /* get file modification time */ 243 244 if (has_been_touched(&saved_time, &mod_time)) { 245 clear(); 246 clean_up("sorry, file has been touched"); 247 } 248 if ((!wizard)) { 249 strcpy(save_name, fname); 250 atexit(del_save_file); 251 } 252 msg_cleared = 0; 253 ring_stats(0); 254 fclose(fp); 255 } 256 257 static void 258 write_pack(const object *pack, FILE *fp) 259 { 260 object t; 261 262 while ((pack = pack->next_object) != NULL) { 263 r_write(fp, (const char *) pack, sizeof(object)); 264 } 265 t.ichar = t.what_is = 0; 266 r_write(fp, (const char *) &t, sizeof(object)); 267 } 268 269 static void 270 read_pack(object *pack, FILE *fp, boolean is_rogue) 271 { 272 object read_obj, *new_obj; 273 274 for (;;) { 275 r_read(fp, (char *) &read_obj, sizeof(object)); 276 if (read_obj.ichar == 0) { 277 pack->next_object = NULL; 278 break; 279 } 280 new_obj = alloc_object(); 281 *new_obj = read_obj; 282 if (is_rogue) { 283 if (new_obj->in_use_flags & BEING_WORN) { 284 do_wear(new_obj); 285 } else if (new_obj->in_use_flags & BEING_WIELDED) { 286 do_wield(new_obj); 287 } else if (new_obj->in_use_flags & (ON_EITHER_HAND)) { 288 do_put_on(new_obj, 289 ((new_obj->in_use_flags & ON_LEFT_HAND) ? 1 : 0)); 290 } 291 } 292 pack->next_object = new_obj; 293 pack = new_obj; 294 } 295 } 296 297 static void 298 rw_dungeon(FILE *fp, boolean rw) 299 { 300 short i, j; 301 char buf[DCOLS]; 302 303 for (i = 0; i < DROWS; i++) { 304 if (rw) { 305 r_write(fp, (char *) dungeon[i], (DCOLS * sizeof(dungeon[0][0]))); 306 for (j = 0; j < DCOLS; j++) { 307 buf[j] = mvinch(i, j); 308 } 309 r_write(fp, buf, DCOLS); 310 } else { 311 r_read(fp, (char *) dungeon[i], (DCOLS * sizeof(dungeon[0][0]))); 312 r_read(fp, buf, DCOLS); 313 for (j = 0; j < DCOLS; j++) { 314 mvaddch(i, j, buf[j]); 315 } 316 } 317 } 318 } 319 320 static void 321 rw_id(struct id id_table[], FILE *fp, int n, boolean wr) 322 { 323 short i; 324 325 for (i = 0; i < n; i++) { 326 if (wr) { 327 r_write(fp, (const char *) &(id_table[i].value), sizeof(short)); 328 r_write(fp, (const char *) &(id_table[i].id_status), 329 sizeof(unsigned short)); 330 write_string(id_table[i].title, fp); 331 } else { 332 r_read(fp, (char *) &(id_table[i].value), sizeof(short)); 333 r_read(fp, (char *) &(id_table[i].id_status), 334 sizeof(unsigned short)); 335 read_string(id_table[i].title, fp, MAX_ID_TITLE_LEN); 336 } 337 } 338 } 339 340 static void 341 write_string(char *s, FILE *fp) 342 { 343 short n; 344 345 n = strlen(s) + 1; 346 xxxx(s, n); 347 r_write(fp, (char *) &n, sizeof(short)); 348 r_write(fp, s, n); 349 } 350 351 static void 352 read_string(char *s, FILE *fp, size_t len) 353 { 354 short n; 355 356 r_read(fp, (char *) &n, sizeof(short)); 357 if (n < 0 || n > (short)len) { 358 clean_up("read_string: corrupt game file"); 359 } 360 r_read(fp, s, n); 361 xxxx(s, n); 362 /* ensure null termination */ 363 s[n-1] = 0; 364 } 365 366 static void 367 rw_rooms(FILE *fp, boolean rw) 368 { 369 short i; 370 371 for (i = 0; i < MAXROOMS; i++) { 372 rw ? r_write(fp, (char *) (rooms + i), sizeof(room)) : 373 r_read(fp, (char *) (rooms + i), sizeof(room)); 374 } 375 } 376 377 static void 378 r_read(FILE *fp, char *buf, int n) 379 { 380 if (fread(buf, sizeof(char), n, fp) != (unsigned)n) { 381 clean_up("read() failed, don't know why"); 382 } 383 } 384 385 static void 386 r_write(FILE *fp, const char *buf, int n) 387 { 388 if (!write_failed) { 389 if (fwrite(buf, sizeof(char), n, fp) != (unsigned)n) { 390 message("write() failed, don't know why", 0); 391 sound_bell(); 392 write_failed = 1; 393 } 394 } 395 } 396 397 static boolean 398 has_been_touched(const struct rogue_time *saved_time, 399 const struct rogue_time *mod_time) 400 { 401 if (saved_time->year < mod_time->year) { 402 return(1); 403 } else if (saved_time->year > mod_time->year) { 404 return(0); 405 } 406 if (saved_time->month < mod_time->month) { 407 return(1); 408 } else if (saved_time->month > mod_time->month) { 409 return(0); 410 } 411 if (saved_time->day < mod_time->day) { 412 return(1); 413 } else if (saved_time->day > mod_time->day) { 414 return(0); 415 } 416 if (saved_time->hour < mod_time->hour) { 417 return(1); 418 } else if (saved_time->hour > mod_time->hour) { 419 return(0); 420 } 421 if (saved_time->minute < mod_time->minute) { 422 return(1); 423 } else if (saved_time->minute > mod_time->minute) { 424 return(0); 425 } 426 if (saved_time->second < mod_time->second) { 427 return(1); 428 } 429 return(0); 430 } 431