view arogue7/save.c @ 227:696277507a2e

Rogue V4, V5: disable a cheat granting permanent monster detection. In these two games, a potion of monster detection turns on the player's SEEMONST flag. A fuse is set to call turn_see() to turn the flag back off. But the save and restore functions do not recognize turn_see() and fail to set the fuse up again. When restoring, Rogue V4 merely sets the fuse's function to NULL and leaves it burning. When it goes off, a segfault results. Rogue V5 clears all the fuse's fields, and the player retains the ability to see all monsters on the level. The save and restore code can now handle the fuse. The function used is a new wrapper, turn_see_off(), which should lead to less problems with daemons being multiple incompatible types. Also, Rogue V4 and Super-Rogue now properly clear unrecognized daemon and fuse slots when restoring a saved game.
author John "Elwin" Edwards
date Sat, 05 Mar 2016 12:10:20 -0500
parents f9ef86cf22b2
children 3d4252fa2ed3
line wrap: on
line source

/*
 * save.c - save and restore routines
 *
 * Advanced Rogue
 * Copyright (C) 1984, 1985, 1986 Michael Morgan, Ken Dalka and AT&T
 * All rights reserved.
 *
 * Based on "Rogue: Exploring the Dungeons of Doom"
 * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
 * All rights reserved.
 *
 * See the file LICENSE.TXT for full copyright and licensing information.
 */

/*
 * save and restore routines
 *
 */

#include "curses.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include "rogue.h"
#include <fcntl.h>
#include <errno.h>
#include "mach_dep.h"
#ifdef PC7300
#include "sys/window.h"
extern struct uwdata wdata;
#endif

#if u370 || uts
#define ENCREAD(b,n,fd) read(fd,b,n)
#define ENCWRITE(b,n,fd) write(fd,b,n)
#endif
#ifndef ENCREAD
#define ENCREAD encread
#define ENCWRITE encwrite
#endif

bool save_file(int savefd);

typedef struct stat STAT;

extern char version[], encstr[];
/* extern bool _endwin; */
extern int errno;

STAT sbuf;

bool
save_game(void)
{
    register int savefd;
    register int c;
    char buf[LINELEN];

    /*
     * get file name
     */
    mpos = 0;
    if (file_name[0] != '\0')
    {
        if (use_savedir)
	    msg("Save game? ");
        else
	    msg("Save file (%s)? ", file_name);
	do
	{
	    c = readchar();
	    if (c == ESCAPE) return(0);
	} while (c != 'n' && c != 'N' && c != 'y' && c != 'Y');
	mpos = 0;
	if (c == 'y' || c == 'Y')
	{
	    msg("File name: %s", file_name);
	    goto gotfile;
	}
    }

    if (use_savedir)
    {
	msg("");
	return FALSE;
    }

    do
    {
	msg("File name: ");
	mpos = 0;
	buf[0] = '\0';
	if (get_str(buf, msgw) == QUIT)
	{
	    msg("");
	    return FALSE;
	}
	strcpy(file_name, buf);
gotfile:
	if ((savefd = open(file_name, O_WRONLY|O_CREAT|O_TRUNC,0666)) < 0)
        {
	    msg(strerror(errno));	/* fake perror() */
            if (use_savedir)
                return FALSE;
        }
    } while (savefd < 0);

    /*
     * write out encrpyted file (after a stat)
     */
    if (save_file(savefd) == FALSE) {
	msg("Cannot create save file.");
	md_unlink(file_name);
	return(FALSE);
    }
    else return(TRUE);
}

/*
 * automatically save a file.  This is used if a HUP signal is
 * recieved
 */
void
auto_save(int sig)
{
    register int savefd;
    register int i;

    for (i = 0; i < NSIG; i++)
	signal(i, SIG_IGN);
    if (file_name[0] != '\0'	&& 
	pstats.s_hpt > 0	&&
	(savefd = open(file_name, O_WRONLY|O_CREAT|O_TRUNC, 0600)) >= 0)
	save_file(savefd);
    endwin();
#ifdef PC7300
    endhardwin();
#endif
    exit(1);
}

/*
 * write the saved game on the file
 */
bool
save_file(int savefd)
{
    register unsigned num_to_write, num_written;
    FILE *savef;
    int ret;

    wmove(cw, lines-1, 0);
    draw(cw);
    lseek(savefd, 0L, 0);
    fstat(savefd, &sbuf);
    num_to_write = strlen(version) + 1;
    num_written = ENCWRITE(version, num_to_write, savefd);
    sprintf(prbuf,"%d x %d\n", LINES, COLS);
    ENCWRITE(prbuf,80,savefd);
    savef = (FILE *) md_fdopen(savefd,"wb");
    ret = rs_save_file(savef);
    fclose(savef);
    if (num_to_write == num_written && ret == 0) return(TRUE);
    else return(FALSE);
}

bool
restore(char *file, char *envp[])
{
    register int inf;
    extern char **environ;
    char buf[LINELEN];
    STAT sbuf2;
    int oldcol, oldline;	/* Old number of columns and lines */

    if (strcmp(file, "-r") == 0)
	file = file_name;
    if ((inf = open(file, 0)) < 0)
    {
        if (use_savedir && errno == ENOENT)
        {
            return TRUE;
        }
	perror(file);
	return FALSE;
    }

    fflush(stdout);
    ENCREAD(buf, strlen(version) + 1, inf);
    if (strcmp(buf, version) != 0)
    {
	printf("Sorry, saved game is out of date.\n");
	return FALSE;
    }
    
    /*
     * Get the lines and columns from the previous game
     */

    ENCREAD(buf, 80, inf);
    sscanf(buf, "%d x %d\n", &oldline, &oldcol);
    fstat(inf, &sbuf2);
    fflush(stdout);

    initscr();
 
    if (COLS < oldcol || LINES < oldline) {
	endwin();
	printf("Cannot restart the game on a smaller screen.\n");
	return FALSE;
    }

    setup();
    /*
     * Set up windows
     */
    cw = newwin(lines, cols, 0, 0);
    mw = newwin(lines, cols, 0, 0);
    hw = newwin(lines, cols, 0, 0);
    msgw = newwin(4, cols, 0, 0);

    keypad(cw,1);
    keypad(msgw,1);

    if (rs_restore_file(inf) != 0)
    {
	printf("Cannot restore file\n");
	close(inf);
	return(FALSE);
    }

    cols = COLS;
    lines = LINES;
    if (cols > 85) cols = 85;
    if (lines > 24) lines = 24;

    mpos = 0;
    msg("");
    /*msg("%s: %s", file, ctime(&sbuf2.st_mtime));*/

    /*
     * defeat multiple restarting from the same place
     */
    if (!wizard && md_unlink_open_file(file, inf) < 0) {
	printf("Cannot unlink file\n");
	return FALSE;
    }

    environ = envp;
    strcpy(file_name, file);
    setup();
    clearok(curscr, TRUE);
    touchwin(cw);
    srand(getpid());
    playit();
    /*NOTREACHED*/
    return(0);
}

#define ENCWBSIZ	1024
/*
 * perform an encrypted write
 */
int
encwrite(char *start, unsigned int size, int outf)
{
    register char *ep;
    register int i = 0;
    int	num_written = 0;
    auto char buf[ENCWBSIZ];

    ep = encstr;

    while (size--)
    {
	buf[i++] = *start++ ^ *ep++ ;
	if (*ep == '\0')
 	   ep = encstr;

	if (i == ENCWBSIZ || size == 0) {
	    if (write(outf, buf, (unsigned)i) < i) 
		 return(num_written);
	    else {
		num_written += i;
		i = 0;
	    }
	}
    }
    return(num_written);
}

/*
 * perform an encrypted read
 */
int
encread(char *start, unsigned int size, int inf)
{
    register char *ep;
    register int read_size;

    if ((read_size = read(inf, start, size)) == -1 || read_size == 0)
	return read_size;

    ep = encstr;

    size = read_size;
    while (size--)
    {
	*start++ ^= *ep++;
	if (*ep == '\0')
	    ep = encstr;
    }
    return read_size;
}