244 lines
6.3 KiB
C
244 lines
6.3 KiB
C
/* NetHack 3.7 sfstruct.c $NHDT-Date: 1606765215 2020/11/30 19:40:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.4 $ */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/*-Copyright (c) Michael Allison, 2009. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#include "hack.h"
|
|
|
|
/*
|
|
* historical structlevel savefile writing and reading routines follow.
|
|
* These were moved here from save.c and restore.c between 3.6.3 and 3.7.0.
|
|
*/
|
|
|
|
staticfn int getidx(int, int);
|
|
|
|
#if defined(UNIX) || defined(WIN32)
|
|
#define USE_BUFFERING
|
|
#endif
|
|
|
|
struct restore_info restoreinfo = {
|
|
"externalcomp", 0,
|
|
};
|
|
|
|
#define MAXFD 5
|
|
enum {NOFLG = 0, NOSLOT = 1};
|
|
static int bw_sticky[MAXFD] = {-1,-1,-1,-1,-1};
|
|
static int bw_buffered[MAXFD] = {0,0,0,0,0};
|
|
#ifdef USE_BUFFERING
|
|
static FILE *bw_FILE[MAXFD] = {0,0,0,0,0};
|
|
#endif
|
|
|
|
/*
|
|
* Presumably, the fdopen() to allow use of stdio fwrite()
|
|
* over write() was done for performance or functionality
|
|
* reasons to help some particular platform long ago.
|
|
*
|
|
* There have been some issues being encountered with the
|
|
* implementation due to having an individual set of
|
|
* tracking variables, even though there were nested
|
|
* sets of open fd (like INSURANCE).
|
|
*
|
|
* This uses an individual tracking entry for each fd
|
|
* being used.
|
|
*
|
|
* Some notes:
|
|
*
|
|
* Once buffered IO (stdio) has been enabled on the file
|
|
* associated with a descriptor via fdopen():
|
|
*
|
|
* 1. If you use bufoff and bufon to try to toggle the
|
|
* use of write vs fwrite; the code just tracks which
|
|
* routine is to be called through the tracking
|
|
* variables and acts accordingly.
|
|
* bw_sticky[] - used to find the index number for
|
|
* the fd that is stored in it, or -1
|
|
* if it is a free slot.
|
|
* bw_buffered[] - indicator that buffered IO routines
|
|
* are available for use.
|
|
* bw_FILE[] - the non-zero FILE * for use in calling
|
|
* fwrite() when bw_buffered[] is also
|
|
* non-zero.
|
|
*
|
|
* 2. It is illegal to call close(fd) after fdopen(), you
|
|
* must always use fclose() on the FILE * from
|
|
* that point on, so care must be taken to never call
|
|
* close(fd) on the underlying fd or bad things will
|
|
* happen.
|
|
*/
|
|
|
|
staticfn int
|
|
getidx(int fd, int flg)
|
|
{
|
|
int i, retval = -1;
|
|
|
|
for (i = 0; i < MAXFD; ++i)
|
|
if (bw_sticky[i] == fd)
|
|
return i;
|
|
if (flg == NOSLOT)
|
|
return retval;
|
|
for (i = 0; i < MAXFD; ++i)
|
|
if (bw_sticky[i] < 0) {
|
|
bw_sticky[i] = fd;
|
|
retval = i;
|
|
break;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
/* Let caller know that bclose() should handle it (TRUE) */
|
|
boolean
|
|
close_check(int fd)
|
|
{
|
|
int idx = getidx(fd, NOSLOT);
|
|
boolean retval = FALSE;
|
|
|
|
if (idx >= 0)
|
|
retval = TRUE;
|
|
return retval;
|
|
}
|
|
|
|
void
|
|
bufon(int fd)
|
|
{
|
|
int idx = getidx(fd, NOFLG);
|
|
|
|
if (idx >= 0) {
|
|
bw_sticky[idx] = fd;
|
|
#ifdef USE_BUFFERING
|
|
if (bw_buffered[idx])
|
|
panic("buffering already enabled");
|
|
if (!bw_FILE[idx]) {
|
|
if ((bw_FILE[idx] = fdopen(fd, "w")) == 0)
|
|
panic("buffering of file %d failed", fd);
|
|
}
|
|
bw_buffered[idx] = (bw_FILE[idx] != 0);
|
|
#else
|
|
bw_buffered[idx] = 1;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void
|
|
bufoff(int fd)
|
|
{
|
|
int idx = getidx(fd, NOFLG);
|
|
|
|
if (idx >= 0) {
|
|
bflush(fd);
|
|
bw_buffered[idx] = 0; /* just a flag that says "use write(fd)" */
|
|
}
|
|
}
|
|
|
|
void
|
|
bclose(int fd)
|
|
{
|
|
int idx = getidx(fd, NOSLOT);
|
|
|
|
bufoff(fd); /* sets bw_buffered[idx] = 0 */
|
|
if (idx >= 0) {
|
|
#ifdef USE_BUFFERING
|
|
if (bw_FILE[idx]) {
|
|
(void) fclose(bw_FILE[idx]);
|
|
bw_FILE[idx] = 0;
|
|
} else
|
|
#endif
|
|
close(fd);
|
|
/* return the idx to the pool */
|
|
bw_sticky[idx] = -1;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void
|
|
bflush(int fd)
|
|
{
|
|
int idx = getidx(fd, NOFLG);
|
|
|
|
if (idx >= 0) {
|
|
#ifdef USE_BUFFERING
|
|
if (bw_FILE[idx]) {
|
|
if (fflush(bw_FILE[idx]) == EOF)
|
|
panic("flush of savefile failed!");
|
|
}
|
|
#endif
|
|
}
|
|
return;
|
|
}
|
|
|
|
void
|
|
bwrite(int fd, const genericptr_t loc, unsigned num)
|
|
{
|
|
boolean failed;
|
|
int idx = getidx(fd, NOFLG);
|
|
|
|
if (idx >= 0) {
|
|
if (num == 0) {
|
|
/* nothing to do; we need a special case to exit early
|
|
because glibc fwrite doesn't give reliable
|
|
success/failure indication when writing 0 bytes */
|
|
return;
|
|
}
|
|
|
|
#ifdef USE_BUFFERING
|
|
if (bw_buffered[idx] && bw_FILE[idx]) {
|
|
failed = (fwrite(loc, (int) num, 1, bw_FILE[idx]) != 1);
|
|
} else
|
|
#endif /* UNIX */
|
|
{
|
|
/* lint wants 3rd arg of write to be an int; lint -p an unsigned */
|
|
#if defined(BSD) || defined(ULTRIX) || defined(WIN32) || defined(_MSC_VER)
|
|
failed = ((long) write(fd, loc, (int) num) != (long) num);
|
|
#else /* e.g. SYSV, __TURBOC__ */
|
|
failed = ((long) write(fd, loc, num) != (long) num);
|
|
#endif
|
|
}
|
|
if (failed) {
|
|
#if defined(HANGUPHANDLING)
|
|
if (program_state.done_hup)
|
|
nh_terminate(EXIT_FAILURE);
|
|
else
|
|
#endif
|
|
panic("cannot write %u bytes to file #%d", num, fd);
|
|
}
|
|
} else
|
|
impossible("fd not in list (%d)?", fd);
|
|
}
|
|
|
|
/* ===================================================== */
|
|
|
|
void
|
|
minit(void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void
|
|
mread(int fd, genericptr_t buf, unsigned len)
|
|
{
|
|
#if defined(BSD) || defined(ULTRIX)
|
|
#define readLenType int
|
|
#else /* e.g. SYSV, __TURBOC__ */
|
|
#define readLenType unsigned
|
|
#endif
|
|
readLenType rlen;
|
|
/* Not perfect, but we don't have ssize_t available. */
|
|
rlen = (readLenType) read(fd, buf, (readLenType) len);
|
|
if ((readLenType) rlen != (readLenType) len) {
|
|
if (restoreinfo.mread_flags == 1) { /* means "return anyway" */
|
|
restoreinfo.mread_flags = -1;
|
|
return;
|
|
} else {
|
|
pline("Read %d instead of %u bytes.", (int) rlen, len);
|
|
display_nhwindow(WIN_MESSAGE, TRUE); /* flush before error() */
|
|
if (program_state.restoring) {
|
|
(void) nhclose(fd);
|
|
(void) delete_savefile();
|
|
error("Error restoring old game.");
|
|
}
|
|
panic("Error reading level file.");
|
|
}
|
|
}
|
|
}
|
|
|
|
|