/* NetHack 3.7 mdlib.c $NHDT-Date: 1575161954 2019/12/01 00:59:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.6 $ */ /* 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" #ifdef MONITOR_HEAP #undef free /* makedefs, mdlib don't use the alloc and free in src/alloc.c */ #endif #include "permonst.h" #include "objclass.h" #include "monsym.h" #include "artilist.h" #include "dungeon.h" #include "obj.h" #include "monst.h" #include "you.h" #include "context.h" #include "flag.h" #include "dlb.h" #include /* 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 */ #if defined(MAKEDEFS_C) || defined(CROSSCOMPILE_TARGET) /* REPRODUCIBLE_BUILD will change this to TRUE */ static boolean date_via_env = FALSE; static char *FDECL(version_string, (char *, const char *)); static char *FDECL(version_id_string, (char *, const char *)); static char *FDECL(bannerc_string, (char *, const char *)); static int FDECL(case_insensitive_comp, (const char *, const char *)); static void NDECL(make_version); static char *FDECL(eos, (char *)); #endif /* MAKEDEFS_C || CROSSCOMPILE_TARGET */ void NDECL(build_options); static void FDECL(opt_out_words, (char *, int *)); static void NDECL(build_savebones_compat_string); static int idxopttext, done_runtime_opt_init_once = 0; #define MAXOPT 40 static char rttimebuf[MAXOPT]; static char *opttext[120] = { 0 }; char optbuf[BUFSZ]; static struct version_info version; static const char opt_indent[] = " "; struct win_info { const char *id, /* DEFAULT_WINDOW_SYS string */ *name; /* description, often same as id */ }; static struct win_info window_opts[] = { #ifdef TTY_GRAPHICS { "tty", /* testing 'USE_TILES' 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, or MACgraphics can be enabled; we can't tell from here whether that is accurate */ "traditional text with optional line-drawing" #endif }, #endif /*TTY_GRAPHICS */ #ifdef CURSES_GRAPHICS { "curses", "terminal-based graphics" }, #endif #ifdef X11_GRAPHICS { "X11", "X11" }, #endif #ifdef QT_GRAPHICS /* too vague; there are multiple incompatible versions */ { "Qt", "Qt" }, #endif #ifdef GNOME_GRAPHICS /* unmaintained/defunct */ { "Gnome", "Gnome" }, #endif #ifdef MAC /* defunct OS 9 interface */ { "mac", "Mac" }, #endif #ifdef AMIGA_INTUITION /* unmaintained/defunct */ { "amii", "Amiga Intuition" }, #endif #ifdef GEM_GRAPHICS /* defunct Atari interface */ { "Gem", "Gem" }, #endif #ifdef MSWIN_GRAPHICS /* win32 */ { "mswin", "mswin" }, #endif #ifdef BEOS_GRAPHICS /* unmaintained/defunct */ { "BeOS", "BeOS InterfaceKit" }, #endif { 0, 0 } }; /* * 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. * */ #ifndef MD_IGNORED_FEATURES #define MD_IGNORED_FEATURES \ (0L | (1L << 19) /* SCORE_ON_BOTL */ \ | (1L << 27) /* ZEROCOMP */ \ | (1L << 28) /* RLECOMP */ \ ) #endif /* MD_IGNORED_FEATUES */ static void make_version() { 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 */ #ifdef SYSFLAGS ((unsigned long) sizeof(struct sysflag))); #else ((unsigned long) 0L)); #endif return; } #if defined(MAKEDEFS_C) || defined(CROSSCOMPILE_TARGET) static char * version_string(outbuf, delim) 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; } static char * version_id_string(outbuf, build_date) char *outbuf; 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 Sprintf(outbuf, "%s NetHack%s Version %s%s - last %s %s.", PORT_ID, subbuf, version_string(versbuf, "."), statusbuf, date_via_env ? "revision" : "build", build_date); return outbuf; } /* still within #if MAKDEFS_C || CROSSCOMPILE_TARGET */ static char * bannerc_string(outbuf, build_date) char *outbuf; 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 Sprintf(outbuf, " Version %s %s%s, %s %s.", version_string(versbuf, "."), PORT_ID, subbuf, date_via_env ? "revised" : "built", &build_date[4]); #if 0 Sprintf(outbuf, "%s NetHack%s %s Copyright 1985-%s (built %s)", PORT_ID, subbuf, version_string(versbuf,"."), RELEASE_YEAR, &build_date[4]); #endif return outbuf; } #endif /* MAKEDEFS_C || CROSSCOMPILE_TARGET */ static int case_insensitive_comp(s1, s2) const char *s1; const char *s2; { uchar u1, u2; for (;; s1++, s2++) { u1 = (uchar) *s1; if (isupper(u1)) u1 = tolower(u1); u2 = (uchar) *s2; if (isupper(u2)) u2 = tolower(u2); if (u1 == '\0' || u1 != u2) break; } return u1 - u2; } static char * eos(str) char *str; { while (*str) str++; return str; } static char save_bones_compat_buf[BUFSZ]; static void build_savebones_compat_string() { #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 *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 COM_COMPL "command line completion", #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 DUMPLOG "end-of-game dumplogs", #endif #ifdef HOLD_LOCKFILE_OPEN "exclusive lock on level 0 file", #endif #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__)) "external program as a message handler", #endif #ifdef MFLOPPY "floppy drive support", #endif #ifdef INSURANCE "insurance files for recovering from crashes", #endif #ifdef LOGFILE "log file", #endif #ifdef XLOGFILE "extended log file", #endif #ifdef PANICLOG "errors and warnings log file", #endif #ifdef MAIL_STRUCTURES "mail daemon", #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 /* 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 #ifdef NHSTDC /* include which specific one */ "strong PRNG seed available from " DEV_RANDOM, #else "strong PRNG seed available from DEV_RANDOM", #endif #else #ifdef WIN32 "strong PRNG seed available 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 USER_SOUNDS "user sounds", #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 void opt_out_words(str, length_p) char *str; /* input, but modified during processing */ int *length_p; /* in/out */ { char *word; while (*str) { word = index(str, ' '); #if 0 /* treat " (" as unbreakable space */ if (word && *(word + 1) == '(') word = index(word + 1, ' '); #endif if (word) *word = '\0'; if (*length_p + (int) strlen(str) > COLNO - 5) { opttext[idxopttext] = strdup(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() { char buf[BUFSZ]; int i, length, winsyscnt; build_savebones_compat_string(); opttext[idxopttext] = strdup(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] = strdup(optbuf); if (idxopttext < (MAXOPT - 1)) idxopttext++; Sprintf(optbuf, "Options compiled into this edition:"); opttext[idxopttext] = strdup(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++) { opt_out_words(strcat(strcpy(buf, build_opts[i]), (i < SIZE(build_opts) - 1) ? "," : "."), &length); } opttext[idxopttext] = strdup(optbuf); if (idxopttext < (MAXOPT - 1)) idxopttext++; optbuf[0] = '\0'; winsyscnt = SIZE(window_opts) - 1; opttext[idxopttext] = strdup(optbuf); if (idxopttext < (MAXOPT - 1)) idxopttext++; Sprintf(optbuf, "Supported windowing system%s:", (winsyscnt > 1) ? "s" : ""); opttext[idxopttext] = strdup(optbuf); if (idxopttext < (MAXOPT - 1)) idxopttext++; optbuf[0] = '\0'; length = COLNO + 1; /* force 1st item onto new line */ for (i = 0; i < winsyscnt; i++) { 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 (note no period; comes from 'with default' below) * 3+: for, bar, and quux */ opt_out_words(strcat(buf, (winsyscnt == 1) ? "." /* no 'default' */ : (winsyscnt == 2 && i == 0) ? " and" : (i == winsyscnt - 2) ? ", and" : ","), &length); } if (winsyscnt > 1) { Sprintf(buf, "with a default of \"%s\".", DEFAULT_WINDOW_SYS); opt_out_words(buf, &length); } opttext[idxopttext] = strdup(optbuf); if (idxopttext < (MAXOPT - 1)) idxopttext++; optbuf[0] = '\0'; #if defined(MAKEDEFS_C) || (defined(CROSSCOMPILE) && defined(CROSSCOMPILE_TARGET)) { static const char *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] = strdup(lua_info[i]); if (idxopttext < (MAXOPT - 1)) idxopttext++; } } #endif /* MAKEDEFS_C || (CROSSCOMPILE && CROSSCOMPILE_TARGET) */ /* end with a blank line */ opttext[idxopttext] = strdup(""); if (idxopttext < (MAXOPT - 1)) idxopttext++; return; } #if defined(__DATE__) && defined(__TIME__) #define extract_field(t,s,n,z) \ do { \ for (i = 0; i < n; ++i) \ t[i] = s[i + z]; \ t[i] = '\0'; \ } while (0) #endif void runtime_info_init() { int i; char tmpbuf[BUFSZ], *strp; const char *mth[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; struct tm t = {0}; time_t timeresult; if (!done_runtime_opt_init_once) { done_runtime_opt_init_once = 1; build_savebones_compat_string(); /* construct the current version number */ make_version(); /* * In a cross-compiled environment, you can't execute * the target binaries during the build, so we can't * use makedefs to write the values of the build * date and time to a file for retrieval. Not for * information meaningful to the target execution * environment. * * How can we capture the build date/time of the target * binaries in such a situation? We need to rely on the * cross-compiler itself to do it for us during the * cross-compile. * * To that end, we are going to make use of the * following pre-defined preprocessor macros for this: * gcc, msvc, clang __DATE__ "Feb 12 1996" * gcc, msvc, clang __TIME__ "23:59:01" * */ #if defined(__DATE__) && defined(__TIME__) if (sizeof __DATE__ + sizeof __TIME__ + sizeof "123" < sizeof rttimebuf) Sprintf(rttimebuf, "%s %s", __DATE__, __TIME__); /* "Feb 12 1996 23:59:01" 01234567890123456789 */ if ((int) strlen(rttimebuf) == 20) { extract_field(tmpbuf, rttimebuf, 4, 7); /* year */ t.tm_year = atoi(tmpbuf) - 1900; extract_field(tmpbuf, rttimebuf, 3, 0); /* mon */ for (i = 0; i < SIZE(mth); ++i) if (!case_insensitive_comp(tmpbuf, mth[i])) { t.tm_mon = i; break; } extract_field(tmpbuf, rttimebuf, 2, 4); /* mday */ strp = tmpbuf; if (*strp == ' ') strp++; t.tm_mday = atoi(strp); extract_field(tmpbuf, rttimebuf, 2, 12); /* hour */ t.tm_hour = atoi(tmpbuf); extract_field(tmpbuf, rttimebuf, 2, 15); /* min */ t.tm_min = atoi(tmpbuf); extract_field(tmpbuf, rttimebuf, 2, 18); /* sec */ t.tm_sec = atoi(tmpbuf); timeresult = mktime(&t); #if defined(CROSSCOMPILE_TARGET) && !defined(MAKEDEFS_C) BUILD_TIME = (unsigned long) timeresult; BUILD_DATE = rttimebuf; #endif #else /* __DATE__ && __TIME__ */ nhUse(strp); #endif /* __DATE__ && __TIME__ */ #if defined(CROSSCOMPILE_TARGET) && !defined(MAKEDEFS_C) VERSION_NUMBER = version.incarnation; VERSION_FEATURES = version.feature_set; #ifdef MD_IGNORED_FEATURES IGNORED_FEATURES = MD_IGNORED_FEATURES; #endif VERSION_SANITY1 = version.entity_count; VERSION_SANITY2 = version.struct_sizes1; VERSION_SANITY3 = version.struct_sizes2; VERSION_STRING = strdup(version_string(tmpbuf, ".")); VERSION_ID = strdup(version_id_string(tmpbuf, BUILD_DATE)); COPYRIGHT_BANNER_C = strdup(bannerc_string(tmpbuf, BUILD_DATE)); #ifdef NETHACK_HOST_GIT_SHA NETHACK_GIT_SHA = strdup(NETHACK_HOST_GIT_SHA); #endif #ifdef NETHACK_HOST_GIT_BRANCH NETHACK_GIT_BRANCH = strdup(NETHACK_HOST_GIT_BRANCH); #endif #endif /* CROSSCOMPILE_TARGET && !MAKEDEFS_C */ } idxopttext = 0; build_options(); } } const char * do_runtime_info(rtcontext) 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; } /*mdlib.c*/