Files
nethack/src/sfstruct.c
2025-08-28 11:43:47 -04:00

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 */