999 lines
27 KiB
C
999 lines
27 KiB
C
/* NetHack 3.7 mdlib.c $NHDT-Date: 1655402414 2022/06/16 18:00:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.31 $ */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/*-Copyright (c) Kenneth Lorber, Kensington, Maryland, 2015. */
|
|
/* Copyright (c) M. Stephenson, 1990, 1991. */
|
|
/* Copyright (c) Dean Luick, 1990. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
/*
|
|
* This can be linked into a binary to provide the functionality
|
|
* via the contained functions, or it can be #included directly
|
|
* into util/makedefs.c to provide it there.
|
|
*/
|
|
|
|
#ifndef MAKEDEFS_C
|
|
#define MDLIB_C
|
|
#include "config.h"
|
|
#include "permonst.h"
|
|
#include "objclass.h"
|
|
#include "wintype.h"
|
|
#include "sym.h"
|
|
#include "artilist.h"
|
|
#include "dungeon.h"
|
|
#include "sndprocs.h"
|
|
#include "obj.h"
|
|
#include "monst.h"
|
|
#include "you.h"
|
|
#include "context.h"
|
|
#include "flag.h"
|
|
#include "dlb.h"
|
|
#include <ctype.h>
|
|
/* version information */
|
|
#ifdef SHORT_FILENAMES
|
|
#include "patchlev.h"
|
|
#else
|
|
#include "patchlevel.h"
|
|
#endif
|
|
#define Fprintf (void) fprintf
|
|
#define Fclose (void) fclose
|
|
#define Unlink (void) unlink
|
|
#if !defined(AMIGA) || defined(AZTEC_C)
|
|
#define rewind(fp) fseek((fp), 0L, SEEK_SET) /* guarantee a return value */
|
|
#endif /* AMIGA || AZTEC_C */
|
|
#else
|
|
#ifndef GLOBAL_H
|
|
#include "global.h"
|
|
#endif
|
|
#endif /* !MAKEDEFS_C */
|
|
|
|
/* shorten up some lines */
|
|
#define FOR_RUNTIME
|
|
|
|
#if defined(MAKEDEFS_C) || defined(FOR_RUNTIME)
|
|
#include <stdarg.h>
|
|
/* REPRODUCIBLE_BUILD will change this to TRUE */
|
|
static boolean date_via_env = FALSE;
|
|
|
|
extern unsigned long md_ignored_features(void);
|
|
char *mdlib_version_string(char *, const char *);
|
|
char *version_id_string(char *, size_t, const char *);
|
|
char *bannerc_string(char *, size_t, const char *);
|
|
int case_insensitive_comp(const char *, const char *);
|
|
|
|
static void make_version(void);
|
|
static char *eos(char *);
|
|
int mstrength(struct permonst *);
|
|
|
|
#if 0
|
|
static char *mdlib_strsubst(char *, const char *, const char *);
|
|
#endif
|
|
|
|
#ifndef HAS_NO_MKSTEMP
|
|
#ifdef _MSC_VER
|
|
static int mkstemp(char *);
|
|
#endif
|
|
#endif
|
|
|
|
#endif /* MAKEDEFS_C || FOR_RUNTIME */
|
|
|
|
#if !defined(MAKEDEFS_C) && defined(WIN32)
|
|
extern int GUILaunched;
|
|
#endif
|
|
|
|
/* these are in extern.h but we don't include hack.h */
|
|
void runtime_info_init(void);
|
|
const char *do_runtime_info(int *);
|
|
void release_runtime_info(void);
|
|
void populate_nomakedefs(struct version_info *);
|
|
extern void free_nomakedefs(void); /* date.c */
|
|
|
|
void build_options(void);
|
|
static int count_and_validate_winopts(void);
|
|
static void opt_out_words(char *, int *);
|
|
static void build_savebones_compat_string(void);
|
|
|
|
static int idxopttext, done_runtime_opt_init_once = 0;
|
|
#define MAXOPT 40
|
|
static char *opttext[120] = { 0 };
|
|
char optbuf[COLBUFSZ];
|
|
static struct version_info version;
|
|
static const char opt_indent[] = " ";
|
|
|
|
struct win_information {
|
|
const char *id, /* DEFAULT_WINDOW_SYS string */
|
|
*name; /* description, often same as id */
|
|
boolean valid;
|
|
};
|
|
|
|
static struct win_information window_opts[] = {
|
|
#ifdef TTY_GRAPHICS
|
|
{ "tty",
|
|
/* testing 'TILES_IN_GLYPHMAP' here would bring confusion because it could
|
|
apply to another interface such as X11, so check MSDOS explicitly
|
|
instead; even checking TTY_TILES_ESCCODES would probably be
|
|
confusing to most users (and it will already be listed separately
|
|
in the compiled options section so users aware of it can find it) */
|
|
#ifdef MSDOS
|
|
"traditional text with optional 'tiles' graphics",
|
|
#else
|
|
/* assume that one or more of IBMgraphics, DECgraphics
|
|
can be enabled; we can't tell from here whether that is accurate */
|
|
"traditional text with optional line-drawing",
|
|
#endif
|
|
TRUE
|
|
},
|
|
#endif /*TTY_GRAPHICS */
|
|
#ifdef CURSES_GRAPHICS
|
|
{ "curses", "terminal-based graphics", TRUE },
|
|
#endif
|
|
#ifdef X11_GRAPHICS
|
|
{ "X11", "X11", TRUE },
|
|
#endif
|
|
#ifdef QT_GRAPHICS /* too vague; there are multiple incompatible versions */
|
|
{ "Qt", "Qt", TRUE },
|
|
#endif
|
|
#ifdef MSWIN_GRAPHICS /* win32 */
|
|
{ "mswin", "Windows GUI", TRUE },
|
|
#endif
|
|
#ifdef SHIM_GRAPHICS
|
|
{ "shim", "NetHack Library Windowing Shim", TRUE },
|
|
#endif
|
|
|
|
#if 0 /* remainder have been retired */
|
|
#ifdef GNOME_GRAPHICS /* unmaintained/defunct */
|
|
{ "Gnome", "Gnome", TRUE },
|
|
#endif
|
|
#ifdef MAC /* defunct OS 9 interface */
|
|
{ "mac", "Mac", TRUE },
|
|
#endif
|
|
#ifdef AMIGA_INTUITION /* unmaintained/defunct */
|
|
{ "amii", "Amiga Intuition", TRUE },
|
|
#endif
|
|
#ifdef GEM_GRAPHICS /* defunct Atari interface */
|
|
{ "Gem", "Gem", TRUE },
|
|
#endif
|
|
#ifdef BEOS_GRAPHICS /* unmaintained/defunct */
|
|
{ "BeOS", "BeOS InterfaceKit", TRUE },
|
|
#endif
|
|
#endif /* 0 => retired */
|
|
{ 0, 0, FALSE }
|
|
};
|
|
|
|
#if !defined(MAKEDEFS_C)
|
|
static int count_and_validate_soundlibopts(void);
|
|
|
|
struct soundlib_information {
|
|
enum soundlib_ids id;
|
|
const char *const text_id;
|
|
const char *const Url;
|
|
boolean valid;
|
|
};
|
|
|
|
/*
|
|
* soundlibs
|
|
*
|
|
* None of these are endorsements or recommendations of one library
|
|
* or another, in any way. They are just preprocessor conditionals
|
|
* in the event that glue code for such a library is ever added into
|
|
* NetHack.
|
|
*/
|
|
static struct soundlib_information soundlib_opts[] = {
|
|
{ soundlib_nosound, "soundlib_nosound", "", FALSE },
|
|
#ifdef SND_LIB_PORTAUDIO
|
|
{ soundlib_portaudio, "soundlib_portaudio",
|
|
"http://www.portaudio.com/", FALSE },
|
|
#endif
|
|
#ifdef SND_LIB_OPENAL
|
|
{ soundlib_openal, "soundlib_openal",
|
|
"https://www.openal.org/", FALSE },
|
|
#endif
|
|
#ifdef SND_LIB_SDL_MIXER
|
|
{ soundlib_sdl_mixer, "soundlib_sdl_mixer",
|
|
"https://github.com/libsdl-org/SDL_mixer/", FALSE },
|
|
#endif
|
|
#ifdef SND_LIB_MINIAUDIO
|
|
{ soundlib_miniaudio, "soundlib_miniaudio",
|
|
"https://miniaud.io/", FALSE },
|
|
#endif
|
|
#ifdef SND_LIB_FMOD
|
|
/* proprietary, though a Free Indie License exists.
|
|
* https://www.fmod.com/licensing#indie-note
|
|
*/
|
|
{ soundlib_fmod, "soundlib_fmod",
|
|
"https://www.fmod.com/", FALSE },
|
|
#endif
|
|
#ifdef SND_LIB_SOUND_ESCCODES
|
|
{ soundlib_sound_esccodes, "sound_esccodes", "", FALSE },
|
|
#endif
|
|
#ifdef SND_LIB_VISSOUND
|
|
{ soundlib_vissound, "soundlib_vissound", "", FALSE },
|
|
#endif
|
|
#ifdef SND_LIB_WINDSOUND
|
|
/* Uses Windows WIN32 API */
|
|
{ soundlib_windsound, "soundlib_windsound",
|
|
"https://learn.microsoft.com/en-us/windows/win32/multimedia/waveform-audio",
|
|
FALSE },
|
|
#endif
|
|
#ifdef SND_LIB_MACSOUND
|
|
/* Uses AppKit NSSound */
|
|
{ soundlib_macsound, "soundlib_macsound",
|
|
"https://developer.apple.com/documentation/appkit/nssound",
|
|
FALSE },
|
|
#endif
|
|
#ifdef SND_LIB_QTSOUND
|
|
{ soundlib_qtsound, "soundlib_qtsound",
|
|
"https://doc.qt.io/qt-5/qsoundeffect.html", FALSE },
|
|
#endif
|
|
{ 0, 0, 0, FALSE },
|
|
};
|
|
#endif /* !MAKEDEFS_C */
|
|
|
|
/*
|
|
* Use this to explicitly mask out features during version checks.
|
|
*
|
|
* ZEROCOMP, RLECOMP, and ZLIB_COMP describe compression features
|
|
* that the port/plaform which wrote the savefile was capable of
|
|
* dealing with. Don't reject a savefile just because the port
|
|
* reading the savefile doesn't match on all/some of them.
|
|
* The actual compression features used to produce the savefile are
|
|
* recorded in the savefile_info structure immediately following the
|
|
* version_info, and that is what needs to be checked against the
|
|
* feature set of the port that is reading the savefile back in.
|
|
* That check is done in src/restore.c now.
|
|
*
|
|
*/
|
|
unsigned long
|
|
md_ignored_features(void)
|
|
{
|
|
return (0UL
|
|
| (1UL << 19) /* SCORE_ON_BOTL */
|
|
| (1UL << 27) /* ZEROCOMP */
|
|
| (1UL << 28) /* RLECOMP */
|
|
);
|
|
}
|
|
|
|
static void
|
|
make_version(void)
|
|
{
|
|
register int i;
|
|
|
|
/*
|
|
* integer version number
|
|
*/
|
|
version.incarnation = ((unsigned long) VERSION_MAJOR << 24)
|
|
| ((unsigned long) VERSION_MINOR << 16)
|
|
| ((unsigned long) PATCHLEVEL << 8)
|
|
| ((unsigned long) EDITLEVEL);
|
|
/*
|
|
* encoded feature list
|
|
* Note: if any of these magic numbers are changed or reassigned,
|
|
* EDITLEVEL in patchlevel.h should be incremented at the same time.
|
|
* The actual values have no special meaning, and the category
|
|
* groupings are just for convenience.
|
|
*/
|
|
version.feature_set = (unsigned long) (0L
|
|
/* levels and/or topology (0..4) */
|
|
/* monsters (5..9) */
|
|
#ifdef MAIL_STRUCTURES
|
|
| (1L << 6)
|
|
#endif
|
|
/* objects (10..14) */
|
|
/* flag bits and/or other global variables (15..26) */
|
|
#ifdef TEXTCOLOR
|
|
| (1L << 17)
|
|
#endif
|
|
#ifdef INSURANCE
|
|
| (1L << 18)
|
|
#endif
|
|
#ifdef SCORE_ON_BOTL
|
|
| (1L << 19)
|
|
#endif
|
|
/* data format (27..31)
|
|
* External compression methods such as COMPRESS and ZLIB_COMP
|
|
* do not affect the contents and are thus excluded from here */
|
|
#ifdef ZEROCOMP
|
|
| (1L << 27)
|
|
#endif
|
|
#ifdef RLECOMP
|
|
| (1L << 28)
|
|
#endif
|
|
);
|
|
/*
|
|
* Value used for object & monster sanity check.
|
|
* (NROFARTIFACTS<<24) | (NUM_OBJECTS<<12) | (NUMMONS<<0)
|
|
*/
|
|
for (i = 1; artifact_names[i]; i++)
|
|
continue;
|
|
version.entity_count = (unsigned long) (i - 1);
|
|
for (i = 1; objects[i].oc_class != ILLOBJ_CLASS; i++)
|
|
continue;
|
|
version.entity_count = (version.entity_count << 12) | (unsigned long) i;
|
|
for (i = 0; mons[i].mlet; i++)
|
|
continue;
|
|
version.entity_count = (version.entity_count << 12) | (unsigned long) i;
|
|
/*
|
|
* Value used for compiler (word size/field alignment/padding) check.
|
|
*/
|
|
version.struct_sizes1 =
|
|
(((unsigned long) sizeof(struct context_info) << 24)
|
|
| ((unsigned long) sizeof(struct obj) << 17)
|
|
| ((unsigned long) sizeof(struct monst) << 10)
|
|
| ((unsigned long) sizeof(struct you)));
|
|
version.struct_sizes2 = (((unsigned long) sizeof(struct flag) << 10));
|
|
/* free bits in here */
|
|
return;
|
|
}
|
|
|
|
#if defined(MAKEDEFS_C) || defined(FOR_RUNTIME)
|
|
|
|
char *
|
|
mdlib_version_string(char *outbuf, const char *delim)
|
|
{
|
|
Sprintf(outbuf, "%d%s%d%s%d", VERSION_MAJOR, delim, VERSION_MINOR, delim,
|
|
PATCHLEVEL);
|
|
#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
|
|
Sprintf(eos(outbuf), "-%d", EDITLEVEL);
|
|
#endif
|
|
return outbuf;
|
|
}
|
|
|
|
#define Snprintf(str, size, ...) \
|
|
nh_snprintf(__func__, __LINE__, str, size, __VA_ARGS__)
|
|
extern void nh_snprintf(const char *func, int line, char *str, size_t size,
|
|
const char *fmt, ...);
|
|
|
|
#ifdef MAKEDEFS_C
|
|
DISABLE_WARNING_FORMAT_NONLITERAL
|
|
|
|
void
|
|
nh_snprintf(const char *func UNUSED, int line UNUSED, char *str, size_t size,
|
|
const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
int n;
|
|
|
|
va_start(ap, fmt);
|
|
n = vsnprintf(str, size, fmt, ap);
|
|
va_end(ap);
|
|
|
|
if (n < 0 || (size_t)n >= size) { /* is there a problem? */
|
|
str[size-1] = 0; /* make sure it is nul terminated */
|
|
}
|
|
|
|
}
|
|
|
|
RESTORE_WARNING_FORMAT_NONLITERAL
|
|
#endif /* MAKEDEFS_C */
|
|
|
|
char *
|
|
version_id_string(char *outbuf, size_t bufsz, const char *build_date)
|
|
{
|
|
char subbuf[64], versbuf[64];
|
|
char statusbuf[64];
|
|
|
|
#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
|
|
#if (NH_DEVEL_STATUS == NH_STATUS_BETA)
|
|
Strcpy(statusbuf, " Beta");
|
|
#else
|
|
#if (NH_DEVEL_STATUS == NH_STATUS_WIP)
|
|
Strcpy(statusbuf, " Work-in-progress");
|
|
#else
|
|
Strcpy(statusbuf, " post-release");
|
|
#endif
|
|
#endif
|
|
#else
|
|
statusbuf[0] = '\0';
|
|
#endif
|
|
subbuf[0] = '\0';
|
|
#ifdef PORT_SUB_ID
|
|
subbuf[0] = ' ';
|
|
Strcpy(&subbuf[1], PORT_SUB_ID);
|
|
#endif
|
|
|
|
Snprintf(outbuf, bufsz, "%s NetHack%s Version %s%s - last %s %s.", PORT_ID,
|
|
subbuf, mdlib_version_string(versbuf, "."), statusbuf,
|
|
date_via_env ? "revision" : "build", build_date);
|
|
return outbuf;
|
|
}
|
|
|
|
/* still within #if MAKDEFS_C || FOR_RUNTIME */
|
|
|
|
char *
|
|
bannerc_string(char *outbuf, size_t bufsz, const char *build_date)
|
|
{
|
|
char subbuf[64], versbuf[64];
|
|
|
|
subbuf[0] = '\0';
|
|
#ifdef PORT_SUB_ID
|
|
subbuf[0] = ' ';
|
|
Strcpy(&subbuf[1], PORT_SUB_ID);
|
|
#endif
|
|
#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
|
|
#if (NH_DEVEL_STATUS == NH_STATUS_BETA)
|
|
Strcat(subbuf, " Beta");
|
|
#else
|
|
Strcat(subbuf, " Work-in-progress");
|
|
#endif
|
|
#endif
|
|
|
|
Snprintf(outbuf, bufsz, " Version %s %s%s, %s %s.",
|
|
mdlib_version_string(versbuf, "."), PORT_ID, subbuf,
|
|
date_via_env ? "revised" : "built", build_date);
|
|
return outbuf;
|
|
}
|
|
|
|
#ifndef HAS_NO_MKSTEMP
|
|
#ifdef _MSC_VER
|
|
int
|
|
mkstemp(char *template)
|
|
{
|
|
int err;
|
|
|
|
err = _mktemp_s(template, strlen(template) + 1);
|
|
if( err != 0 )
|
|
return -1;
|
|
return _open(template,
|
|
_O_RDWR | _O_BINARY | _O_TEMPORARY | _O_CREAT,
|
|
_S_IREAD | _S_IWRITE);
|
|
}
|
|
#endif /* _MSC_VER */
|
|
#endif /* HAS_NO_MKSTEMP */
|
|
#endif /* MAKEDEFS_C || FOR_RUNTIME */
|
|
|
|
static char *
|
|
eos(char *str)
|
|
{
|
|
while (*str)
|
|
str++;
|
|
return str;
|
|
}
|
|
|
|
#if 0
|
|
static char *
|
|
mdlib_strsubst(char *bp, const char *orig, const char *replacement)
|
|
{
|
|
char *found, buf[BUFSZ];
|
|
|
|
if (bp) {
|
|
/* [this could be replaced by strNsubst(bp, orig, replacement, 1)] */
|
|
found = strstr(bp, orig);
|
|
if (found) {
|
|
Strcpy(buf, found + strlen(orig));
|
|
Strcpy(found, replacement);
|
|
Strcat(bp, buf);
|
|
}
|
|
}
|
|
return bp;
|
|
}
|
|
#endif
|
|
|
|
static char save_bones_compat_buf[BUFSZ];
|
|
|
|
static void
|
|
build_savebones_compat_string(void)
|
|
{
|
|
#ifdef VERSION_COMPATIBILITY
|
|
unsigned long uver = VERSION_COMPATIBILITY,
|
|
cver = (((unsigned long) VERSION_MAJOR << 24)
|
|
| ((unsigned long) VERSION_MINOR << 16)
|
|
| ((unsigned long) PATCHLEVEL << 8));
|
|
#endif
|
|
|
|
Strcpy(save_bones_compat_buf,
|
|
"save and bones files accepted from version");
|
|
#ifdef VERSION_COMPATIBILITY
|
|
if (uver != cver)
|
|
Sprintf(eos(save_bones_compat_buf), "s %lu.%lu.%lu through %d.%d.%d",
|
|
((uver >> 24) & 0x0ffUL),
|
|
((uver >> 16) & 0x0ffUL),
|
|
((uver >> 8) & 0x0ffUL),
|
|
VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL);
|
|
else
|
|
#endif
|
|
Sprintf(eos(save_bones_compat_buf), " %d.%d.%d only",
|
|
VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL);
|
|
}
|
|
|
|
static const char *const build_opts[] = {
|
|
#ifdef AMIGA_WBENCH
|
|
"Amiga WorkBench support",
|
|
#endif
|
|
#ifdef ANSI_DEFAULT
|
|
"ANSI default terminal",
|
|
#endif
|
|
#ifdef TEXTCOLOR
|
|
"color",
|
|
#endif
|
|
#ifdef TTY_GRAPHICS
|
|
#ifdef TTY_TILES_ESCCODES
|
|
"console escape codes for tile hinting",
|
|
#endif
|
|
#endif
|
|
#ifdef LIFE
|
|
"Conway's Game of Life",
|
|
#endif
|
|
#ifdef COMPRESS
|
|
"data file compression",
|
|
#endif
|
|
#ifdef ZLIB_COMP
|
|
"ZLIB data file compression",
|
|
#endif
|
|
#ifdef DLB
|
|
#ifndef VERSION_IN_DLB_FILENAME
|
|
"data librarian",
|
|
#else
|
|
"data librarian with a version-dependent name",
|
|
#endif
|
|
#endif
|
|
#ifdef EDIT_GETLIN
|
|
"edit getlin - some prompts remember previous response",
|
|
#endif
|
|
#ifdef DUMPLOG
|
|
"end-of-game dumplogs",
|
|
#endif
|
|
#ifdef HOLD_LOCKFILE_OPEN
|
|
"exclusive lock on level 0 file",
|
|
#endif
|
|
#ifdef MSGHANDLER
|
|
"external program as a message handler",
|
|
#endif
|
|
#if defined(HANGUPHANDLING) && !defined(NO_SIGNAL)
|
|
#ifdef SAFERHANGUP
|
|
"deferred handling of hangup signal",
|
|
#else
|
|
"immediate handling of hangup signal",
|
|
#endif
|
|
#endif
|
|
#ifdef INSURANCE
|
|
"insurance files for recovering from crashes",
|
|
#endif
|
|
#ifdef LIVELOG
|
|
"live logging support",
|
|
#endif
|
|
#ifdef LOGFILE
|
|
"log file",
|
|
#endif
|
|
#ifdef XLOGFILE
|
|
"extended log file",
|
|
#endif
|
|
#ifdef PANICLOG
|
|
"errors and warnings log file",
|
|
#endif
|
|
#ifdef MAIL
|
|
"mail daemon",
|
|
#endif
|
|
#ifdef MONITOR_HEAP
|
|
"monitor heap - record memory usage for later analysis",
|
|
#endif
|
|
#if defined(GNUDOS) || defined(__DJGPP__)
|
|
"MSDOS protected mode",
|
|
#endif
|
|
#ifdef NEWS
|
|
"news file",
|
|
#endif
|
|
#ifdef OVERLAY
|
|
#ifdef MOVERLAY
|
|
"MOVE overlays",
|
|
#else
|
|
#ifdef VROOMM
|
|
"VROOMM overlays",
|
|
#else
|
|
"overlays",
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#ifdef UNIX
|
|
#if defined(DEF_PAGER) && !defined(DLB)
|
|
"external pager used for viewing help files",
|
|
#else
|
|
"internal pager used for viewing help files",
|
|
#endif
|
|
#endif /* UNIX */
|
|
/* pattern matching method will be substituted by nethack at run time */
|
|
"pattern matching via :PATMATCH:",
|
|
#ifdef USE_ISAAC64
|
|
"pseudo random numbers generated by ISAAC64",
|
|
#ifdef DEV_RANDOM
|
|
/* include which specific one */
|
|
"strong PRNG seed from " DEV_RANDOM,
|
|
#else
|
|
#ifdef WIN32
|
|
"strong PRNG seed from CNG BCryptGenRandom()",
|
|
#endif
|
|
#endif /* DEV_RANDOM */
|
|
#else /* ISAAC64 */
|
|
#ifdef RANDOM
|
|
"pseudo random numbers generated by random()",
|
|
#else
|
|
"pseudo random numbers generated by C rand()",
|
|
#endif
|
|
#endif /* ISAAC64 */
|
|
#ifdef SELECTSAVED
|
|
"restore saved games via menu",
|
|
#endif
|
|
#ifdef SCORE_ON_BOTL
|
|
"score on status line",
|
|
#endif
|
|
#ifdef CLIPPING
|
|
"screen clipping",
|
|
#endif
|
|
#ifdef NO_TERMS
|
|
#ifdef MAC
|
|
"screen control via mactty",
|
|
#endif
|
|
#ifdef SCREEN_BIOS
|
|
"screen control via BIOS",
|
|
#endif
|
|
#ifdef SCREEN_DJGPPFAST
|
|
"screen control via DJGPP fast",
|
|
#endif
|
|
#ifdef SCREEN_VGA
|
|
"screen control via VGA graphics",
|
|
#endif
|
|
#ifdef WIN32CON
|
|
"screen control via WIN32 console I/O",
|
|
#endif
|
|
#endif /* NO_TERMS */
|
|
#ifdef SHELL
|
|
"shell command",
|
|
#endif
|
|
"traditional status display",
|
|
#ifdef STATUS_HILITES
|
|
"status via windowport with highlighting",
|
|
#else
|
|
"status via windowport without highlighting",
|
|
#endif
|
|
#ifdef SUSPEND
|
|
"suspend command",
|
|
#endif
|
|
#ifdef TTY_GRAPHICS
|
|
#ifdef TERMINFO
|
|
"terminal info library",
|
|
#else
|
|
#if defined(TERMLIB) || (!defined(MICRO) && !defined(WIN32))
|
|
"terminal capability library",
|
|
#endif
|
|
#endif
|
|
#endif /*TTY_GRAPHICS*/
|
|
#ifdef USE_XPM
|
|
"tiles file in XPM format",
|
|
#endif
|
|
#ifdef GRAPHIC_TOMBSTONE
|
|
"graphical RIP screen",
|
|
#endif
|
|
#ifdef TIMED_DELAY
|
|
"timed wait for display effects",
|
|
#endif
|
|
#ifdef PREFIXES_IN_USE
|
|
"variable playground",
|
|
#endif
|
|
#ifdef VISION_TABLES
|
|
"vision tables",
|
|
#endif
|
|
#ifdef ZEROCOMP
|
|
"zero-compressed save files",
|
|
#endif
|
|
#ifdef RLECOMP
|
|
"run-length compression of map in save files",
|
|
#endif
|
|
#ifdef SYSCF
|
|
"system configuration at run-time",
|
|
#endif
|
|
save_bones_compat_buf,
|
|
"and basic NetHack features"
|
|
};
|
|
|
|
static int
|
|
count_and_validate_winopts(void)
|
|
{
|
|
int i, cnt = 0;
|
|
|
|
/* window_opts has a fencepost entry at the end */
|
|
for (i = 0; i < SIZE(window_opts) - 1; i++) {
|
|
#if !defined(MAKEDEFS_C) && defined(FOR_RUNTIME)
|
|
#ifdef WIN32
|
|
window_opts[i].valid = FALSE;
|
|
if ((GUILaunched
|
|
&& case_insensitive_comp(window_opts[i].id, "mswin") != 0)
|
|
|| (!GUILaunched
|
|
&& case_insensitive_comp(window_opts[i].id, "mswin") == 0))
|
|
continue;
|
|
#endif
|
|
#endif /* !MAKEDEFS_C && FOR_RUNTIME */
|
|
++cnt;
|
|
window_opts[i].valid = TRUE;
|
|
}
|
|
return cnt;
|
|
}
|
|
|
|
#if !defined(MAKEDEFS_C)
|
|
static int
|
|
count_and_validate_soundlibopts(void)
|
|
{
|
|
int i, cnt = 0;
|
|
|
|
/* soundlib_opts has a fencepost entry at the end */
|
|
for (i = 0; i < SIZE(soundlib_opts) - 1; i++) {
|
|
++cnt;
|
|
soundlib_opts[i].valid = TRUE;
|
|
}
|
|
return cnt;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
opt_out_words(
|
|
char *str, /* input, but modified during processing */
|
|
int *length_p) /* in/out */
|
|
{
|
|
char *word;
|
|
|
|
while (*str) {
|
|
word = strchr(str, ' ');
|
|
#if 0
|
|
/* treat " (" as unbreakable space */
|
|
if (word && *(word + 1) == '(')
|
|
word = strchr(word + 1, ' ');
|
|
#endif
|
|
if (word)
|
|
*word = '\0';
|
|
if (*length_p + (int) strlen(str) > COLNO - 5) {
|
|
opttext[idxopttext] = dupstr(optbuf);
|
|
if (idxopttext < (MAXOPT - 1))
|
|
idxopttext++;
|
|
Sprintf(optbuf, "%s", opt_indent),
|
|
*length_p = (int) strlen(opt_indent);
|
|
} else {
|
|
Sprintf(eos(optbuf), " "), (*length_p)++;
|
|
}
|
|
Sprintf(eos(optbuf), "%s", str), *length_p += (int) strlen(str);
|
|
str += strlen(str) + (word ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
build_options(void)
|
|
{
|
|
char buf[COLBUFSZ];
|
|
int i, length, winsyscnt, cnt = 0;
|
|
const char *defwinsys = DEFAULT_WINDOW_SYS;
|
|
#if !defined(MAKEDEFS_C) && defined(FOR_RUNTIME)
|
|
int soundlibcnt;
|
|
|
|
#ifdef WIN32
|
|
defwinsys = GUILaunched ? "mswin" : "tty";
|
|
#endif
|
|
#endif
|
|
build_savebones_compat_string();
|
|
opttext[idxopttext] = dupstr(optbuf);
|
|
if (idxopttext < (MAXOPT - 1))
|
|
idxopttext++;
|
|
#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
|
|
#if (NH_DEVEL_STATUS == NH_STATUS_BETA)
|
|
#define STATUS_ARG " [beta]"
|
|
#else
|
|
#define STATUS_ARG " [work-in-progress]"
|
|
#endif
|
|
#else
|
|
#define STATUS_ARG ""
|
|
#endif /* NH_DEVEL_STATUS == NH_STATUS_RELEASED */
|
|
Sprintf(optbuf, "%sNetHack version %d.%d.%d%s\n",
|
|
opt_indent, VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL, STATUS_ARG);
|
|
opttext[idxopttext] = dupstr(optbuf);
|
|
if (idxopttext < (MAXOPT - 1))
|
|
idxopttext++;
|
|
Sprintf(optbuf, "Options compiled into this edition:");
|
|
opttext[idxopttext] = dupstr(optbuf);
|
|
if (idxopttext < (MAXOPT - 1))
|
|
idxopttext++;
|
|
optbuf[0] = '\0';
|
|
length = COLNO + 1; /* force 1st item onto new line */
|
|
for (i = 0; i < SIZE(build_opts); i++) {
|
|
#if !defined(MAKEDEFS_C) && defined(FOR_RUNTIME)
|
|
#ifdef WIN32
|
|
/* ignore the console entry if GUI version */
|
|
if (GUILaunched
|
|
&& !strcmp("screen control via WIN32 console I/O", build_opts[i]))
|
|
continue;
|
|
#endif
|
|
#endif /* !MAKEDEFS_C && FOR_RUNTIME */
|
|
Strcat(strcpy(buf, build_opts[i]),
|
|
(i < SIZE(build_opts) - 1) ? "," : ".");
|
|
opt_out_words(buf, &length);
|
|
}
|
|
opttext[idxopttext] = dupstr(optbuf);
|
|
if (idxopttext < (MAXOPT - 1))
|
|
idxopttext++;
|
|
optbuf[0] = '\0';
|
|
winsyscnt = count_and_validate_winopts();
|
|
opttext[idxopttext] = dupstr(optbuf);
|
|
if (idxopttext < (MAXOPT - 1))
|
|
idxopttext++;
|
|
Sprintf(optbuf, "Supported windowing system%s:",
|
|
(winsyscnt > 1) ? "s" : "");
|
|
opttext[idxopttext] = dupstr(optbuf);
|
|
if (idxopttext < (MAXOPT - 1))
|
|
idxopttext++;
|
|
optbuf[0] = '\0';
|
|
length = COLNO + 1; /* force 1st item onto new line */
|
|
|
|
for (i = 0; i < SIZE(window_opts) - 1; i++) {
|
|
if (!window_opts[i].valid)
|
|
continue;
|
|
Sprintf(buf, "\"%s\"", window_opts[i].id);
|
|
if (strcmp(window_opts[i].name, window_opts[i].id))
|
|
Sprintf(eos(buf), " (%s)", window_opts[i].name);
|
|
/*
|
|
* 1 : foo.
|
|
* 2 : foo and bar,
|
|
* 3+: for, bar, and quux,
|
|
*
|
|
* 2+ will be followed by " with a default of..."
|
|
*/
|
|
Strcat(buf, (winsyscnt == 1) ? "." /* no 'default' */
|
|
: (winsyscnt == 2 && cnt == 0) ? " and"
|
|
: (cnt == winsyscnt - 2) ? ", and"
|
|
: ",");
|
|
opt_out_words(buf, &length);
|
|
cnt++;
|
|
}
|
|
if (cnt > 1) {
|
|
/* loop ended with a comma; opt_out_words() will insert a space */
|
|
Sprintf(buf, "with a default of \"%s\".", defwinsys);
|
|
opt_out_words(buf, &length);
|
|
}
|
|
|
|
#if !defined(MAKEDEFS_C)
|
|
cnt = 0;
|
|
opttext[idxopttext] = dupstr(optbuf);
|
|
if (idxopttext < (MAXOPT - 1))
|
|
idxopttext++;
|
|
optbuf[0] = '\0';
|
|
soundlibcnt = count_and_validate_soundlibopts();
|
|
opttext[idxopttext] = dupstr(optbuf);
|
|
if (idxopttext < (MAXOPT - 1))
|
|
idxopttext++;
|
|
Sprintf(optbuf, "Supported soundlib%s:",
|
|
(soundlibcnt > 1) ? "s" : "");
|
|
opttext[idxopttext] = dupstr(optbuf);
|
|
if (idxopttext < (MAXOPT - 1))
|
|
idxopttext++;
|
|
optbuf[0] = '\0';
|
|
length = COLNO + 1; /* force 1st item onto new line */
|
|
|
|
#ifdef USER_SOUNDS
|
|
soundlibcnt += 1;
|
|
#endif
|
|
for (i = 0; i < SIZE(soundlib_opts) - 1; i++) {
|
|
const char *soundlib;
|
|
|
|
if (!soundlib_opts[i].valid)
|
|
continue;
|
|
soundlib = soundlib_opts[i].text_id;
|
|
if (!strncmp(soundlib, "soundlib_", 9))
|
|
soundlib += 9;
|
|
Sprintf(buf, "\"%s\"", soundlib);
|
|
/*
|
|
* 1 : foo.
|
|
* 2 : foo and bar.
|
|
* 3+: for, bar, and quux.
|
|
*/
|
|
Strcat(buf, (soundlibcnt == 1 || cnt == soundlibcnt - 1)
|
|
? "." /* no 'with default' */
|
|
: (soundlibcnt == 2 && cnt == 0) ? " and"
|
|
: (cnt == soundlibcnt - 2) ? ", and"
|
|
: ",");
|
|
opt_out_words(buf, &length);
|
|
cnt++;
|
|
}
|
|
#ifdef USER_SOUNDS
|
|
if (cnt > 1) {
|
|
/* loop ended with a comma; opt_out_words() will insert a space */
|
|
Sprintf(buf, "user sounds.");
|
|
opt_out_words(buf, &length);
|
|
}
|
|
#endif
|
|
#endif /* !MAKEDEFS_C */
|
|
|
|
opttext[idxopttext] = dupstr(optbuf);
|
|
if (idxopttext < (MAXOPT - 1))
|
|
idxopttext++;
|
|
optbuf[0] = '\0';
|
|
|
|
#if defined(MAKEDEFS_C) || defined(FOR_RUNTIME)
|
|
{
|
|
static const char *const lua_info[] = {
|
|
"", "NetHack 3.7.* uses the 'Lua' interpreter to process some data:", "",
|
|
" :LUACOPYRIGHT:", "",
|
|
/* 1 2 3 4 5 6 7
|
|
1234567890123456789012345678901234567890123456789012345678901234567890123456
|
|
*/
|
|
" \"Permission is hereby granted, free of charge, to any person obtaining",
|
|
" a copy of this software and associated documentation files (the ",
|
|
" \"Software\"), to deal in the Software without restriction including",
|
|
" without limitation the rights to use, copy, modify, merge, publish,",
|
|
" distribute, sublicense, and/or sell copies of the Software, and to ",
|
|
" permit persons to whom the Software is furnished to do so, subject to",
|
|
" the following conditions:",
|
|
" The above copyright notice and this permission notice shall be",
|
|
" included in all copies or substantial portions of the Software.\"",
|
|
(const char *) 0
|
|
};
|
|
|
|
/* add lua copyright notice;
|
|
":TAG:" substitutions are deferred to caller */
|
|
for (i = 0; lua_info[i]; ++i) {
|
|
opttext[idxopttext] = dupstr(lua_info[i]);
|
|
if (idxopttext < (MAXOPT - 1))
|
|
idxopttext++;
|
|
}
|
|
}
|
|
#endif /* MAKEDEFS_C || FOR_RUNTIME */
|
|
|
|
/* end with a blank line */
|
|
opttext[idxopttext] = dupstr("");
|
|
if (idxopttext < (MAXOPT - 1))
|
|
idxopttext++;
|
|
return;
|
|
}
|
|
|
|
int
|
|
case_insensitive_comp(const char *s1, const char *s2)
|
|
{
|
|
uchar u1, u2;
|
|
|
|
for (;; s1++, s2++) {
|
|
u1 = (uchar) *s1;
|
|
if (isupper(u1))
|
|
u1 = (uchar) tolower(u1);
|
|
u2 = (uchar) *s2;
|
|
if (isupper(u2))
|
|
u2 = (uchar) tolower(u2);
|
|
if (u1 == '\0' || u1 != u2)
|
|
break;
|
|
}
|
|
return u1 - u2;
|
|
}
|
|
|
|
void
|
|
runtime_info_init(void)
|
|
{
|
|
if (!done_runtime_opt_init_once) {
|
|
done_runtime_opt_init_once = 1;
|
|
build_savebones_compat_string();
|
|
/* construct the current version number */
|
|
make_version();
|
|
populate_nomakedefs(&version); /* date.c */
|
|
idxopttext = 0;
|
|
build_options();
|
|
}
|
|
}
|
|
|
|
const char *
|
|
do_runtime_info(int *rtcontext)
|
|
{
|
|
const char *retval = (const char *) 0;
|
|
|
|
if (!done_runtime_opt_init_once)
|
|
runtime_info_init();
|
|
if (idxopttext && rtcontext)
|
|
if (*rtcontext >= 0 && *rtcontext < (MAXOPT - 1)) {
|
|
retval = opttext[*rtcontext];
|
|
*rtcontext += 1;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
void
|
|
release_runtime_info(void)
|
|
{
|
|
while (idxopttext > 0) {
|
|
--idxopttext;
|
|
free((genericptr_t) opttext[idxopttext]), opttext[idxopttext] = 0;
|
|
}
|
|
done_runtime_opt_init_once = 0;
|
|
free_nomakedefs();
|
|
}
|
|
|
|
/*mdlib.c*/
|