/* NetHack 3.6 makedefs.c $NHDT-Date: 1582403492 2020/02/22 20:31:32 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.177 $ */ /* 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. */ #define MAKEDEFS_C /* use to conditionally include file sections */ #include "config.h" #ifdef MONITOR_HEAP #undef free /* makedefs doesn'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" /* version information */ #ifdef SHORT_FILENAMES #include "patchlev.h" #else #include "patchlevel.h" #endif #include #ifdef MAC #if defined(__SC__) || defined(__MRC__) /* MPW compilers */ #define MPWTOOL #include #include #else /* MAC without MPWTOOL */ #define MACsansMPWTOOL #endif #endif /* MAC */ #ifndef MPWTOOL #define SpinCursor(x) #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 #if defined(UNIX) && !defined(LINT) && !defined(GCC_WARN) static const char SCCS_Id[] UNUSED = "@(#)makedefs.c\t3.7\t2020/01/18"; #endif /* names of files to be generated */ #define DATE_FILE "date.h" #define MONST_FILE "pm.h" #define ONAME_FILE "onames.h" #ifndef OPTIONS_FILE #define OPTIONS_FILE "options" #endif #define ORACLE_FILE "oracles" #define DATA_FILE "data" #define RUMOR_FILE "rumors" #define DGN_I_FILE "dungeon.def" #define DGN_O_FILE "dungeon.pdf" #define MON_STR_C "monstr.c" #if 0 #define QTXT_I_FILE "quest.txt" #define QTXT_O_FILE "quest.dat" #endif #define VIS_TAB_H "vis_tab.h" #define VIS_TAB_C "vis_tab.c" #define GITINFO_FILE "gitinfo.txt" /* locations for those files */ #ifdef AMIGA #define FILE_PREFIX #define INCLUDE_TEMPLATE "NH:include/t.%s" #define SOURCE_TEMPLATE "NH:src/%s" #define DGN_TEMPLATE "NH:dat/%s" /* where dungeon.pdf file goes */ #define DATA_TEMPLATE "NH:slib/%s" #define DATA_IN_TEMPLATE "NH:dat/%s" #else /* not AMIGA */ #if defined(MAC) && !defined(__MACH__) /* MacOS 9 or earlier */ #define INCLUDE_TEMPLATE ":include:%s" #define SOURCE_TEMPLATE ":src:%s" #define DGN_TEMPLATE ":dat:%s" /* where dungeon.pdf file goes */ #if __SC__ || __MRC__ #define DATA_TEMPLATE ":Dungeon:%s" #else #define DATA_TEMPLATE ":lib:%s" #endif /* __SC__ || __MRC__ */ #define DATA_IN_TEMPLATE ":dat:%s" #else /* neither AMIGA nor MAC */ #ifdef OS2 #define INCLUDE_TEMPLATE "..\\include\\%s" #define SOURCE_TEMPLATE "..\\src\\%s" #define DGN_TEMPLATE "..\\dat\\%s" /* where dungeon.pdf file goes */ #define DATA_TEMPLATE "..\\dat\\%s" #define DATA_IN_TEMPLATE "..\\dat\\%s" #else /* not AMIGA, MAC, or OS2 */ #define INCLUDE_TEMPLATE "../include/%s" #define SOURCE_TEMPLATE "../src/%s" #define DGN_TEMPLATE "../dat/%s" /* where dungeon.pdf file goes */ #define DATA_TEMPLATE "../dat/%s" #define DATA_IN_TEMPLATE "../dat/%s" #endif /* else !OS2 */ #endif /* else !MAC */ #endif /* else !AMIGA */ static const char *Dont_Edit_Code = "/* This source file is generated by 'makedefs'. Do not edit. */\n", *Dont_Edit_Data = "#\tThis data file is generated by 'makedefs'. Do not edit. \n"; static struct version_info version; /* definitions used for vision tables */ #define TEST_WIDTH COLNO #define TEST_HEIGHT ROWNO #define BLOCK_WIDTH (TEST_WIDTH + 10) #define BLOCK_HEIGHT TEST_HEIGHT /* don't need extra spaces */ #define MAX_ROW (BLOCK_HEIGHT + TEST_HEIGHT) #define MAX_COL (BLOCK_WIDTH + TEST_WIDTH) /* Use this as an out-of-bound value in the close table. */ #define CLOSE_OFF_TABLE_STRING "99" /* for the close table */ #define FAR_OFF_TABLE_STRING "0xff" /* for the far table */ #define sign(z) ((z) < 0 ? -1 : ((z) ? 1 : 0)) #ifdef VISION_TABLES static char xclear[MAX_ROW][MAX_COL]; #endif /*-end of vision defs-*/ #define MAXFNAMELEN 600 static char filename[MAXFNAMELEN]; #ifdef FILE_PREFIX /* if defined, a first argument not starting with - is * taken as a text string to be prepended to any * output filename generated */ char *file_prefix = ""; #endif #ifdef MACsansMPWTOOL int FDECL(main, (void)); #else int FDECL(main, (int, char **)); #endif void FDECL(do_makedefs, (char *)); void NDECL(do_objs); void NDECL(do_data); void NDECL(do_dungeon); void NDECL(do_options); void NDECL(do_monstr); void NDECL(do_permonst); void NDECL(do_questtxt); void NDECL(do_rumors); void NDECL(do_oracles); void NDECL(do_vision); void NDECL(do_date); extern void NDECL(monst_globals_init); /* monst.c */ extern void NDECL(objects_globals_init); /* objects.c */ static char *FDECL(name_file, (const char *, const char *)); static void FDECL(delete_file, (const char *template, const char *)); static FILE *FDECL(getfp, (const char *, const char *, const char *)); static void FDECL(do_ext_makedefs, (int, char **)); static char *FDECL(xcrypt, (const char *)); static unsigned long FDECL(read_rumors_file, (const char *, int *, long *, unsigned long)); static void FDECL(do_rnd_access_file, (const char *, const char *)); static boolean FDECL(d_filter, (char *)); static boolean FDECL(h_filter, (char *)); static void FDECL(opt_out_words, (char *, int *)); #ifdef VISION_TABLES static void NDECL(H_close_gen); static void NDECL(H_far_gen); static void NDECL(C_close_gen); static void NDECL(C_far_gen); static int FDECL(clear_path, (int, int, int, int)); #endif static char *FDECL(fgetline, (FILE*)); static char *FDECL(tmpdup, (const char *)); static char *FDECL(limit, (char *, int)); static void NDECL(windowing_sanity); static boolean FDECL(get_gitinfo, (char *, char *)); /* input, output, tmp */ static FILE *ifp, *ofp, *tfp; static boolean use_enum = #ifdef ENUM_PM TRUE; #else FALSE; #endif #if defined(__BORLANDC__) && !defined(_WIN32) extern unsigned _stklen = STKSIZ; #endif /* * Some of the routines in this source file were moved into .../src/mdlib * to facilitate the use of a cross-compiler generation of some of the * information for the target environment during the game compile portion * under the cross-compiler and/or at runtime in some cases. */ #include "../src/mdlib.c" #ifdef MACsansMPWTOOL int main(void) { const char *def_options = "odemvpqrshz"; char buf[100]; int len; printf("Enter options to run: [%s] ", def_options); fflush(stdout); fgets(buf, 100, stdin); len = strlen(buf); if (len <= 1) Strcpy(buf, def_options); else buf[len - 1] = 0; /* remove return */ if (buf[0] == '-' && buf[1] == '-') { #if 0 split up buf into words do_ext_makedefs(fakeargc, fakeargv); #else printf("extended makedefs not implemented for Mac OS9\n"); exit(EXIT_FAILURE); #endif } do_makedefs(buf); exit(EXIT_SUCCESS); return 0; } #else /* ! MAC */ int main(argc, argv) int argc; char *argv[]; { if ((argc == 1) || ((argc != 2) #ifdef FILE_PREFIX && (argc != 3) #endif && !(argv[1][0] == '-' && argv[1][1] == '-'))) { Fprintf(stderr, "Bad arg count (%d).\n", argc - 1); (void) fflush(stderr); return 1; } #ifdef FILE_PREFIX if (argc >= 2 && argv[1][0] != '-') { file_prefix = argv[1]; argc--; argv++; } #endif if (argv[1][0] == '-' && argv[1][1] == '-') { do_ext_makedefs(argc, argv); } else { do_makedefs(&argv[1][1]); } exit(EXIT_SUCCESS); /*NOTREACHED*/ return 0; } #endif void do_makedefs(options) char *options; { boolean more_than_one; objects_globals_init(); monst_globals_init(); /* construct the current version number */ make_version(); more_than_one = strlen(options) > 1; while (*options) { if (more_than_one) Fprintf(stderr, "makedefs -%c\n", *options); switch (*options) { case 'o': case 'O': do_objs(); break; case 'd': case 'D': do_data(); break; case 'e': case 'E': do_dungeon(); break; case 'm': case 'M': do_monstr(); break; case 'v': case 'V': do_date(); do_options(); break; case 'p': case 'P': do_permonst(); break; case 'q': case 'Q': do_questtxt(); break; case 'r': case 'R': do_rumors(); break; case 's': case 'S': /* * post-3.6.5: * File must not be empty to avoid divide by 0 * in core's rn2(), so provide a default entry. */ do_rnd_access_file(EPITAPHFILE, /* default epitaph: parody of the default engraving */ "No matter where I went, here I am."); do_rnd_access_file(ENGRAVEFILE, /* default engraving: popularized by "The Adventures of Buckaroo Bonzai Across the 8th Dimenstion" but predates that 1984 movie; some attribute it to Confucius */ "No matter where you go, there you are."); do_rnd_access_file(BOGUSMONFILE, /* default bogusmon: iconic monster that isn't in nethack */ "grue"); break; case 'h': case 'H': do_oracles(); break; case 'z': case 'Z': do_vision(); break; default: Fprintf(stderr, "Unknown option '%c'.\n", *options); (void) fflush(stderr); exit(EXIT_FAILURE); } options++; } if (more_than_one) Fprintf(stderr, "Completed.\n"); /* feedback */ } static char namebuf[1000]; static char * name_file(template, tag) const char *template; const char *tag; { Sprintf(namebuf, template, tag); return namebuf; } static void delete_file(template, tag) const char *template; const char *tag; { char *name = name_file(template, tag); Unlink(name); } static FILE * getfp(template, tag, mode) const char *template; const char *tag; const char *mode; { char *name = name_file(template, tag); FILE *rv = fopen(name, mode); if (!rv) { Fprintf(stderr, "Can't open '%s'.\n", name); exit(EXIT_FAILURE); } return rv; } static boolean debug = FALSE; static FILE *inputfp; static FILE *outputfp; struct grep_var { const char *name; int is_defined; /* 0 undef; 1 defined */ }; /* struct grep_var grep_vars[] and TODO_* constants in include file: */ #include "mdgrep.h" static void NDECL(do_grep_showvars); static struct grep_var *FDECL(grepsearch, (const char *)); static int FDECL(grep_check_id, (const char *)); static void FDECL(grep_show_wstack, (const char *)); static char *FDECL(do_grep_control, (char *)); static void NDECL(do_grep); static void FDECL(grep0, (FILE *, FILE *)); static int grep_trace = 0; #define IS_OPTION(str) if (!strcmp(&argv[0][2], str)) #define CONTINUE \ argv++, argc--; \ continue #define CONSUME \ argv++, argc--; \ if (argc == 0) { \ Fprintf(stderr, "missing option\n"); \ exit(EXIT_FAILURE); \ } static void do_ext_makedefs(int argc, char **argv) { int todo = 0; argc--; argv++; /* skip program name */ while (argc) { if (argv[0][0] != '-') break; if (argv[0][1] != '-') { Fprintf(stderr, "Can't mix - and -- options.\n"); exit(EXIT_FAILURE); } IS_OPTION("svs") { /* short version string for packaging - note no \n */ char buf[100]; char delim[10]; argv++; /* not CONSUME */ delim[0] = '\0'; if (argv[0]) strcpy(delim, argv[0]); Fprintf(stdout, "%s", version_string(buf, delim)); exit(EXIT_SUCCESS); } IS_OPTION("debug") { debug = TRUE; CONTINUE; } IS_OPTION("make") { CONSUME; do_makedefs(argv[0]); exit(EXIT_SUCCESS); } IS_OPTION("input") { CONSUME; if (!strcmp(argv[0], "-")) { inputfp = stdin; } else { inputfp = fopen(argv[0], RDTMODE); if (!inputfp) { Fprintf(stderr, "Can't open '%s'.\n", argv[0]); exit(EXIT_FAILURE); } } CONTINUE; } IS_OPTION("output") { CONSUME; if (!strcmp(argv[0], "-")) { outputfp = stdout; } else { outputfp = fopen(argv[0], WRTMODE); if (!outputfp) { Fprintf(stderr, "Can't open '%s'.\n", argv[0]); exit(EXIT_FAILURE); } } CONTINUE; } IS_OPTION("grep") { if (todo) { Fprintf(stderr, "Can't do grep and something else.\n"); exit(EXIT_FAILURE); } todo = TODO_GREP; CONTINUE; } IS_OPTION("grep-showvars") { do_grep_showvars(); exit(EXIT_SUCCESS); } IS_OPTION("grep-trace") { grep_trace = 1; CONTINUE; } IS_OPTION("grep-define") { struct grep_var *p; CONSUME; p = grepsearch(argv[0]); if (p) { p->is_defined = 1; } else { Fprintf(stderr, "Unknown symbol '%s'\n", argv[0]); exit(EXIT_FAILURE); } CONTINUE; } IS_OPTION("grep-undef") { struct grep_var *p; CONSUME; p = grepsearch(argv[0]); if (p) { p->is_defined = 0; } else { Fprintf(stderr, "Unknown symbol '%s'\n", argv[0]); exit(EXIT_FAILURE); } CONTINUE; } #ifdef notyet IS_OPTION("help") { } #endif Fprintf(stderr, "Unknown option '%s'.\n", argv[0]); exit(EXIT_FAILURE); } if (argc) { Fprintf(stderr, "unexpected argument '%s'.\n", argv[0]); exit(EXIT_FAILURE); } switch (todo) { default: Fprintf(stderr, "Confused about what to do?\n"); exit(EXIT_FAILURE); case 0: Fprintf(stderr, "Nothing to do?\n"); exit(EXIT_FAILURE); case TODO_GREP: do_grep(); break; } } #undef IS_OPTION #undef CONTINUE #undef CONSUME /* * Filtering syntax: * Any line NOT starting with a caret is either suppressed or passed * through unchanged depending on the current conditional state. * * The default conditional state is printing on. * * Conditionals may be nested. * * makedefs will exit with a EXIT_FAILURE if any errors are detected; * as many errors as possible are detected before giving up. * * Unknown identifiers are treated as TRUE and also as an error to * allow processing to continue past the unknown identifier (note * that "#undef" is different than unknown). * * Any line starting with a caret is a control line; as in C, zero or * more spaces may be embedded in the line almost anywhere; the caret * MUST be in column 1. * (XXX for the moment, no white space is allowed after the caret because * existing lines in the docs look like that.) * * Control lines: * ^^ a line starting with a (single) literal caret * ^# a comment - the line is ignored * ^?ID if defined(ID) * ^!ID if !defined(ID) * ^: else * ^. endif */ #define GREP_MAGIC '^' #define GREP_STACK_SIZE 100 #ifdef notyet static int grep_rewrite = 0; /* need to (possibly) rewrite lines */ #endif static int grep_writing = 1; /* need to copy lines to output */ static int grep_errors = 0; static int grep_sp = 0; #define ST_LD(old, opp) (((old) ? 1 : 0) | ((opp) ? 2 : 0)) #define ST_OLD(v) (((v) & 1) != 0) #define ST_OPP(v) (((v) & 2) != 0) #define ST_ELSE 4 static int grep_stack[GREP_STACK_SIZE] = { ST_LD(1, 0) }; static int grep_lineno = 0; static void do_grep_showvars() { int x; for (x = 0; x < SIZE(grep_vars) - 1; x++) { printf("%d\t%s\n", grep_vars[x].is_defined, grep_vars[x].name); } } static struct grep_var * grepsearch(name) const char *name; { /* XXX make into binary search */ int x = 0; while (x < SIZE(grep_vars) - 1) { if (!strcmp(grep_vars[x].name, name)) return &grep_vars[x]; x++; } return 0; } static int grep_check_id(id) const char *id; { struct grep_var *rv; while (*id && isspace((uchar) *id)) id++; if (!*id) { Fprintf(stderr, "missing identifier in line %d", grep_lineno); grep_errors++; return 0; } rv = grepsearch(id); if (rv) { if (grep_trace) { Fprintf(outputfp, "ID %d %s\n", rv->is_defined, id); } return rv->is_defined; } if (grep_trace) { Fprintf(outputfp, "ID U %s\n", id); } Fprintf(stderr, "unknown identifier '%s' in line %d.\n", id, grep_lineno); grep_errors++; return 2; /* So new features can be checked before makedefs * is rebuilt. */ } static void grep_show_wstack(tag) const char *tag; { int x; if (!grep_trace) return; Fprintf(outputfp, "%s w=%d sp=%d\t", tag, grep_writing, grep_sp); for (x = grep_sp; x >= 0 && x > grep_sp - 6; x--) { Fprintf(outputfp, "[%d]=%d ", x, grep_stack[x]); } Fprintf(outputfp, "\n"); } static char * do_grep_control(buf) char *buf; { int isif = 1; char *buf0 = buf; #if 1 if (isspace((uchar) buf[0])) return &buf[-1]; /* XXX see docs above */ #else while (buf[0] && isspace((uchar) buf[0])) buf++; #endif switch (buf[0]) { case '#': /* comment */ break; case '.': /* end of if level */ if (grep_sp == 0) { Fprintf(stderr, "unmatched ^. (endif) at line %d.\n", grep_lineno); grep_errors++; } else { grep_writing = ST_OLD(grep_stack[grep_sp--]); grep_show_wstack("pop"); } break; case '!': /* if not ID */ isif = 0; /* FALLTHROUGH */ case '?': /* if ID */ if (grep_sp == GREP_STACK_SIZE - 2) { Fprintf(stderr, "stack overflow at line %d.", grep_lineno); exit(EXIT_FAILURE); } if (grep_writing) { isif = grep_check_id(&buf[1]) ? isif : !isif; grep_stack[++grep_sp] = ST_LD(grep_writing, !isif); grep_writing = isif; } else { grep_stack[++grep_sp] = ST_LD(0, 0); /* grep_writing = 0; */ } grep_show_wstack("push"); break; case ':': /* else */ if (ST_ELSE & grep_stack[grep_sp]) { Fprintf(stderr, "multiple : for same conditional at line %d.\n", grep_lineno); grep_errors++; } grep_writing = ST_OPP(grep_stack[grep_sp]); grep_stack[grep_sp] |= ST_ELSE; break; #if defined(notyet) case '(': /* start of expression */ #endif case GREP_MAGIC: /* ^^ -> ^ */ return buf0; default: { char str[10]; if (isprint((uchar) buf[0])) { str[0] = buf[0]; str[1] = '\0'; } else { sprintf(str, "0x%02x", buf[0]); } Fprintf(stderr, "unknown control ^%s at line %d.\n", str, grep_lineno); grep_errors++; } break; } return NULL; } #ifdef notyet static void do_grep_rewrite(buf) char *buf; { /* no language features use this yet */ return; } #endif static void grep0(FILE *, FILE *); static void do_grep() { if (!inputfp) { Fprintf(stderr, "--grep requires --input\n"); } if (!outputfp) { Fprintf(stderr, "--grep requires --output\n"); } if (!inputfp || !outputfp) { exit(EXIT_FAILURE); } grep0(inputfp, outputfp); } static void grep0(inputfp0, outputfp0) FILE *inputfp0; FILE *outputfp0; { char buf[16384]; /* looong, just in case */ while (!feof(inputfp0) && !ferror(inputfp0)) { char *tmp; char *buf1; if (fgets(buf, sizeof(buf), inputfp0) == 0) break; if ((tmp = strchr(buf, '\n'))) *tmp = '\0'; grep_lineno++; if (grep_trace) { Fprintf(outputfp0, "%04d %c >%s\n", grep_lineno, grep_writing ? ' ' : '#', buf); } if (buf[0] == GREP_MAGIC) { buf1 = do_grep_control(&buf[1]); if (!buf1) continue; } else { buf1 = buf; } #ifdef notyet if (grep_rewrite) do_grep_rewrite(buf1); #endif if (grep_writing) Fprintf(outputfp0, "%s\n", buf1); } if (ferror(inputfp0)) { Fprintf(stderr, "read error!\n"); exit(EXIT_FAILURE); } if (ferror(outputfp0)) { Fprintf(stderr, "write error!\n"); exit(EXIT_FAILURE); } fclose(inputfp0); fclose(outputfp0); if (grep_sp) { Fprintf(stderr, "%d unterminated conditional level%s\n", grep_sp, grep_sp == 1 ? "" : "s"); grep_errors++; } if (grep_errors) { Fprintf(stderr, "%d error%s detected.\n", grep_errors, grep_errors == 1 ? "" : "s"); exit(EXIT_FAILURE); } } /* trivial text encryption routine which can't be broken with `tr' */ static char * xcrypt(str) const char *str; { /* duplicated in src/hacklib.c */ static char buf[BUFSZ]; register const char *p; register char *q; register int bitmask; for (bitmask = 1, p = str, q = buf; *p; q++) { *q = *p++; if (*q & (32 | 64)) *q ^= bitmask; if ((bitmask <<= 1) >= 32) bitmask = 1; } *q = '\0'; return buf; } #define PAD_RUMORS_TO 60 /* common code for do_rumors(). Return 0 on error. */ static unsigned long read_rumors_file(file_ext, rumor_count, rumor_size, old_rumor_offset) const char *file_ext; int *rumor_count; long *rumor_size; unsigned long old_rumor_offset; { char infile[MAXFNAMELEN]; char *line; unsigned long rumor_offset; Sprintf(infile, DATA_IN_TEMPLATE, RUMOR_FILE); Strcat(infile, file_ext); if (!(ifp = fopen(infile, RDTMODE))) { perror(infile); return 0L; } /* copy the rumors */ while ((line = fgetline(ifp)) != 0) { #ifdef PAD_RUMORS_TO /* rumor selection is accomplished by seeking to a random position in the file, advancing to newline, and taking the next line; therefore, rumors which follow long-line rumors are most likely to be chosen and rumors which follow short-line rumors are least likely to be chosen; we ameliorate the latter by padding the shortest lines, increasing the chance of the random seek landing in them */ int len = (int) strlen(line); if (len <= PAD_RUMORS_TO) { char *base = index(line, '\n'); /* this is only safe because fgetline() overallocates */ while (len++ < PAD_RUMORS_TO) { *base++ = '_'; } *base++ = '\n'; *base = '\0'; } #endif (*rumor_count)++; #if 0 /*[if we forced binary output, this would be sufficient]*/ *rumor_size += strlen(line); /* includes newline */ #endif (void) fputs(xcrypt(line), tfp); free(line); } /* record the current position; next rumors section will start here */ rumor_offset = (unsigned long) ftell(tfp); Fclose(ifp); /* all done with rumors.file_ext */ /* the calculated value for *_rumor_count assumes that a single-byte line terminator is in use; for platforms which use two byte CR+LF, we need to override that value [it's much simpler to do so unconditionally, rendering the loop's accumulation above obsolete] */ *rumor_size = (long) (rumor_offset - old_rumor_offset); return rumor_offset; } static void do_rnd_access_file(fname, deflt_content) const char *fname; const char *deflt_content; { char *line; Sprintf(filename, DATA_IN_TEMPLATE, fname); Strcat(filename, ".txt"); if (!(ifp = fopen(filename, RDTMODE))) { perror(filename); exit(EXIT_FAILURE); } filename[0] = '\0'; #ifdef FILE_PREFIX Strcat(filename, file_prefix); #endif Sprintf(eos(filename), DATA_TEMPLATE, fname); if (!(ofp = fopen(filename, WRTMODE))) { perror(filename); exit(EXIT_FAILURE); } Fprintf(ofp, "%s", Dont_Edit_Data); /* write out the default content entry unconditionally instead of waiting to see whether there are no regular output lines; if it matches a regular entry (bogusmon "grue"), that entry will become more likely to be picked than normal but it's nothing to worry about */ (void) fputs(xcrypt(deflt_content), ofp); tfp = getfp(DATA_TEMPLATE, "grep.tmp", WRTMODE); grep0(ifp, tfp); ifp = getfp(DATA_TEMPLATE, "grep.tmp", RDTMODE); while ((line = fgetline(ifp)) != 0) { if (line[0] != '#' && line[0] != '\n') (void) fputs(xcrypt(line), ofp); free((genericptr_t) line); } Fclose(ifp); Fclose(ofp); delete_file(DATA_TEMPLATE, "grep.tmp"); return; } void do_rumors() { char *line; static const char rumors_header[] = "%s%04d,%06ld,%06lx;%04d,%06ld,%06lx;0,0,%06lx\n"; char tempfile[MAXFNAMELEN]; int true_rumor_count, false_rumor_count; long true_rumor_size, false_rumor_size; unsigned long true_rumor_offset, false_rumor_offset, eof_offset; Sprintf(tempfile, DATA_TEMPLATE, "rumors.tmp"); filename[0] = '\0'; #ifdef FILE_PREFIX Strcat(filename, file_prefix); #endif Sprintf(eos(filename), DATA_TEMPLATE, RUMOR_FILE); if (!(ofp = fopen(filename, WRTMODE))) { perror(filename); exit(EXIT_FAILURE); } if (!(tfp = fopen(tempfile, WRTMODE))) { perror(tempfile); Fclose(ofp); exit(EXIT_FAILURE); } true_rumor_count = false_rumor_count = 0; true_rumor_size = false_rumor_size = 0L; true_rumor_offset = false_rumor_offset = eof_offset = 0L; /* output a dummy header record; we'll replace it in final output */ Fprintf(tfp, rumors_header, Dont_Edit_Data, true_rumor_count, true_rumor_size, true_rumor_offset, false_rumor_count, false_rumor_size, false_rumor_offset, eof_offset); /* record the current position; true rumors will start here */ true_rumor_offset = ftell(tfp); false_rumor_offset = read_rumors_file( ".tru", &true_rumor_count, &true_rumor_size, true_rumor_offset); if (!false_rumor_offset) goto rumors_failure; eof_offset = read_rumors_file(".fal", &false_rumor_count, &false_rumor_size, false_rumor_offset); if (!eof_offset) goto rumors_failure; /* get ready to transfer the contents of temp file to output file */ line = malloc(BUFSZ + MAXFNAMELEN); Sprintf(line, "rewind of \"%s\"", tempfile); if (rewind(tfp) != 0) { perror(line); free(line); goto rumors_failure; } free(line); /* output the header record */ Fprintf(ofp, rumors_header, Dont_Edit_Data, true_rumor_count, true_rumor_size, true_rumor_offset, false_rumor_count, false_rumor_size, false_rumor_offset, eof_offset); /* skip the temp file's dummy header */ if (!(line = fgetline(tfp))) { /* "Don't Edit" */ perror(tempfile); goto rumors_failure; } free(line); if (!(line = fgetline(tfp))) { /* count,size,offset */ perror(tempfile); goto rumors_failure; } free(line); /* copy the rest of the temp file into the final output file */ while ((line = fgetline(tfp)) != 0) { (void) fputs(line, ofp); free(line); } /* all done; delete temp file */ Fclose(tfp); Unlink(tempfile); Fclose(ofp); return; rumors_failure: Fclose(ofp); Unlink(filename); /* kill empty or incomplete output file */ Fclose(tfp); Unlink(tempfile); /* and temporary file */ exit(EXIT_FAILURE); } void do_date() { #ifdef KR1ED long clocktim = 0; #else time_t clocktim = 0; #endif char githash[BUFSZ], gitbranch[BUFSZ]; char *c, cbuf[60], buf[BUFSZ]; const char *ul_sfx; #if defined(CROSSCOMPILE) && defined(CROSSCOMPILE_HOST) int steps = 0; const char ind[] = " "; const char *xpref = "HOST_"; #else const char *xpref = (const char *) 0; #endif /* CROSSCOMPILE && CROSSCOMPILE_HOST */ /* before creating date.h, make sure that xxx_GRAPHICS and DEFAULT_WINDOW_SYS have been set up in a viable fashion */ windowing_sanity(); filename[0] = '\0'; #ifdef FILE_PREFIX Strcat(filename, file_prefix); #endif Sprintf(eos(filename), INCLUDE_TEMPLATE, DATE_FILE); if (!(ofp = fopen(filename, WRTMODE))) { perror(filename); exit(EXIT_FAILURE); } /* NB: We've moved on from SCCS, but this way this line * won't get clobbered when downstream projects import * this file into something more modern. */ Fprintf(ofp, "%s", Dont_Edit_Code); (void) time(&clocktim); #ifdef REPRODUCIBLE_BUILD { /* * Use date+time of latest source file revision (set up in * our environment rather than derived by scanning sources) * instead of current date+time, so that later rebuilds of * the same sources specifying the same configuration will * produce the same result. * * Changing the configuration should be done by modifying * config.h or conf.h and setting SOURCE_DATE_EPOCH * based on whichever changed most recently, not by using * make CFLAGS='-Dthis -Dthat' * to make alterations on the fly. * * Limited validation is performed to prevent dates in the * future (beyond a leeway of 24 hours) or distant past. * * Assumes the value of time_t is in seconds, which is * fundamental for Unix and mandated by POSIX. For any ports * where that isn't true, leaving REPRODUCIBLE_BUILD disabled * is probably preferrable to hacking this code.... */ static struct tm nh360; /* static init should yield UTC timezone */ unsigned long sd_num, sd_earliest, sd_latest; const char *sd_str = getenv("SOURCE_DATE_EPOCH"); if (sd_str) { sd_num = strtoul(sd_str, (char **) 0, 10); /* * Note: this does not need to be updated for future * releases. It serves as a sanity check for potentially * mis-set environment, not a hard baseline for when the * current version could have first been built. */ /* oldest date we'll accept: 7-Dec-2015 (release of 3.6.0) */ nh360.tm_mday = 7; nh360.tm_mon = 12 - 1; nh360.tm_year = 2015 - 1900; sd_earliest = (unsigned long) mktime(&nh360); /* 'youngest' date we'll accept: 24 hours in the future */ sd_latest = (unsigned long) clocktim + 24L * 60L * 60L; if (sd_num >= sd_earliest && sd_num <= sd_latest) { /* use SOURCE_DATE_EPOCH value */ clocktim = (time_t) sd_num; date_via_env = TRUE; } else { Fprintf(stderr, "? Invalid value for SOURCE_DATE_EPOCH (%lu)", sd_num); if (sd_num > 0L && sd_num < sd_earliest) Fprintf(stderr, ", older than %lu", sd_earliest); else if (sd_num > sd_latest) Fprintf(stderr, ", newer than %lu", sd_latest); Fprintf(stderr, ".\n"); Fprintf(stderr, ": Reverting to current date+time (%lu).\n", (unsigned long) clocktim); (void) fflush(stderr); } } else { /* REPRODUCIBLE_BUILD enabled but SOURCE_DATE_EPOCH is missing */ Fprintf(stderr, "? No value for SOURCE_DATE_EPOCH.\n"); Fprintf(stderr, ": Using current date+time (%lu).\n", (unsigned long) clocktim); (void) fflush(stderr); } Strcpy(cbuf, asctime(gmtime(&clocktim))); } #else /* ordinary build: use current date+time */ Strcpy(cbuf, ctime(&clocktim)); #endif /* REPRODUCIBLE_BUILD */ if ((c = index(cbuf, '\n')) != 0) *c = '\0'; /* strip off the '\n' */ #ifdef NHSTDC ul_sfx = "UL"; #else ul_sfx = "L"; #endif #if !defined(CROSSCOMPILE) || defined(CROSSCOMPILE_HOST) Fprintf(ofp, "\n#if !defined(CROSSCOMPILE) || defined(CROSSCOMPILE_HOST)\n"); #endif /* CROSSCOMPILE || CROSSCOMPILE_HOST */ if (date_via_env) Fprintf(ofp, "#define SOURCE_DATE_EPOCH (%lu%s) /* via getenv() */\n", (unsigned long) clocktim, ul_sfx); Fprintf(ofp, "#define BUILD_DATE \"%s\"\n", cbuf); if (date_via_env) Fprintf(ofp, "#define BUILD_TIME SOURCE_DATE_EPOCH\n"); else Fprintf(ofp, "#define BUILD_TIME (%lu%s)\n", (unsigned long) clocktim, ul_sfx); Fprintf(ofp, "\n"); Fprintf(ofp, "#define VERSION_NUMBER 0x%08lx%s\n", version.incarnation, ul_sfx); Fprintf(ofp, "#define VERSION_FEATURES 0x%08lx%s\n", version.feature_set, ul_sfx); #ifdef MD_IGNORED_FEATURES Fprintf(ofp, "#define IGNORED_FEATURES 0x%08lx%s\n", (unsigned long) MD_IGNORED_FEATURES, ul_sfx); #endif Fprintf(ofp, "#define VERSION_SANITY1 0x%08lx%s\n", version.entity_count, ul_sfx); Fprintf(ofp, "#define VERSION_SANITY2 0x%08lx%s\n", version.struct_sizes1, ul_sfx); Fprintf(ofp, "#define VERSION_SANITY3 0x%08lx%s\n", version.struct_sizes2, ul_sfx); Fprintf(ofp, "\n"); Fprintf(ofp, "#define VERSION_STRING \"%s\"\n", version_string(buf, ".")); Fprintf(ofp, "#define VERSION_ID \\\n \"%s\"\n", version_id_string(buf, cbuf)); Fprintf(ofp, "#define COPYRIGHT_BANNER_C \\\n \"%s\"\n", bannerc_string(buf, cbuf)); if (get_gitinfo(githash, gitbranch)) { Fprintf(ofp, "#define NETHACK_GIT_SHA \"%s\"\n", githash); Fprintf(ofp, "#define NETHACK_GIT_BRANCH \"%s\"\n", gitbranch); } if (xpref && get_gitinfo(githash, gitbranch)) { Fprintf(ofp, "#else /* !CROSSCOMPILE || CROSSCOMPILE_HOST */\n"); Fprintf(ofp, "#define NETHACK_%sGIT_SHA \"%s\"\n", xpref, githash); Fprintf(ofp, "#define NETHACK_%sGIT_BRANCH \"%s\"\n", xpref, gitbranch); } Fprintf(ofp, "#endif /* !CROSSCOMPILE || CROSSCOMPILE_HOST */\n"); Fprintf(ofp, "\n"); #ifdef AMIGA { struct tm *tm = localtime((time_t *) &clocktim); Fprintf(ofp, "#define AMIGA_VERSION_STRING "); Fprintf(ofp, "\"\\0$VER: NetHack %d.%d.%d (%d.%d.%d)\"\n", VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL, tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900); } #endif Fclose(ofp); return; } static boolean get_gitinfo(githash, gitbranch) char *githash, *gitbranch; { FILE *gifp; size_t len; char infile[MAXFNAMELEN]; char *line, *strval, *opt, *c, *end; boolean havebranch = FALSE, havehash = FALSE; if (!githash || !gitbranch) return FALSE; Sprintf(infile, DATA_IN_TEMPLATE, GITINFO_FILE); if (!(gifp = fopen(infile, RDTMODE))) { /* perror(infile); */ return FALSE; } /* read the gitinfo file */ while ((line = fgetline(gifp)) != 0) { strval = index(line, '='); if (strval && strlen(strval) < (BUFSZ-1)) { opt = line; *strval++ = '\0'; /* strip off the '\n' */ if ((c = index(strval, '\n')) != 0) *c = '\0'; if ((c = index(opt, '\n')) != 0) *c = '\0'; /* strip leading and trailing white space */ while (*strval == ' ' || *strval == '\t') strval++; end = eos(strval); while (--end >= strval && (*end == ' ' || *end == '\t')) *end = '\0'; while (*opt == ' ' || *opt == '\t') opt++; end = eos(opt); while (--end >= opt && (*end == ' ' || *end == '\t')) *end = '\0'; len = strlen(opt); if ((len >= strlen("gitbranch")) && !case_insensitive_comp(opt, "gitbranch")) { Strcpy(gitbranch, strval); havebranch = TRUE; } if ((len >= strlen("githash")) && !case_insensitive_comp(opt, "githash")) { Strcpy(githash, strval); havehash = TRUE; } } free(line); } Fclose(gifp); if (havebranch && havehash) return TRUE; return FALSE; } void do_options() { const char *optline; int infocontext = 0; windowing_sanity(); filename[0] = '\0'; #ifdef FILE_PREFIX Strcat(filename, file_prefix); #endif Sprintf(eos(filename), DATA_TEMPLATE, OPTIONS_FILE); if (!(ofp = fopen(filename, WRTMODE))) { perror(filename); exit(EXIT_FAILURE); } while ((optline = do_runtime_info(&infocontext)) != 0) Fprintf(ofp, "%s\n", optline); Fclose(ofp); return; } static void windowing_sanity() { #ifndef DEFAULT_WINDOW_SYS /* pre-standard compilers didn't support #error; wait til run-time */ Fprintf(stderr, "Configuration error: DEFAULT_WINDOW_SYS is not defined.\n"); exit(EXIT_FAILURE); /*NOTREACHED*/ /* put in a dummy value so that do_options() will compile and makedefs will build, otherwise the message above won't ever get delivered */ #define DEFAULT_WINDOW_SYS "" #else /*DEFAULT_WINDOW_SYS*/ if (!window_opts[0].id) { Fprintf(stderr, "Configuration error: no windowing systems " "(TTY_GRAPHICS, &c) enabled.\n"); exit(EXIT_FAILURE); } { int i; for (i = 0; window_opts[i].id; ++i) if (!strcmp(window_opts[i].id, DEFAULT_WINDOW_SYS)) break; if (!window_opts[i].id) { /* went through whole list without a match */ Fprintf(stderr, "Configuration error: DEFAULT_WINDOW_SYS (%s)\n", DEFAULT_WINDOW_SYS); Fprintf(stderr, " does not match any enabled windowing system (%s%s).\n", window_opts[0].id, window_opts[1].id ? ", &c" : ""); exit(EXIT_FAILURE); } } #endif /*DEFAULT_WINDOW_SYS*/ } /* routine to decide whether to discard something from data.base */ static boolean d_filter(line) char *line; { if (*line == '#') return TRUE; /* ignore comment lines */ return FALSE; } /* * New format (v3.1) of 'data' file which allows much faster lookups [pr] "do not edit" first record is a comment line 01234567 hexadecimal formatted offset to text area name-a first name of interest 123,4 offset to name's text, and number of lines for it name-b next name of interest name-c multiple names which share same description also 456,7 share a single offset,count line . sentinel to mark end of names 789,0 dummy record containing offset, count of EOF text-a 4 lines of descriptive text for name-a text-a at file position 0x01234567L + 123L text-a text-a text-b/text-c 7 lines of text for names-b and -c text-b/text-c at fseek(0x01234567L + 456L) ... * */ void do_data() { char infile[60], tempfile[60]; boolean ok; long txt_offset; int entry_cnt, line_cnt; char *line; Sprintf(tempfile, DATA_TEMPLATE, "database.tmp"); filename[0] = '\0'; #ifdef FILE_PREFIX Strcat(filename, file_prefix); #endif Sprintf(eos(filename), DATA_TEMPLATE, DATA_FILE); Sprintf(infile, DATA_IN_TEMPLATE, DATA_FILE); #ifdef SHORT_FILENAMES Strcat(infile, ".bas"); #else Strcat(infile, ".base"); #endif if (!(ifp = fopen(infile, RDTMODE))) { /* data.base */ perror(infile); exit(EXIT_FAILURE); } if (!(ofp = fopen(filename, WRTMODE))) { /* data */ perror(filename); Fclose(ifp); exit(EXIT_FAILURE); } if (!(tfp = fopen(tempfile, WRTMODE))) { /* database.tmp */ perror(tempfile); Fclose(ifp); Fclose(ofp); Unlink(filename); exit(EXIT_FAILURE); } /* output a dummy header record; we'll rewind and overwrite it later */ Fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, 0L); entry_cnt = line_cnt = 0; /* read through the input file and split it into two sections */ while ((line = fgetline(ifp)) != 0) { if (d_filter(line)) { free(line); continue; } if (*line > ' ') { /* got an entry name */ /* first finish previous entry */ if (line_cnt) Fprintf(ofp, "%d\n", line_cnt), line_cnt = 0; /* output the entry name */ (void) fputs(line, ofp); entry_cnt++; /* update number of entries */ } else if (entry_cnt) { /* got some descriptive text */ /* update previous entry with current text offset */ if (!line_cnt) Fprintf(ofp, "%ld,", ftell(tfp)); /* save the text line in the scratch file */ (void) fputs(line, tfp); line_cnt++; /* update line counter */ } free(line); } /* output an end marker and then record the current position */ if (line_cnt) Fprintf(ofp, "%d\n", line_cnt); Fprintf(ofp, ".\n%ld,%d\n", ftell(tfp), 0); txt_offset = ftell(ofp); Fclose(ifp); /* all done with original input file */ /* reprocess the scratch file; 1st format an error msg, just in case */ line = malloc(BUFSZ + MAXFNAMELEN); Sprintf(line, "rewind of \"%s\"", tempfile); if (rewind(tfp) != 0) goto dead_data; free(line); /* copy all lines of text from the scratch file into the output file */ while ((line = fgetline(tfp)) != 0) { (void) fputs(line, ofp); free(line); } /* finished with scratch file */ Fclose(tfp); Unlink(tempfile); /* remove it */ /* update the first record of the output file; prepare error msg 1st */ line = malloc(BUFSZ + MAXFNAMELEN); Sprintf(line, "rewind of \"%s\"", filename); ok = (rewind(ofp) == 0); if (ok) { Sprintf(line, "header rewrite of \"%s\"", filename); ok = (fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, (unsigned long) txt_offset) >= 0); } if (!ok) { dead_data: perror(line); /* report the problem */ free(line); /* close and kill the aborted output file, then give up */ Fclose(ofp); Unlink(filename); exit(EXIT_FAILURE); } free(line); /* all done */ Fclose(ofp); return; } /* routine to decide whether to discard something from oracles.txt */ static boolean h_filter(line) char *line; { static boolean skip = FALSE; char *tag; SpinCursor(3); if (*line == '#') return TRUE; /* ignore comment lines */ tag = malloc(strlen(line)); if (sscanf(line, "----- %s", tag) == 1) { skip = FALSE; } else if (skip && !strncmp(line, "-----", 5)) skip = FALSE; free(tag); return skip; } static const char *special_oracle[] = { "\"...it is rather disconcerting to be confronted with the", "following theorem from [Baker, Gill, and Solovay, 1975].", "", "Theorem 7.18 There exist recursive languages A and B such that", " (1) P(A) == NP(A), and", " (2) P(B) != NP(B)", "", "This provides impressive evidence that the techniques that are", "currently available will not suffice for proving that P != NP or " " ", "that P == NP.\" [Garey and Johnson, p. 185.]" }; /* The oracle file consists of a "do not edit" comment, a decimal count N and set of N+1 hexadecimal fseek offsets, followed by N multiple-line records, separated by "---" lines. The first oracle is a special case. The input data contains just those multi-line records, separated by "-----" lines. */ void do_oracles() { char infile[60], tempfile[60]; boolean in_oracle, ok; long fpos; unsigned long txt_offset, offset; int oracle_cnt; register int i; char *line; Sprintf(tempfile, DATA_TEMPLATE, "oracles.tmp"); filename[0] = '\0'; #ifdef FILE_PREFIX Strcat(filename, file_prefix); #endif Sprintf(eos(filename), DATA_TEMPLATE, ORACLE_FILE); Sprintf(infile, DATA_IN_TEMPLATE, ORACLE_FILE); Strcat(infile, ".txt"); if (!(ifp = fopen(infile, RDTMODE))) { perror(infile); exit(EXIT_FAILURE); } if (!(ofp = fopen(filename, WRTMODE))) { perror(filename); Fclose(ifp); exit(EXIT_FAILURE); } if (!(tfp = fopen(tempfile, WRTMODE))) { /* oracles.tmp */ perror(tempfile); Fclose(ifp); Fclose(ofp); Unlink(filename); exit(EXIT_FAILURE); } /* output a dummy header record; we'll rewind and overwrite it later */ Fprintf(ofp, "%s%5d\n", Dont_Edit_Data, 0); /* handle special oracle; it must come first */ (void) fputs("---\n", tfp); offset = (unsigned long) ftell(tfp); Fprintf(ofp, "%05lx\n", offset); /* start pos of special oracle */ for (i = 0; i < SIZE(special_oracle); i++) { (void) fputs(xcrypt(special_oracle[i]), tfp); (void) fputc('\n', tfp); } SpinCursor(3); oracle_cnt = 1; (void) fputs("---\n", tfp); offset = (unsigned long) ftell(tfp); Fprintf(ofp, "%05lx\n", offset); /* start pos of first oracle */ in_oracle = FALSE; while ((line = fgetline(ifp)) != 0) { SpinCursor(3); if (h_filter(line)) { free(line); continue; } if (!strncmp(line, "-----", 5)) { if (!in_oracle) { free(line); continue; } in_oracle = FALSE; oracle_cnt++; (void) fputs("---\n", tfp); offset = (unsigned long) ftell(tfp); Fprintf(ofp, "%05lx\n", offset); /* start pos of this oracle */ } else { in_oracle = TRUE; (void) fputs(xcrypt(line), tfp); } free(line); } if (in_oracle) { /* need to terminate last oracle */ oracle_cnt++; (void) fputs("---\n", tfp); offset = (unsigned long) ftell(tfp); Fprintf(ofp, "%05lx\n", offset); /* eof position */ } /* record the current position */ txt_offset = (unsigned long) ftell(ofp); Fclose(ifp); /* all done with original input file */ /* reprocess the scratch file; 1st format an error msg, just in case */ line = malloc(BUFSZ + MAXFNAMELEN); Sprintf(line, "rewind of \"%s\"", tempfile); if (rewind(tfp) != 0) goto dead_data; free(line); /* copy all lines of text from the scratch file into the output file */ while ((line = fgetline(tfp)) != 0) { (void) fputs(line, ofp); free(line); } /* finished with scratch file */ Fclose(tfp); Unlink(tempfile); /* remove it */ /* update the first record of the output file; prepare error msg 1st */ line = malloc(BUFSZ + MAXFNAMELEN); Sprintf(line, "rewind of \"%s\"", filename); ok = (rewind(ofp) == 0); if (ok) { Sprintf(line, "header rewrite of \"%s\"", filename); ok = (fprintf(ofp, "%s%5d\n", Dont_Edit_Data, oracle_cnt) >= 0); } if (ok) { Sprintf(line, "data rewrite of \"%s\"", filename); for (i = 0; i <= oracle_cnt; i++) { #ifndef VMS /* alpha/vms v1.0; this fflush seems to confuse ftell */ if (!(ok = (fflush(ofp) == 0))) break; #endif if (!(ok = (fpos = ftell(ofp)) >= 0)) break; if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0))) break; if (!(ok = (fscanf(ofp, "%5lx", &offset) == 1))) break; #ifdef MAC #ifdef __MWERKS__ /* MetroWerks CodeWarrior Pro 1's (AKA CW12) version of MSL (ANSI C Libraries) needs this rewind or else the fprintf stops working. This may also be true for CW11, but has never been checked. */ rewind(ofp); #endif #endif if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0))) break; offset += txt_offset; if (!(ok = (fprintf(ofp, "%05lx\n", offset) >= 0))) break; } } if (!ok) { dead_data: perror(line); /* report the problem */ free(line); /* close and kill the aborted output file, then give up */ Fclose(ofp); Unlink(filename); exit(EXIT_FAILURE); } free(line); /* all done */ Fclose(ofp); return; } void do_dungeon() { char *line; Sprintf(filename, DATA_IN_TEMPLATE, DGN_I_FILE); if (!(ifp = fopen(filename, RDTMODE))) { perror(filename); exit(EXIT_FAILURE); } filename[0] = '\0'; #ifdef FILE_PREFIX Strcat(filename, file_prefix); #endif Sprintf(eos(filename), DGN_TEMPLATE, DGN_O_FILE); if (!(ofp = fopen(filename, WRTMODE))) { perror(filename); exit(EXIT_FAILURE); } Fprintf(ofp, "%s", Dont_Edit_Data); tfp = getfp(DATA_TEMPLATE, "grep.tmp", WRTMODE); grep0(ifp, tfp); ifp = getfp(DATA_TEMPLATE, "grep.tmp", RDTMODE); while ((line = fgetline(ifp)) != 0) { SpinCursor(3); if (line[0] == '#') { free(line); continue; /* discard comments */ } (void) fputs(line, ofp); free(line); } Fclose(ifp); Fclose(ofp); delete_file(DATA_TEMPLATE, "grep.tmp"); return; } void do_monstr() { /* Don't break anything for ports that haven't been updated. */ printf("DEPRECATION WARNINGS:\n"); printf("'makedefs -m' is deprecated. Remove all references\n"); printf(" to it from the build process.\n"); printf("'monstr.c' is deprecated. Remove all references to\n"); printf(" it from the build process.\n"); printf("monstr[] is deprecated. Replace monstr[x] with\n"); printf(" mons[x].difficulty\n"); printf("monstr_init() is deprecated. Remove all references to it.\n"); /* * create the source file, "monstr.c" */ filename[0] = '\0'; #ifdef FILE_PREFIX Strcat(filename, file_prefix); #endif Sprintf(eos(filename), SOURCE_TEMPLATE, MON_STR_C); if (!(ofp = fopen(filename, WRTMODE))) { perror(filename); exit(EXIT_FAILURE); } Fprintf(ofp, "%s", Dont_Edit_Code); Fprintf(ofp, "#include \"config.h\"\n"); Fprintf(ofp, "\nconst int monstrXXX[] = {\n"); Fprintf(ofp, "0};\n"); Fprintf(ofp, "/*\n"); Fprintf(ofp, "DEPRECATION WARNINGS:\n"); Fprintf(ofp, "'makedefs -m' is deprecated. Remove all references\n"); Fprintf(ofp, " to it from the build process.\n"); Fprintf(ofp, "'monstr.c' is deprecated. Remove all references to\n"); Fprintf(ofp, " it from the build process.\n"); Fprintf(ofp, "monstr[] is deprecated. Replace monstr[x] with\n"); Fprintf(ofp, " mons[x].difficulty\n"); Fprintf(ofp, "monstr_init() is deprecated. Remove all references to it.\n"); Fprintf(ofp, "*/\n"); Fprintf(ofp, "\nvoid NDECL(monstr_init);\n"); Fprintf(ofp, "\nvoid\n"); Fprintf(ofp, "monstr_init()\n"); Fprintf(ofp, "{\n"); Fprintf(ofp, " return;\n"); Fprintf(ofp, "}\n"); Fprintf(ofp, "\n/*monstr.c*/\n"); Fclose(ofp); return; } void do_permonst() { int i; char *c, *nam; filename[0] = '\0'; #ifdef FILE_PREFIX Strcat(filename, file_prefix); #endif Sprintf(eos(filename), INCLUDE_TEMPLATE, MONST_FILE); if (!(ofp = fopen(filename, WRTMODE))) { perror(filename); exit(EXIT_FAILURE); } Fprintf(ofp, "%s", Dont_Edit_Code); Fprintf(ofp, "#ifndef PM_H\n#define PM_H\n"); if (use_enum) { Fprintf(ofp, "\nenum monnums {"); #if 0 /* need #define ENUM_PM for the full NetHack build to include these */ Fprintf(ofp, "\n NON_PM = -1,"); Fprintf(ofp, "\n LOW_PM = 0,"); #endif } for (i = 0; mons[i].mlet; i++) { SpinCursor(3); if (use_enum) Fprintf(ofp, "\n PM_"); else Fprintf(ofp, "\n#define\tPM_"); if (mons[i].mlet == S_HUMAN && !strncmp(mons[i].mname, "were", 4)) Fprintf(ofp, "HUMAN_"); for (nam = c = tmpdup(mons[i].mname); *c; c++) if (*c >= 'a' && *c <= 'z') *c -= (char) ('a' - 'A'); else if (*c < 'A' || *c > 'Z') *c = '_'; if (use_enum) Fprintf(ofp, "%s = %d,", nam, i); else Fprintf(ofp, "%s\t%d", nam, i); } if (use_enum) { Fprintf(ofp, "\n\n NUMMONS = %d", i); Fprintf(ofp, "\n};\n"); } else { Fprintf(ofp, "\n\n#define\tNUMMONS\t%d\n", i); } Fprintf(ofp, "\n#endif /* PM_H */\n"); Fclose(ofp); return; } /* Start of Quest text file processing. */ void do_questtxt() { printf("DEPRECATION WARNINGS:\n"); printf("'makedefs -q' is no longer required. Remove all references\n"); printf(" to it from the build process.\n"); printf("'dat/quest.txt' is no longer part of the source tree.\n"); return; } static char temp[32]; static char *limit(name, pref) /* limit a name to 30 characters length */ char *name; int pref; { (void) strncpy(temp, name, pref ? 26 : 30); temp[pref ? 26 : 30] = 0; return temp; } void do_objs() { int i, sum = 0; char *c, *objnam; int nspell = 0; int prefix = 0; char class = '\0'; boolean sumerr = FALSE; filename[0] = '\0'; #ifdef FILE_PREFIX Strcat(filename, file_prefix); #endif Sprintf(eos(filename), INCLUDE_TEMPLATE, ONAME_FILE); if (!(ofp = fopen(filename, WRTMODE))) { perror(filename); exit(EXIT_FAILURE); } Fprintf(ofp, "%s", Dont_Edit_Code); Fprintf(ofp, "#ifndef ONAMES_H\n#define ONAMES_H\n\n"); for (i = 0; !i || objects[i].oc_class != ILLOBJ_CLASS; i++) { SpinCursor(3); objects[i].oc_name_idx = objects[i].oc_descr_idx = i; /* init */ if (!(objnam = tmpdup(OBJ_NAME(objects[i])))) continue; /* make sure probabilities add up to 1000 */ if (objects[i].oc_class != class) { if (sum && sum != 1000) { Fprintf(stderr, "prob error for class %d (%d%%)", class, sum); (void) fflush(stderr); sumerr = TRUE; } class = objects[i].oc_class; sum = 0; } for (c = objnam; *c; c++) if (*c >= 'a' && *c <= 'z') *c -= (char) ('a' - 'A'); else if (*c < 'A' || *c > 'Z') *c = '_'; switch (class) { case WAND_CLASS: Fprintf(ofp, "#define\tWAN_"); prefix = 1; break; case RING_CLASS: Fprintf(ofp, "#define\tRIN_"); prefix = 1; break; case POTION_CLASS: Fprintf(ofp, "#define\tPOT_"); prefix = 1; break; case SPBOOK_CLASS: Fprintf(ofp, "#define\tSPE_"); prefix = 1; nspell++; break; case SCROLL_CLASS: Fprintf(ofp, "#define\tSCR_"); prefix = 1; break; case AMULET_CLASS: /* avoid trouble with stupid C preprocessors */ Fprintf(ofp, "#define\t"); if (objects[i].oc_material == PLASTIC) { Fprintf(ofp, "FAKE_AMULET_OF_YENDOR\t%d\n", i); prefix = -1; break; } break; case GEM_CLASS: /* avoid trouble with stupid C preprocessors */ if (objects[i].oc_material == GLASS) { Fprintf(ofp, "/* #define\t%s\t%d */\n", objnam, i); prefix = -1; break; } /*FALLTHRU*/ default: Fprintf(ofp, "#define\t"); } if (prefix >= 0) Fprintf(ofp, "%s\t%d\n", limit(objnam, prefix), i); prefix = 0; sum += objects[i].oc_prob; } /* check last set of probabilities */ if (sum && sum != 1000) { Fprintf(stderr, "prob error for class %d (%d%%)", class, sum); (void) fflush(stderr); sumerr = TRUE; } Fprintf(ofp, "#define\tLAST_GEM\t(JADE)\n"); Fprintf(ofp, "#define\tMAXSPELL\t%d\n", nspell + 1); Fprintf(ofp, "#define\tNUM_OBJECTS\t%d\n", i); Fprintf(ofp, "\n/* Artifacts (unique objects) */\n\n"); for (i = 1; artifact_names[i]; i++) { SpinCursor(3); for (c = objnam = tmpdup(artifact_names[i]); *c; c++) if (*c >= 'a' && *c <= 'z') *c -= (char) ('a' - 'A'); else if (*c < 'A' || *c > 'Z') *c = '_'; if (!strncmp(objnam, "THE_", 4)) objnam += 4; /* fudge _platinum_ YENDORIAN EXPRESS CARD */ if (!strncmp(objnam, "PLATINUM_", 9)) objnam += 9; Fprintf(ofp, "#define\tART_%s\t%d\n", limit(objnam, 1), i); } Fprintf(ofp, "#define\tNROFARTIFACTS\t%d\n", i - 1); Fprintf(ofp, "\n#endif /* ONAMES_H */\n"); Fclose(ofp); if (sumerr) exit(EXIT_FAILURE); return; } /* Read one line from input, up to and including the next newline * character. Returns a pointer to the heap-allocated string, or a * null pointer if no characters were read. */ static char * fgetline(fd) FILE *fd; { static const int inc = 256; int len = inc; char *c = malloc(len), *ret; for (;;) { ret = fgets(c + len - inc, inc, fd); if (!ret) { free(c); c = NULL; break; } else if (index(c, '\n')) { /* normal case: we have a full line */ break; } len += inc; c = realloc(c, len); } return c; } static char * tmpdup(str) const char *str; { static char buf[128]; if (!str) return (char *) 0; (void) strncpy(buf, str, 127); return buf; } /* * macro used to control vision algorithms: * VISION_TABLES => generate tables */ void do_vision() { #ifdef VISION_TABLES int i, j; /* Everything is clear. xclear may be malloc'ed. * Block the upper left corner (BLOCK_HEIGHTxBLOCK_WIDTH) */ for (i = 0; i < MAX_ROW; i++) for (j = 0; j < MAX_COL; j++) if (i < BLOCK_HEIGHT && j < BLOCK_WIDTH) xclear[i][j] = '\000'; else xclear[i][j] = '\001'; #endif /* VISION_TABLES */ SpinCursor(3); /* * create the include file, "vis_tab.h" */ filename[0] = '\0'; #ifdef FILE_PREFIX Strcat(filename, file_prefix); #endif Sprintf(eos(filename), INCLUDE_TEMPLATE, VIS_TAB_H); if (!(ofp = fopen(filename, WRTMODE))) { perror(filename); exit(EXIT_FAILURE); } Fprintf(ofp, "%s", Dont_Edit_Code); Fprintf(ofp, "#ifdef VISION_TABLES\n"); #ifdef VISION_TABLES H_close_gen(); H_far_gen(); #endif /* VISION_TABLES */ Fprintf(ofp, "\n#endif /* VISION_TABLES */\n"); Fclose(ofp); SpinCursor(3); /* * create the source file, "vis_tab.c" */ filename[0] = '\0'; #ifdef FILE_PREFIX Strcat(filename, file_prefix); #endif Sprintf(eos(filename), SOURCE_TEMPLATE, VIS_TAB_C); if (!(ofp = fopen(filename, WRTMODE))) { perror(filename); /* creating vis_tab.c failed; remove the vis_tab.h we just made */ filename[0] = '\0'; #ifdef FILE_PREFIX Strcat(filename, file_prefix); #endif Sprintf(eos(filename), INCLUDE_TEMPLATE, VIS_TAB_H); Unlink(filename); exit(EXIT_FAILURE); } Fprintf(ofp, "%s", Dont_Edit_Code); Fprintf(ofp, "#include \"config.h\"\n"); Fprintf(ofp, "#ifdef VISION_TABLES\n"); Fprintf(ofp, "#include \"vis_tab.h\"\n"); SpinCursor(3); #ifdef VISION_TABLES C_close_gen(); C_far_gen(); Fprintf(ofp, "\nvoid vis_tab_init() { return; }\n"); #endif /* VISION_TABLES */ SpinCursor(3); Fprintf(ofp, "\n#endif /* VISION_TABLES */\n"); Fprintf(ofp, "\n/*vis_tab.c*/\n"); Fclose(ofp); return; } #ifdef VISION_TABLES /*-------------- vision tables --------------*\ * * Generate the close and far tables. This is done by setting up a * fake dungeon and moving our source to different positions relative * to a block and finding the first/last visible position. The fake * dungeon is all clear execpt for the upper left corner (BLOCK_HEIGHT * by BLOCK_WIDTH) is blocked. Then we move the source around relative * to the corner of the block. For each new position of the source * we check positions on rows "kittycorner" from the source. We check * positions until they are either in sight or out of sight (depends on * which table we are generating). The picture below shows the setup * for the generation of the close table. The generation of the far * table would switch the quadrants of the '@' and the "Check rows * here". * * * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,, Check rows here ,,,,,,,,,,,, * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXB,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, * ............................... * ............................... * .........@..................... * ............................... * * Table generation figure (close_table). The 'X's are blocked points. * The 'B' is a special blocked point. The '@' is the source. The ','s * are the target area. The '.' are just open areas. * * * Example usage of close_table[][][]. * * The table is as follows: * * dy = |row of '@' - row of 'B'| - 1 * dx = |col of '@' - col of 'B'| * * The first indices are the deltas from the source '@' and the block 'B'. * You must check for the value inside the abs value bars being zero. If * so then the block is on the same row and you don't need to do a table * lookup. The last value: * * dcy = |row of block - row to be checked| * * Is the value of the first visible spot on the check row from the * block column. So * * first visible col = close_table[dy][dx][dcy] + col of 'B' * \*-------------- vision tables --------------*/ static void H_close_gen() { Fprintf(ofp, "\n/* Close */\n"); Fprintf(ofp, "#define CLOSE_MAX_SB_DY %2d\t/* |src row - block row| - 1\t*/\n", TEST_HEIGHT - 1); Fprintf(ofp, "#define CLOSE_MAX_SB_DX %2d\t/* |src col - block col|\t*/\n", TEST_WIDTH); Fprintf(ofp, "#define CLOSE_MAX_BC_DY %2d\t/* |block row - check row|\t*/\n", TEST_HEIGHT); Fprintf(ofp, "typedef struct {\n"); Fprintf(ofp, " unsigned char close[CLOSE_MAX_SB_DX][CLOSE_MAX_BC_DY];\n"); Fprintf(ofp, "} close2d;\n"); Fprintf(ofp, "extern close2d close_table[CLOSE_MAX_SB_DY];\n"); return; } static void H_far_gen() { Fprintf(ofp, "\n/* Far */\n"); Fprintf(ofp, "#define FAR_MAX_SB_DY %2d\t/* |src row - block row|\t*/\n", TEST_HEIGHT); Fprintf(ofp, "#define FAR_MAX_SB_DX %2d\t/* |src col - block col| - 1\t*/\n", TEST_WIDTH - 1); Fprintf(ofp, "#define FAR_MAX_BC_DY %2d\t/* |block row - check row| - 1\t*/\n", TEST_HEIGHT - 1); Fprintf(ofp, "typedef struct {\n"); Fprintf(ofp, " unsigned char far_q[FAR_MAX_SB_DX][FAR_MAX_BC_DY];\n"); Fprintf(ofp, "} far2d;\n"); Fprintf(ofp, "extern far2d far_table[FAR_MAX_SB_DY];\n"); return; } static void C_close_gen() { int i, dx, dy; int src_row, src_col; /* source */ int block_row, block_col; /* block */ int this_row; int no_more; const char *delim; block_row = BLOCK_HEIGHT - 1; block_col = BLOCK_WIDTH - 1; Fprintf(ofp, "\n#ifndef FAR_TABLE_ONLY\n"); Fprintf(ofp, "\nclose2d close_table[CLOSE_MAX_SB_DY] = {\n"); #ifndef no_vision_progress Fprintf(stderr, "\nclose:"); #endif for (dy = 1; dy < TEST_HEIGHT; dy++) { src_row = block_row + dy; Fprintf(ofp, "/* DY = %2d (- 1)*/\n {{\n", dy); #ifndef no_vision_progress Fprintf(stderr, " %2d", dy), (void) fflush(stderr); #endif for (dx = 0; dx < TEST_WIDTH; dx++) { src_col = block_col - dx; Fprintf(ofp, " /*%2d*/ {", dx); no_more = 0; for (this_row = 0; this_row < TEST_HEIGHT; this_row++) { delim = (this_row < TEST_HEIGHT - 1) ? "," : ""; if (no_more) { Fprintf(ofp, "%s%s", CLOSE_OFF_TABLE_STRING, delim); continue; } SpinCursor(3); /* Find the first column that we can see. */ for (i = block_col + 1; i < MAX_COL; i++) { if (clear_path(src_row, src_col, block_row - this_row, i)) break; } if (i == MAX_COL) no_more = 1; Fprintf(ofp, "%2d%s", i - block_col, delim); } Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n"); } Fprintf(ofp, " }},\n"); } Fprintf(ofp, "}; /* close_table[] */\n"); /* closing brace for table */ Fprintf(ofp, "#endif /* !FAR_TABLE_ONLY */\n"); #ifndef no_vision_progress Fprintf(stderr, "\n"); #endif return; } static void C_far_gen() { int i, dx, dy; int src_row, src_col; /* source */ int block_row, block_col; /* block */ int this_row; const char *delim; block_row = BLOCK_HEIGHT - 1; block_col = BLOCK_WIDTH - 1; Fprintf(ofp, "\n#ifndef CLOSE_TABLE_ONLY\n"); Fprintf(ofp, "\nfar2d far_table[FAR_MAX_SB_DY] = {\n"); #ifndef no_vision_progress Fprintf(stderr, "\n_far_:"); #endif for (dy = 0; dy < TEST_HEIGHT; dy++) { src_row = block_row - dy; Fprintf(ofp, "/* DY = %2d */\n {{\n", dy); #ifndef no_vision_progress Fprintf(stderr, " %2d", dy), (void) fflush(stderr); #endif for (dx = 1; dx < TEST_WIDTH; dx++) { src_col = block_col + dx; Fprintf(ofp, " /*%2d(-1)*/ {", dx); for (this_row = block_row + 1; this_row < block_row + TEST_HEIGHT; this_row++) { delim = (this_row < block_row + TEST_HEIGHT - 1) ? "," : ""; SpinCursor(3); /* Find first col that we can see. */ for (i = 0; i <= block_col; i++) { if (clear_path(src_row, src_col, this_row, i)) break; } if (block_col - i < 0) Fprintf(ofp, "%s%s", FAR_OFF_TABLE_STRING, delim); else Fprintf(ofp, "%2d%s", block_col - i, delim); } Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n"); } Fprintf(ofp, " }},\n"); } Fprintf(ofp, "}; /* far_table[] */\n"); /* closing brace for table */ Fprintf(ofp, "#endif /* !CLOSE_TABLE_ONLY */\n"); #ifndef no_vision_progress Fprintf(stderr, "\n"); #endif return; } /* * "Draw" a line from the hero to the given location. Stop if we hit a * wall. * * Generalized integer Bresenham's algorithm (fast line drawing) for * all quadrants. From _Procedural Elements for Computer Graphics_, by * David F. Rogers. McGraw-Hill, 1985. * * I have tried a little bit of optimization by pulling compares out of * the inner loops. * * NOTE: This had better *not* be called from a position on the * same row as the hero. */ static int clear_path(you_row, you_col, y2, x2) int you_row, you_col, y2, x2; { int dx, dy, s1, s2; register int i, error, x, y, dxs, dys; x = you_col; y = you_row; dx = abs(x2 - you_col); dy = abs(y2 - you_row); s1 = sign(x2 - you_col); s2 = sign(y2 - you_row); if (s1 == 0) { /* same column */ if (s2 == 1) { /* below (larger y2 value) */ for (i = you_row + 1; i < y2; i++) if (!xclear[i][you_col]) return 0; } else { /* above (smaller y2 value) */ for (i = y2 + 1; i < you_row; i++) if (!xclear[i][you_col]) return 0; } return 1; } /* * Lines at 0 and 90 degrees have been weeded out. */ if (dy > dx) { error = dx; dx = dy; dy = error; /* swap the values */ dxs = dx << 1; /* save the shifted values */ dys = dy << 1; error = dys - dx; /* NOTE: error is used as a temporary above */ for (i = 0; i < dx; i++) { if (!xclear[y][x]) return 0; /* plot point */ while (error >= 0) { x += s1; error -= dxs; } y += s2; error += dys; } } else { dxs = dx << 1; /* save the shifted values */ dys = dy << 1; error = dys - dx; for (i = 0; i < dx; i++) { if (!xclear[y][x]) return 0; /* plot point */ while (error >= 0) { y += s2; error -= dxs; } x += s1; error += dys; } } return 1; } #endif /* VISION_TABLES */ #ifdef STRICT_REF_DEF NEARDATA struct flag flags; #ifdef ATTRIB_H struct attribs attrmax, attrmin; #endif #endif /* STRICT_REF_DEF */ /*makedefs.c*/