631 lines
19 KiB
C
631 lines
19 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, 2025. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#include "hack.h"
|
|
#include "sfprocs.h"
|
|
|
|
/* #define SFLOGGING */ /* debugging */
|
|
|
|
staticfn void sfstruct_read_error(void);
|
|
|
|
/* historical full struct savings */
|
|
|
|
#ifdef SAVEFILE_DEBUGGING
|
|
#if defined(__GNUC__)
|
|
#define DEBUGFORMATSTR64 "%s %s %ld %ld %d\n"
|
|
#elif defined(_MSC_VER)
|
|
#define DEBUGFORMATSTR64 "%s %s %lld %ld %d\n"
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef SFLOGGING
|
|
staticfn void logging_finish(void);
|
|
#endif
|
|
|
|
#define SFO_BODY(dt) \
|
|
{ \
|
|
bwrite(nhfp->fd, (genericptr_t) d_##dt, sizeof *d_##dt); \
|
|
}
|
|
|
|
#define SFI_BODY(dt) \
|
|
{ \
|
|
if (nhfp->eof) { \
|
|
sfstruct_read_error(); \
|
|
} \
|
|
mread(nhfp->fd, (genericptr_t) d_##dt, sizeof *d_##dt); \
|
|
if (restoreinfo.mread_flags == -1) \
|
|
nhfp->eof = TRUE; \
|
|
}
|
|
|
|
#define SF_A(dtyp) \
|
|
void historical_sfo_##dtyp(NHFILE *, dtyp *d_##dtyp, const char *); \
|
|
void historical_sfi_##dtyp(NHFILE *, dtyp *d_##dtyp, const char *); \
|
|
\
|
|
void historical_sfo_##dtyp(NHFILE *nhfp, dtyp *d_##dtyp, \
|
|
const char *myname UNUSED) \
|
|
SFO_BODY(dtyp) \
|
|
\
|
|
void historical_sfi_##dtyp(NHFILE *nhfp, dtyp *d_##dtyp, \
|
|
const char *myname UNUSED) \
|
|
SFI_BODY(dtyp)
|
|
|
|
#define SFO_CBODY(dt) \
|
|
{ \
|
|
norm_ptrs_##dt(d_##dt); \
|
|
bwrite(nhfp->fd, (genericptr_t) d_##dt, sizeof *d_##dt); \
|
|
}
|
|
|
|
#define SFI_CBODY(dt) \
|
|
{ \
|
|
if (nhfp->eof) { \
|
|
sfstruct_read_error(); \
|
|
} \
|
|
mread(nhfp->fd, (genericptr_t) d_##dt, sizeof *d_##dt); \
|
|
norm_ptrs_##dt(d_##dt); \
|
|
if (restoreinfo.mread_flags == -1) \
|
|
nhfp->eof = TRUE; \
|
|
}
|
|
|
|
#define SF_C(keyw, dtyp) \
|
|
void historical_sfo_##dtyp(NHFILE *, keyw dtyp *d_##dtyp, \
|
|
const char *); \
|
|
void historical_sfi_##dtyp(NHFILE *, keyw dtyp *d_##dtyp, \
|
|
const char *); \
|
|
extern void norm_ptrs_##dtyp(keyw dtyp *d_##dtyp); \
|
|
\
|
|
void historical_sfo_##dtyp(NHFILE *nhfp, keyw dtyp *d_##dtyp, \
|
|
const char *myname UNUSED) \
|
|
SFO_CBODY(dtyp) \
|
|
\
|
|
void historical_sfi_##dtyp(NHFILE *nhfp, keyw dtyp *d_##dtyp, \
|
|
const char *myname UNUSED) \
|
|
SFI_CBODY(dtyp)
|
|
|
|
#define SF_X(xxx, dtyp) \
|
|
void historical_sfo_##dtyp(NHFILE *, xxx *d_##dtyp, const char *, int); \
|
|
void historical_sfi_##dtyp(NHFILE *, xxx *d_##dtyp, const char *, int); \
|
|
\
|
|
void historical_sfo_##dtyp(NHFILE *nhfp, xxx *d_##dtyp, \
|
|
const char *myname UNUSED, int bflen UNUSED) \
|
|
SFO_BODY(dtyp) \
|
|
\
|
|
void historical_sfi_##dtyp(NHFILE *nhfp, xxx *d_##dtyp, \
|
|
const char *myname UNUSED, int bflen UNUSED) \
|
|
SFI_BODY(dtyp)
|
|
|
|
|
|
#include "sfmacros.h"
|
|
SF_C(struct, version_info)
|
|
|
|
void historical_sfo_char(NHFILE *, char *d_char, const char *, int);
|
|
void historical_sfi_char(NHFILE *, char *d_char, const char *, int);
|
|
|
|
void
|
|
historical_sfo_char(NHFILE *nhfp, char *d_char,
|
|
const char *myname UNUSED, int cnt)
|
|
{
|
|
bwrite(nhfp->fd, (genericptr_t) d_char, cnt * sizeof (char));
|
|
}
|
|
|
|
void
|
|
historical_sfi_char(NHFILE *nhfp, char *d_char,
|
|
const char *myname UNUSED, int cnt)
|
|
{
|
|
mread(nhfp->fd, (genericptr_t) d_char, cnt * sizeof (char));
|
|
if (restoreinfo.mread_flags == -1)
|
|
nhfp->eof = TRUE;
|
|
}
|
|
//extern void sfo_genericptr(NHFILE *, void **, const char *);
|
|
//extern void sfi_genericptr(NHFILE *, void **, const char *);
|
|
//extern void sfo_x_genericptr(NHFILE *, void **, const char *);
|
|
//extern void sfi_x_genericptr(NHFILE *, void **, const char *);
|
|
|
|
void historical_sfo_genericptr_t(NHFILE *, genericptr_t *d_genericptr_t,
|
|
const char *);
|
|
void historical_sfi_genericptr_t(NHFILE *, genericptr_t *d_genericptr_t,
|
|
const char *);
|
|
void
|
|
historical_sfo_genericptr_t(NHFILE *nhfp, genericptr_t *d_genericptr_t,
|
|
const char *myname UNUSED)
|
|
{
|
|
bwrite(nhfp->fd, (genericptr_t) d_genericptr_t, sizeof *d_genericptr_t);
|
|
}
|
|
void
|
|
historical_sfi_genericptr_t(NHFILE *nhfp, genericptr_t *d_genericptr_t,
|
|
const char *myname UNUSED)
|
|
{
|
|
if (nhfp->eof) {
|
|
sfstruct_read_error();
|
|
}
|
|
mread(nhfp->fd, (genericptr_t) d_genericptr_t, sizeof *d_genericptr_t);
|
|
if (restoreinfo.mread_flags == -1)
|
|
nhfp->eof = TRUE;
|
|
}
|
|
|
|
SF_X(uint8_t, bitfield)
|
|
|
|
struct sf_structlevel_procs historical_sfo_procs = {
|
|
"",
|
|
/* sf */
|
|
{
|
|
historical_sfo_arti_info,
|
|
historical_sfo_nhrect,
|
|
historical_sfo_branch,
|
|
historical_sfo_bubble,
|
|
historical_sfo_cemetery,
|
|
historical_sfo_context_info,
|
|
historical_sfo_nhcoord,
|
|
historical_sfo_damage,
|
|
historical_sfo_dest_area,
|
|
historical_sfo_dgn_topology,
|
|
historical_sfo_dungeon,
|
|
historical_sfo_d_level,
|
|
historical_sfo_ebones,
|
|
historical_sfo_edog,
|
|
historical_sfo_egd,
|
|
historical_sfo_emin,
|
|
historical_sfo_engr,
|
|
historical_sfo_epri,
|
|
historical_sfo_eshk,
|
|
historical_sfo_fe,
|
|
historical_sfo_flag,
|
|
historical_sfo_fruit,
|
|
historical_sfo_gamelog_line,
|
|
historical_sfo_kinfo,
|
|
historical_sfo_levelflags,
|
|
historical_sfo_ls_t,
|
|
historical_sfo_linfo,
|
|
historical_sfo_mapseen_feat,
|
|
historical_sfo_mapseen_flags,
|
|
historical_sfo_mapseen_rooms,
|
|
historical_sfo_mkroom,
|
|
historical_sfo_monst,
|
|
historical_sfo_mvitals,
|
|
historical_sfo_obj,
|
|
historical_sfo_objclass,
|
|
historical_sfo_q_score,
|
|
historical_sfo_rm,
|
|
historical_sfo_spell,
|
|
historical_sfo_stairway,
|
|
historical_sfo_s_level,
|
|
historical_sfo_trap,
|
|
historical_sfo_version_info,
|
|
historical_sfo_you,
|
|
historical_sfo_any,
|
|
|
|
historical_sfo_aligntyp,
|
|
historical_sfo_boolean,
|
|
historical_sfo_coordxy,
|
|
historical_sfo_genericptr_t,
|
|
historical_sfo_int,
|
|
historical_sfo_int16,
|
|
historical_sfo_int32,
|
|
historical_sfo_int64,
|
|
historical_sfo_long,
|
|
historical_sfo_schar,
|
|
historical_sfo_short,
|
|
historical_sfo_size_t,
|
|
historical_sfo_time_t,
|
|
historical_sfo_uchar,
|
|
historical_sfo_uint16,
|
|
historical_sfo_uint32,
|
|
historical_sfo_uint64,
|
|
historical_sfo_ulong,
|
|
historical_sfo_unsigned,
|
|
historical_sfo_ushort,
|
|
historical_sfo_xint16,
|
|
historical_sfo_xint8,
|
|
historical_sfo_char,
|
|
historical_sfo_bitfield,
|
|
}
|
|
};
|
|
|
|
struct sf_structlevel_procs historical_sfi_procs = {
|
|
"",
|
|
/* sf */
|
|
{
|
|
historical_sfi_arti_info,
|
|
historical_sfi_nhrect,
|
|
historical_sfi_branch,
|
|
historical_sfi_bubble,
|
|
historical_sfi_cemetery,
|
|
historical_sfi_context_info,
|
|
historical_sfi_nhcoord,
|
|
historical_sfi_damage,
|
|
historical_sfi_dest_area,
|
|
historical_sfi_dgn_topology,
|
|
historical_sfi_dungeon,
|
|
historical_sfi_d_level,
|
|
historical_sfi_ebones,
|
|
historical_sfi_edog,
|
|
historical_sfi_egd,
|
|
historical_sfi_emin,
|
|
historical_sfi_engr,
|
|
historical_sfi_epri,
|
|
historical_sfi_eshk,
|
|
historical_sfi_fe,
|
|
historical_sfi_flag,
|
|
historical_sfi_fruit,
|
|
historical_sfi_gamelog_line,
|
|
historical_sfi_kinfo,
|
|
historical_sfi_levelflags,
|
|
historical_sfi_ls_t,
|
|
historical_sfi_linfo,
|
|
historical_sfi_mapseen_feat,
|
|
historical_sfi_mapseen_flags,
|
|
historical_sfi_mapseen_rooms,
|
|
historical_sfi_mkroom,
|
|
historical_sfi_monst,
|
|
historical_sfi_mvitals,
|
|
historical_sfi_obj,
|
|
historical_sfi_objclass,
|
|
historical_sfi_q_score,
|
|
historical_sfi_rm,
|
|
historical_sfi_spell,
|
|
historical_sfi_stairway,
|
|
historical_sfi_s_level,
|
|
historical_sfi_trap,
|
|
historical_sfi_version_info,
|
|
historical_sfi_you,
|
|
historical_sfi_any,
|
|
|
|
historical_sfi_aligntyp,
|
|
historical_sfi_boolean,
|
|
historical_sfi_coordxy,
|
|
historical_sfi_genericptr_t,
|
|
historical_sfi_int,
|
|
historical_sfi_int16,
|
|
historical_sfi_int32,
|
|
historical_sfi_int64,
|
|
historical_sfi_long,
|
|
historical_sfi_schar,
|
|
historical_sfi_short,
|
|
historical_sfi_size_t,
|
|
historical_sfi_time_t,
|
|
historical_sfi_uchar,
|
|
historical_sfi_uint16,
|
|
historical_sfi_uint32,
|
|
historical_sfi_uint64,
|
|
historical_sfi_ulong,
|
|
historical_sfi_unsigned,
|
|
historical_sfi_ushort,
|
|
historical_sfi_xint16,
|
|
historical_sfi_xint8,
|
|
historical_sfi_char,
|
|
historical_sfi_bitfield,
|
|
}
|
|
};
|
|
|
|
/*
|
|
* The historical bwrite() and mread() functions follow
|
|
*/
|
|
|
|
#ifdef SAVEFILE_DEBUGGING
|
|
static long floc = 0L;
|
|
#endif
|
|
|
|
/*
|
|
* 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
|
|
|
|
#ifdef SFLOGGING
|
|
static FILE *ofp[20] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
char ofnamebuf[80];
|
|
static long ocnt = 0L;
|
|
|
|
static FILE *ifp[20] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
char ifnamebuf[80];
|
|
static long icnt = 0L;
|
|
#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;
|
|
}
|
|
#ifdef SFLOGGING
|
|
if (fd >= 0 && fd <= SIZE(ofp)) {
|
|
if (ofp[fd]) {
|
|
fclose(ofp[fd]);
|
|
ofp[fd] = 0;
|
|
} else if (ifp[fd]) {
|
|
fclose(ifp[fd]);
|
|
ifp[fd] = 0;
|
|
}
|
|
}
|
|
#endif
|
|
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);
|
|
|
|
#ifdef SFLOGGING
|
|
if (fd >= 0 && fd < SIZE(ofp)) {
|
|
if (!ofp[fd]) {
|
|
Snprintf(ofnamebuf, sizeof ofnamebuf, "bwrite_%02d.log", fd);
|
|
ofp[fd] = fopen(ofnamebuf, "w");
|
|
}
|
|
if (ofp[fd]) {
|
|
fprintf(ofp[fd], "%08ld, %08ld, %d\n", ocnt,
|
|
ftell(ofp[fd]), num);
|
|
ocnt++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
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
|
|
mread(int fd, genericptr_t buf, unsigned len)
|
|
{
|
|
|
|
#ifdef SFLOGGING
|
|
if (fd >= 0 && fd < 9) {
|
|
if (!ifp[fd]) {
|
|
Snprintf(ifnamebuf, sizeof ifnamebuf, "mread_%02d.log", fd);
|
|
ifp[fd] = fopen(ifnamebuf, "w");
|
|
}
|
|
if (ifp[fd]) {
|
|
fprintf(ifp[fd], "%08ld, %08ld, %d\n", icnt,
|
|
ftell(ifp[fd]), (int) len);
|
|
icnt++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(BSD) || defined(ULTRIX) || defined(WIN32)
|
|
#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 {
|
|
#ifndef SFCTOOL
|
|
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.");
|
|
#else
|
|
printf("Read %d instead of %u bytes.\n", (int) rlen, len);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
staticfn void
|
|
sfstruct_read_error(void)
|
|
{
|
|
/* problem */;
|
|
}
|
|
|
|
#ifdef SFLOGGING
|
|
staticfn void
|
|
logging_finish(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < SIZE(ofp); ++i) {
|
|
if (ofp[i]) {
|
|
fclose(ofp[i]);
|
|
ofp[i] = 0;
|
|
}
|
|
}
|
|
ocnt = 0L;
|
|
|
|
for (i = 0; i < SIZE(ifp); ++i) {
|
|
if (ifp[i]) {
|
|
fclose(ifp[i]);
|
|
ifp[i] = 0;
|
|
}
|
|
}
|
|
icnt = 0L;
|
|
}
|
|
#endif /* SFLOGGING */
|
|
|
|
#undef SF_X
|
|
#undef SF_C
|
|
#undef SF_A
|
|
|
|
/* end of sfstruct.c */
|
|
|