With fragile wands, use snap instead of break
[aNetHack.git] / util / makedefs.c
blob25614d8b28bc778945835e7086cb643ae9751acd
1 /* NetHack 3.6 makedefs.c $NHDT-Date: 1459208813 2016/03/28 23:46:53 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.110 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* Copyright (c) M. Stephenson, 1990, 1991. */
4 /* Copyright (c) Dean Luick, 1990. */
5 /* NetHack may be freely redistributed. See license for details. */
7 #define MAKEDEFS_C /* use to conditionally include file sections */
9 #include "config.h"
10 #ifdef MONITOR_HEAP
11 #undef free /* makedefs doesn't use the alloc and free in src/alloc.c */
12 #endif
13 #include "permonst.h"
14 #include "objclass.h"
15 #include "monsym.h"
16 #include "artilist.h"
17 #include "dungeon.h"
18 #include "obj.h"
19 #include "monst.h"
20 #include "you.h"
21 #include "context.h"
22 #include "flag.h"
23 #include "dlb.h"
25 /* version information */
26 #ifdef SHORT_FILENAMES
27 #include "patchlev.h"
28 #else
29 #include "patchlevel.h"
30 #endif
32 #include <ctype.h>
33 #ifdef MAC
34 #if defined(__SC__) || defined(__MRC__) /* MPW compilers */
35 #define MPWTOOL
36 #include <CursorCtl.h>
37 #include <string.h>
38 #else /* MAC without MPWTOOL */
39 #define MACsansMPWTOOL
40 #endif
41 #endif /* MAC */
43 #ifndef MPWTOOL
44 #define SpinCursor(x)
45 #endif
47 #define Fprintf (void) fprintf
48 #define Fclose (void) fclose
49 #define Unlink (void) unlink
50 #if !defined(AMIGA) || defined(AZTEC_C)
51 #define rewind(fp) fseek((fp), 0L, SEEK_SET) /* guarantee a return value */
52 #endif
54 #if defined(UNIX) && !defined(LINT) && !defined(GCC_WARN)
55 static const char SCCS_Id[] = "@(#)makedefs.c\t3.6\t2016/02/12";
56 #endif
58 /* names of files to be generated */
59 #define DATE_FILE "date.h"
60 #define MONST_FILE "pm.h"
61 #define ONAME_FILE "onames.h"
62 #ifndef OPTIONS_FILE
63 #define OPTIONS_FILE "options"
64 #endif
65 #define ORACLE_FILE "oracles"
66 #define DATA_FILE "data"
67 #define RUMOR_FILE "rumors"
68 #define DGN_I_FILE "dungeon.def"
69 #define DGN_O_FILE "dungeon.pdf"
70 #define MON_STR_C "monstr.c"
71 #define QTXT_I_FILE "quest.txt"
72 #define QTXT_O_FILE "quest.dat"
73 #define VIS_TAB_H "vis_tab.h"
74 #define VIS_TAB_C "vis_tab.c"
75 /* locations for those files */
76 #ifdef AMIGA
77 #define FILE_PREFIX
78 #define INCLUDE_TEMPLATE "NH:include/t.%s"
79 #define SOURCE_TEMPLATE "NH:src/%s"
80 #define DGN_TEMPLATE "NH:dat/%s" /* where dungeon.pdf file goes */
81 #define DATA_TEMPLATE "NH:slib/%s"
82 #define DATA_IN_TEMPLATE "NH:dat/%s"
83 #else /* not AMIGA */
84 #if defined(MAC) && !defined(__MACH__)
85 /* MacOS 9 or earlier */
86 #define INCLUDE_TEMPLATE ":include:%s"
87 #define SOURCE_TEMPLATE ":src:%s"
88 #define DGN_TEMPLATE ":dat:%s" /* where dungeon.pdf file goes */
89 #if __SC__ || __MRC__
90 #define DATA_TEMPLATE ":Dungeon:%s"
91 #else
92 #define DATA_TEMPLATE ":lib:%s"
93 #endif /* __SC__ || __MRC__ */
94 #define DATA_IN_TEMPLATE ":dat:%s"
95 #else /* neither AMIGA nor MAC */
96 #ifdef OS2
97 #define INCLUDE_TEMPLATE "..\\include\\%s"
98 #define SOURCE_TEMPLATE "..\\src\\%s"
99 #define DGN_TEMPLATE "..\\dat\\%s" /* where dungeon.pdf file goes */
100 #define DATA_TEMPLATE "..\\dat\\%s"
101 #define DATA_IN_TEMPLATE "..\\dat\\%s"
102 #else /* not AMIGA, MAC, or OS2 */
103 #define INCLUDE_TEMPLATE "../include/%s"
104 #define SOURCE_TEMPLATE "../src/%s"
105 #define DGN_TEMPLATE "../dat/%s" /* where dungeon.pdf file goes */
106 #define DATA_TEMPLATE "../dat/%s"
107 #define DATA_IN_TEMPLATE "../dat/%s"
108 #endif /* else !OS2 */
109 #endif /* else !MAC */
110 #endif /* else !AMIGA */
112 static const char
113 *Dont_Edit_Code =
114 "/* This source file is generated by 'makedefs'. Do not edit. */\n",
115 *Dont_Edit_Data =
116 "#\tThis data file is generated by 'makedefs'. Do not edit. \n";
118 static struct version_info version;
120 /* definitions used for vision tables */
121 #define TEST_WIDTH COLNO
122 #define TEST_HEIGHT ROWNO
123 #define BLOCK_WIDTH (TEST_WIDTH + 10)
124 #define BLOCK_HEIGHT TEST_HEIGHT /* don't need extra spaces */
125 #define MAX_ROW (BLOCK_HEIGHT + TEST_HEIGHT)
126 #define MAX_COL (BLOCK_WIDTH + TEST_WIDTH)
127 /* Use this as an out-of-bound value in the close table. */
128 #define CLOSE_OFF_TABLE_STRING "99" /* for the close table */
129 #define FAR_OFF_TABLE_STRING "0xff" /* for the far table */
131 #define sign(z) ((z) < 0 ? -1 : ((z) ? 1 : 0))
132 #ifdef VISION_TABLES
133 static char xclear[MAX_ROW][MAX_COL];
134 #endif
135 /*-end of vision defs-*/
137 static char filename[600];
139 #ifdef FILE_PREFIX
140 /* if defined, a first argument not starting with - is
141 * taken as a text string to be prepended to any
142 * output filename generated */
143 char *file_prefix = "";
144 #endif
146 #ifdef MACsansMPWTOOL
147 int FDECL(main, (void));
148 #else
149 int FDECL(main, (int, char **));
150 #endif
151 void FDECL(do_makedefs, (char *));
152 void NDECL(do_objs);
153 void NDECL(do_data);
154 void NDECL(do_dungeon);
155 void NDECL(do_date);
156 void NDECL(do_options);
157 void NDECL(do_monstr);
158 void NDECL(do_permonst);
159 void NDECL(do_questtxt);
160 void NDECL(do_rumors);
161 void NDECL(do_oracles);
162 void NDECL(do_vision);
164 extern void NDECL(monst_init); /* monst.c */
165 extern void NDECL(objects_init); /* objects.c */
167 static void NDECL(link_sanity_check);
168 static char *FDECL(name_file, (const char *, const char *));
169 static void FDECL(delete_file, (const char *template, const char *));
170 static FILE *FDECL(getfp, (const char *, const char *, const char *));
171 static void FDECL(do_ext_makedefs, (int, char **));
173 static void NDECL(make_version);
174 static char *FDECL(version_string, (char *, const char *));
175 static char *FDECL(version_id_string, (char *, const char *));
176 static char *FDECL(bannerc_string, (char *, const char *));
177 static char *FDECL(xcrypt, (const char *));
178 static unsigned long FDECL(read_rumors_file,
179 (const char *, int *, long *, unsigned long));
180 static void FDECL(do_rnd_access_file, (const char *));
181 static boolean FDECL(d_filter, (char *));
182 static boolean FDECL(h_filter, (char *));
183 static boolean FDECL(ranged_attk, (struct permonst *));
184 static int FDECL(mstrength, (struct permonst *));
185 static void NDECL(build_savebones_compat_string);
186 static void NDECL(windowing_sanity);
188 static boolean FDECL(qt_comment, (char *));
189 static boolean FDECL(qt_control, (char *));
190 static int FDECL(get_hdr, (char *));
191 static boolean FDECL(new_id, (char *));
192 static boolean FDECL(known_msg, (int, int));
193 static void FDECL(new_msg, (char *, int, int));
194 static char *FDECL(valid_qt_summary, (char *, BOOLEAN_P));
195 static void FDECL(do_qt_control, (char *));
196 static void FDECL(do_qt_text, (char *));
197 static void NDECL(adjust_qt_hdrs);
198 static void NDECL(put_qt_hdrs);
200 #ifdef VISION_TABLES
201 static void NDECL(H_close_gen);
202 static void NDECL(H_far_gen);
203 static void NDECL(C_close_gen);
204 static void NDECL(C_far_gen);
205 static int FDECL(clear_path, (int, int, int, int));
206 #endif
208 static char *FDECL(fgetline, (FILE*));
209 static char *FDECL(tmpdup, (const char *));
210 static char *FDECL(limit, (char *, int));
211 static char *FDECL(eos, (char *));
213 /* input, output, tmp */
214 static FILE *ifp, *ofp, *tfp;
216 #if defined(__BORLANDC__) && !defined(_WIN32)
217 extern unsigned _stklen = STKSIZ;
218 #endif
220 #ifdef MACsansMPWTOOL
222 main(void)
224 const char *def_options = "odemvpqrshz";
225 char buf[100];
226 int len;
228 printf("Enter options to run: [%s] ", def_options);
229 fflush(stdout);
230 fgets(buf, 100, stdin);
231 len = strlen(buf);
232 if (len <= 1)
233 Strcpy(buf, def_options);
234 else
235 buf[len - 1] = 0; /* remove return */
237 if (buf[0] == '-' && buf[1] == '-') {
238 #if 0
239 split up buf into words
240 do_ext_makedefs(fakeargc, fakeargv);
241 #else
242 printf("extended makedefs not implemented for Mac OS9\n");
243 exit(EXIT_FAILURE);
244 #endif
247 do_makedefs(buf);
248 exit(EXIT_SUCCESS);
249 return 0;
252 #else /* ! MAC */
255 main(argc, argv)
256 int argc;
257 char *argv[];
259 if ((argc != 2)
260 #ifdef FILE_PREFIX
261 && (argc != 3)
262 #endif
263 && !(argv[1][0] == '-' && argv[1][1] == '-')) {
264 Fprintf(stderr, "Bad arg count (%d).\n", argc - 1);
265 (void) fflush(stderr);
266 return 1;
269 #ifdef FILE_PREFIX
270 if (argc >= 2 && argv[1][0] != '-') {
271 file_prefix = argv[1];
272 argc--;
273 argv++;
275 #endif
277 if (argv[1][0] == '-' && argv[1][1] == '-') {
278 do_ext_makedefs(argc, argv);
279 } else {
280 do_makedefs(&argv[1][1]);
282 exit(EXIT_SUCCESS);
283 /*NOTREACHED*/
284 return 0;
287 #endif
289 static void
290 link_sanity_check()
292 /* Note: these initializers don't do anything except guarantee that
293 we're linked properly.
295 monst_init();
296 objects_init();
299 void
300 do_makedefs(options)
301 char *options;
303 boolean more_than_one;
305 link_sanity_check();
307 /* construct the current version number */
308 make_version();
310 more_than_one = strlen(options) > 1;
311 while (*options) {
312 if (more_than_one)
313 Fprintf(stderr, "makedefs -%c\n", *options);
315 switch (*options) {
316 case 'o':
317 case 'O':
318 do_objs();
319 break;
320 case 'd':
321 case 'D':
322 do_data();
323 break;
324 case 'e':
325 case 'E':
326 do_dungeon();
327 break;
328 case 'm':
329 case 'M':
330 do_monstr();
331 break;
332 case 'v':
333 case 'V':
334 do_date();
335 do_options();
336 break;
337 case 'p':
338 case 'P':
339 do_permonst();
340 break;
341 case 'q':
342 case 'Q':
343 do_questtxt();
344 break;
345 case 'r':
346 case 'R':
347 do_rumors();
348 break;
349 case 's':
350 case 'S':
351 do_rnd_access_file(EPITAPHFILE);
352 do_rnd_access_file(ENGRAVEFILE);
353 do_rnd_access_file(BOGUSMONFILE);
354 break;
355 case 'h':
356 case 'H':
357 do_oracles();
358 break;
359 case 'z':
360 case 'Z':
361 do_vision();
362 break;
364 default:
365 Fprintf(stderr, "Unknown option '%c'.\n", *options);
366 (void) fflush(stderr);
367 exit(EXIT_FAILURE);
369 options++;
371 if (more_than_one)
372 Fprintf(stderr, "Completed.\n"); /* feedback */
375 static char namebuf[1000];
377 static char *
378 name_file(template, tag)
379 const char *template;
380 const char *tag;
382 Sprintf(namebuf, template, tag);
383 return namebuf;
386 static void
387 delete_file(template, tag)
388 const char *template;
389 const char *tag;
391 char *name = name_file(template, tag);
393 Unlink(name);
396 static FILE *
397 getfp(template, tag, mode)
398 const char *template;
399 const char *tag;
400 const char *mode;
402 char *name = name_file(template, tag);
403 FILE *rv = fopen(name, mode);
405 if (!rv) {
406 Fprintf(stderr, "Can't open '%s'.\n", name);
407 exit(EXIT_FAILURE);
409 return rv;
412 static boolean debug = FALSE;
414 static FILE *inputfp;
415 static FILE *outputfp;
417 struct grep_var {
418 const char *name;
419 int is_defined; /* 0 undef; 1 defined */
421 /* struct grep_var grep_vars[] and TODO_* constants in include file: */
422 #include "mdgrep.h"
424 static void NDECL(do_grep_showvars);
425 static struct grep_var *FDECL(grepsearch, (const char *));
426 static int FDECL(grep_check_id, (const char *));
427 static void FDECL(grep_show_wstack, (const char *));
428 static char *FDECL(do_grep_control, (char *));
429 static void NDECL(do_grep);
430 static void FDECL(grep0, (FILE *, FILE *));
432 static int grep_trace = 0;
434 #define IS_OPTION(str) if (!strcmp(&argv[0][2], str))
435 #define CONTINUE \
436 argv++, argc--; \
437 continue
438 #define CONSUME \
439 argv++, argc--; \
440 if (argc == 0) { \
441 Fprintf(stderr, "missing option\n"); \
442 exit(EXIT_FAILURE); \
445 static void
446 do_ext_makedefs(int argc, char **argv)
448 int todo = 0;
450 link_sanity_check();
452 argc--;
453 argv++; /* skip program name */
455 while (argc) {
456 if (argv[0][0] != '-')
457 break;
458 if (argv[0][1] != '-') {
459 Fprintf(stderr, "Can't mix - and -- options.\n");
460 exit(EXIT_FAILURE);
462 IS_OPTION("svs") {
463 /* short version string for packaging - note no \n */
464 char buf[100];
465 char delim[10];
467 argv++; /* not CONSUME */
468 delim[0] = '\0';
469 if (argv[0])
470 strcpy(delim, argv[0]);
471 Fprintf(stdout, "%s", version_string(buf, delim));
472 exit(EXIT_SUCCESS);
474 IS_OPTION("debug") {
475 debug = TRUE;
476 CONTINUE;
478 IS_OPTION("make") {
479 CONSUME;
480 do_makedefs(argv[0]);
481 exit(EXIT_SUCCESS);
483 IS_OPTION("input") {
484 CONSUME;
485 if (!strcmp(argv[0], "-")) {
486 inputfp = stdin;
487 } else {
488 inputfp = fopen(argv[0], RDTMODE);
489 if (!inputfp) {
490 Fprintf(stderr, "Can't open '%s'.\n", argv[0]);
491 exit(EXIT_FAILURE);
494 CONTINUE;
496 IS_OPTION("output") {
497 CONSUME;
498 if (!strcmp(argv[0], "-")) {
499 outputfp = stdout;
500 } else {
501 outputfp = fopen(argv[0], WRTMODE);
502 if (!outputfp) {
503 Fprintf(stderr, "Can't open '%s'.\n", argv[0]);
504 exit(EXIT_FAILURE);
507 CONTINUE;
509 IS_OPTION("grep") {
510 if (todo) {
511 Fprintf(stderr, "Can't do grep and something else.\n");
512 exit(EXIT_FAILURE);
514 todo = TODO_GREP;
515 CONTINUE;
517 IS_OPTION("grep-showvars") {
518 do_grep_showvars();
519 exit(EXIT_SUCCESS);
521 IS_OPTION("grep-trace") {
522 grep_trace = 1;
523 CONTINUE;
525 IS_OPTION("grep-define") {
526 struct grep_var *p;
528 CONSUME;
529 p = grepsearch(argv[0]);
530 if (p) {
531 p->is_defined = 1;
532 } else {
533 Fprintf(stderr, "Unknown symbol '%s'\n", argv[0]);
534 exit(EXIT_FAILURE);
536 CONTINUE;
538 IS_OPTION("grep-undef") {
539 struct grep_var *p;
541 CONSUME;
542 p = grepsearch(argv[0]);
543 if (p) {
544 p->is_defined = 0;
545 } else {
546 Fprintf(stderr, "Unknown symbol '%s'\n", argv[0]);
547 exit(EXIT_FAILURE);
549 CONTINUE;
551 #ifdef notyet
552 IS_OPTION("help") {
554 #endif
555 Fprintf(stderr, "Unknown option '%s'.\n", argv[0]);
556 exit(EXIT_FAILURE);
558 if (argc) {
559 Fprintf(stderr, "unexpected argument '%s'.\n", argv[0]);
560 exit(EXIT_FAILURE);
563 switch (todo) {
564 default:
565 Fprintf(stderr, "Confused about what to do?\n");
566 exit(EXIT_FAILURE);
567 case 0:
568 Fprintf(stderr, "Nothing to do?\n");
569 exit(EXIT_FAILURE);
570 case TODO_GREP:
571 do_grep();
572 break;
576 #undef IS_OPTION
577 #undef CONTINUE
578 #undef CONSUME
581 * Filtering syntax:
582 * Any line NOT starting with a caret is either suppressed or passed
583 * through unchanged depending on the current conditional state.
585 * The default conditional state is printing on.
587 * Conditionals may be nested.
589 * makedefs will exit with a EXIT_FAILURE if any errors are detected;
590 * as many errors as possible are detected before giving up.
592 * Unknown identifiers are treated as TRUE and also as an error to
593 * allow processing to continue past the unknown identifier (note
594 * that "#undef" is different than unknown).
596 * Any line starting with a caret is a control line; as in C, zero or
597 * more spaces may be embedded in the line almost anywhere; the caret
598 * MUST be in column 1.
599 * (XXX for the moment, no white space is allowed after the caret because
600 * existing lines in the docs look like that.)
602 * Control lines:
603 * ^^ a line starting with a (single) literal caret
604 * ^# a comment - the line is ignored
605 * ^?ID if defined(ID)
606 * ^!ID if !defined(ID)
607 * ^: else
608 * ^. endif
610 #define GREP_MAGIC '^'
611 #define GREP_STACK_SIZE 100
612 #ifdef notyet
613 static int grep_rewrite = 0; /* need to (possibly) rewrite lines */
614 #endif
615 static int grep_writing = 1; /* need to copy lines to output */
616 static int grep_errors = 0;
617 static int grep_sp = 0;
618 #define ST_LD(old, opp) (((old) ? 1 : 0) | ((opp) ? 2 : 0))
619 #define ST_OLD(v) (((v) & 1) != 0)
620 #define ST_OPP(v) (((v) & 2) != 0)
621 #define ST_ELSE 4
622 static int grep_stack[GREP_STACK_SIZE] = { ST_LD(1, 0) };
623 static int grep_lineno = 0;
625 static void
626 do_grep_showvars()
628 int x;
630 for (x = 0; x < SIZE(grep_vars) - 1; x++) {
631 printf("%d\t%s\n", grep_vars[x].is_defined, grep_vars[x].name);
635 static struct grep_var *
636 grepsearch(name)
637 const char *name;
639 /* XXX make into binary search */
640 int x = 0;
642 while (x < SIZE(grep_vars) - 1) {
643 if (!strcmp(grep_vars[x].name, name))
644 return &grep_vars[x];
645 x++;
647 return 0;
650 static int
651 grep_check_id(id)
652 const char *id;
654 struct grep_var *rv;
656 while (*id && isspace((uchar) *id))
657 id++;
658 if (!*id) {
659 Fprintf(stderr, "missing identifier in line %d", grep_lineno);
660 grep_errors++;
661 return 0;
663 rv = grepsearch(id);
664 if (rv) {
665 if (grep_trace) {
666 Fprintf(outputfp, "ID %d %s\n", rv->is_defined, id);
668 return rv->is_defined;
671 if (grep_trace) {
672 Fprintf(outputfp, "ID U %s\n", id);
674 Fprintf(stderr, "unknown identifier '%s' in line %d.\n", id, grep_lineno);
675 grep_errors++;
676 return 2; /* So new features can be checked before makedefs
677 * is rebuilt. */
680 static void
681 grep_show_wstack(tag)
682 const char *tag;
684 int x;
686 if (!grep_trace)
687 return;
689 Fprintf(outputfp, "%s w=%d sp=%d\t", tag, grep_writing, grep_sp);
690 for (x = grep_sp; x >= 0 && x > grep_sp - 6; x--) {
691 Fprintf(outputfp, "[%d]=%d ", x, grep_stack[x]);
693 Fprintf(outputfp, "\n");
696 static char *
697 do_grep_control(buf)
698 char *buf;
700 int isif = 1;
701 char *buf0 = buf;
702 #if 1
703 if (isspace((uchar) buf[0]))
704 return &buf[-1]; /* XXX see docs above */
705 #else
706 while (buf[0] && isspace((uchar) buf[0]))
707 buf++;
708 #endif
709 switch (buf[0]) {
710 case '#': /* comment */
711 break;
712 case '.': /* end of if level */
713 if (grep_sp == 0) {
714 Fprintf(stderr, "unmatched ^. (endif) at line %d.\n",
715 grep_lineno);
716 grep_errors++;
717 } else {
718 grep_writing = ST_OLD(grep_stack[grep_sp--]);
719 grep_show_wstack("pop");
721 break;
722 case '!': /* if not ID */
723 isif = 0;
724 /* FALLTHROUGH */
725 case '?': /* if ID */
726 if (grep_sp == GREP_STACK_SIZE - 2) {
727 Fprintf(stderr, "stack overflow at line %d.", grep_lineno);
728 exit(EXIT_FAILURE);
730 if (grep_writing) {
731 isif = grep_check_id(&buf[1]) ? isif : !isif;
732 grep_stack[++grep_sp] = ST_LD(grep_writing, !isif);
733 grep_writing = isif;
734 } else {
735 grep_stack[++grep_sp] = ST_LD(0, 0);
736 /* grep_writing = 0; */
738 grep_show_wstack("push");
739 break;
740 case ':': /* else */
741 if (ST_ELSE & grep_stack[grep_sp]) {
742 Fprintf(stderr, "multiple : for same conditional at line %d.\n",
743 grep_lineno);
744 grep_errors++;
746 grep_writing = ST_OPP(grep_stack[grep_sp]);
747 grep_stack[grep_sp] |= ST_ELSE;
748 break;
749 #if defined(notyet)
750 case '(': /* start of expression */
751 #endif
752 case GREP_MAGIC: /* ^^ -> ^ */
753 return buf0;
754 default: {
755 char str[10];
757 if (isprint((uchar) buf[0])) {
758 str[0] = buf[0];
759 str[1] = '\0';
760 } else {
761 sprintf(str, "0x%02x", buf[0]);
763 Fprintf(stderr, "unknown control ^%s at line %d.\n", str,
764 grep_lineno);
765 grep_errors++;
766 } break;
768 return NULL;
771 #ifdef notyet
772 static void
773 do_grep_rewrite(buf)
774 char *buf;
776 /* no language features use this yet */
777 return;
779 #endif
781 static void grep0(FILE *, FILE *);
783 static void
784 do_grep()
786 if (!inputfp) {
787 Fprintf(stderr, "--grep requires --input\n");
789 if (!outputfp) {
790 Fprintf(stderr, "--grep requires --output\n");
792 if (!inputfp || !outputfp) {
793 exit(EXIT_FAILURE);
796 grep0(inputfp, outputfp);
799 static void
800 grep0(inputfp0, outputfp0)
801 FILE *inputfp0;
802 FILE *outputfp0;
804 char buf[16384]; /* looong, just in case */
806 while (!feof(inputfp0) && !ferror(inputfp0)) {
807 char *tmp;
808 char *buf1;
810 if (fgets(buf, sizeof(buf), inputfp0) == 0)
811 break;
812 if ((tmp = strchr(buf, '\n')))
813 *tmp = '\0';
814 grep_lineno++;
815 if (grep_trace) {
816 Fprintf(outputfp0, "%04d %c >%s\n", grep_lineno,
817 grep_writing ? ' ' : '#', buf);
820 if (buf[0] == GREP_MAGIC) {
821 buf1 = do_grep_control(&buf[1]);
822 if (!buf1)
823 continue;
824 } else {
825 buf1 = buf;
827 #ifdef notyet
828 if (grep_rewrite)
829 do_grep_rewrite(buf1);
830 #endif
831 if (grep_writing)
832 Fprintf(outputfp0, "%s\n", buf1);
834 if (ferror(inputfp0)) {
835 Fprintf(stderr, "read error!\n");
836 exit(EXIT_FAILURE);
838 if (ferror(outputfp0)) {
839 Fprintf(stderr, "write error!\n");
840 exit(EXIT_FAILURE);
842 fclose(inputfp0);
843 fclose(outputfp0);
844 if (grep_sp) {
845 Fprintf(stderr, "%d unterminated conditional level%s\n", grep_sp,
846 grep_sp == 1 ? "" : "s");
847 grep_errors++;
849 if (grep_errors) {
850 Fprintf(stderr, "%d error%s detected.\n", grep_errors,
851 grep_errors == 1 ? "" : "s");
852 exit(EXIT_FAILURE);
856 /* trivial text encryption routine which can't be broken with `tr' */
857 static char *
858 xcrypt(str)
859 const char *str;
860 { /* duplicated in src/hacklib.c */
861 static char buf[BUFSZ];
862 register const char *p;
863 register char *q;
864 register int bitmask;
866 for (bitmask = 1, p = str, q = buf; *p; q++) {
867 *q = *p++;
868 if (*q & (32 | 64))
869 *q ^= bitmask;
870 if ((bitmask <<= 1) >= 32)
871 bitmask = 1;
873 *q = '\0';
874 return buf;
877 #define PAD_RUMORS_TO 60
878 /* common code for do_rumors(). Return 0 on error. */
879 static unsigned long
880 read_rumors_file(file_ext, rumor_count, rumor_size, old_rumor_offset)
881 const char *file_ext;
882 int *rumor_count;
883 long *rumor_size;
884 unsigned long old_rumor_offset;
886 char infile[600];
887 char *line;
888 unsigned long rumor_offset;
890 Sprintf(infile, DATA_IN_TEMPLATE, RUMOR_FILE);
891 Strcat(infile, file_ext);
892 if (!(ifp = fopen(infile, RDTMODE))) {
893 perror(infile);
894 return 0L;
897 /* copy the rumors */
898 while ((line = fgetline(ifp)) != 0) {
899 #ifdef PAD_RUMORS_TO
900 /* rumor selection is accomplished by seeking to a random
901 position in the file, advancing to newline, and taking
902 the next line; therefore, rumors which follow long-line
903 rumors are most likely to be chosen and rumors which
904 follow short-line rumors are least likely to be chosen;
905 we ameliorate the latter by padding the shortest lines,
906 increasing the chance of the random seek landing in them */
907 int len = (int) strlen(line);
909 if (len <= PAD_RUMORS_TO) {
910 char *base = index(line, '\n');
911 /* this is only safe because fgetline() overallocates */
912 while (len++ < PAD_RUMORS_TO) {
913 *base++ = '_';
915 *base++ = '\n';
916 *base = '\0';
918 #endif
919 (*rumor_count)++;
920 #if 0
921 /*[if we forced binary output, this would be sufficient]*/
922 *rumor_size += strlen(line); /* includes newline */
923 #endif
924 (void) fputs(xcrypt(line), tfp);
925 free(line);
927 /* record the current position; next rumors section will start here */
928 rumor_offset = (unsigned long) ftell(tfp);
929 Fclose(ifp); /* all done with rumors.file_ext */
931 /* the calculated value for *_rumor_count assumes that
932 a single-byte line terminator is in use; for platforms
933 which use two byte CR+LF, we need to override that value
934 [it's much simpler to do so unconditionally, rendering
935 the loop's accumulation above obsolete] */
936 *rumor_size = (long) (rumor_offset - old_rumor_offset);
937 return rumor_offset;
940 void
941 do_rnd_access_file(fname)
942 const char *fname;
944 char *line;
946 Sprintf(filename, DATA_IN_TEMPLATE, fname);
947 Strcat(filename, ".txt");
948 if (!(ifp = fopen(filename, RDTMODE))) {
949 perror(filename);
950 exit(EXIT_FAILURE);
952 filename[0] = '\0';
953 #ifdef FILE_PREFIX
954 Strcat(filename, file_prefix);
955 #endif
956 Sprintf(eos(filename), DATA_TEMPLATE, fname);
957 if (!(ofp = fopen(filename, WRTMODE))) {
958 perror(filename);
959 exit(EXIT_FAILURE);
961 Fprintf(ofp, "%s", Dont_Edit_Data);
963 tfp = getfp(DATA_TEMPLATE, "grep.tmp", WRTMODE);
964 grep0(ifp, tfp);
965 ifp = getfp(DATA_TEMPLATE, "grep.tmp", RDTMODE);
967 while ((line = fgetline(ifp)) != 0) {
968 if (line[0] != '#' && line[0] != '\n')
969 (void) fputs(xcrypt(line), ofp);
970 free(line);
972 Fclose(ifp);
973 Fclose(ofp);
975 delete_file(DATA_TEMPLATE, "grep.tmp");
976 return;
979 void
980 do_rumors()
982 char *line;
983 static const char rumors_header[] =
984 "%s%04d,%06ld,%06lx;%04d,%06ld,%06lx;0,0,%06lx\n";
985 char tempfile[600];
986 int true_rumor_count, false_rumor_count;
987 long true_rumor_size, false_rumor_size;
988 unsigned long true_rumor_offset, false_rumor_offset, eof_offset;
990 Sprintf(tempfile, DATA_TEMPLATE, "rumors.tmp");
991 filename[0] = '\0';
992 #ifdef FILE_PREFIX
993 Strcat(filename, file_prefix);
994 #endif
995 Sprintf(eos(filename), DATA_TEMPLATE, RUMOR_FILE);
996 if (!(ofp = fopen(filename, WRTMODE))) {
997 perror(filename);
998 exit(EXIT_FAILURE);
1000 if (!(tfp = fopen(tempfile, WRTMODE))) {
1001 perror(tempfile);
1002 Fclose(ofp);
1003 exit(EXIT_FAILURE);
1006 true_rumor_count = false_rumor_count = 0;
1007 true_rumor_size = false_rumor_size = 0L;
1008 true_rumor_offset = false_rumor_offset = eof_offset = 0L;
1010 /* output a dummy header record; we'll replace it in final output */
1011 Fprintf(tfp, rumors_header, Dont_Edit_Data, true_rumor_count,
1012 true_rumor_size, true_rumor_offset, false_rumor_count,
1013 false_rumor_size, false_rumor_offset, eof_offset);
1014 /* record the current position; true rumors will start here */
1015 true_rumor_offset = ftell(tfp);
1017 false_rumor_offset = read_rumors_file(
1018 ".tru", &true_rumor_count, &true_rumor_size, true_rumor_offset);
1019 if (!false_rumor_offset)
1020 goto rumors_failure;
1022 eof_offset = read_rumors_file(".fal", &false_rumor_count,
1023 &false_rumor_size, false_rumor_offset);
1024 if (!eof_offset)
1025 goto rumors_failure;
1027 /* get ready to transfer the contents of temp file to output file */
1028 line = malloc(256);
1029 Sprintf(line, "rewind of \"%s\"", tempfile);
1030 if (rewind(tfp) != 0) {
1031 perror(line);
1032 free(line);
1033 goto rumors_failure;
1035 free(line);
1037 /* output the header record */
1038 Fprintf(ofp, rumors_header, Dont_Edit_Data, true_rumor_count,
1039 true_rumor_size, true_rumor_offset, false_rumor_count,
1040 false_rumor_size, false_rumor_offset, eof_offset);
1041 /* skip the temp file's dummy header */
1042 if (!(line = fgetline(tfp))) { /* "Don't Edit" */
1043 perror(tempfile);
1044 goto rumors_failure;
1046 free(line);
1047 if (!(line = fgetline(tfp))) { /* count,size,offset */
1048 perror(tempfile);
1049 goto rumors_failure;
1051 free(line);
1052 /* copy the rest of the temp file into the final output file */
1053 while ((line = fgetline(tfp)) != 0) {
1054 (void) fputs(line, ofp);
1055 free(line);
1057 /* all done; delete temp file */
1058 Fclose(tfp);
1059 Unlink(tempfile);
1060 Fclose(ofp);
1061 return;
1063 rumors_failure:
1064 Fclose(ofp);
1065 Unlink(filename); /* kill empty or incomplete output file */
1066 Fclose(tfp);
1067 Unlink(tempfile); /* and temporary file */
1068 exit(EXIT_FAILURE);
1072 * Use this to explicitly mask out features during version checks.
1074 * ZEROCOMP, RLECOMP, and ZLIB_COMP describe compression features
1075 * that the port/plaform which wrote the savefile was capable of
1076 * dealing with. Don't reject a savefile just because the port
1077 * reading the savefile doesn't match on all/some of them.
1078 * The actual compression features used to produce the savefile are
1079 * recorded in the savefile_info structure immediately following the
1080 * version_info, and that is what needs to be checked against the
1081 * feature set of the port that is reading the savefile back in.
1082 * That check is done in src/restore.c now.
1085 #define IGNORED_FEATURES \
1086 (0L | (1L << 19) /* SCORE_ON_BOTL */ \
1087 | (1L << 27) /* ZEROCOMP */ \
1088 | (1L << 28) /* RLECOMP */ \
1091 static void
1092 make_version()
1094 register int i;
1097 * integer version number
1099 version.incarnation = ((unsigned long) VERSION_MAJOR << 24)
1100 | ((unsigned long) VERSION_MINOR << 16)
1101 | ((unsigned long) PATCHLEVEL << 8)
1102 | ((unsigned long) EDITLEVEL);
1104 * encoded feature list
1105 * Note: if any of these magic numbers are changed or reassigned,
1106 * EDITLEVEL in patchlevel.h should be incremented at the same time.
1107 * The actual values have no special meaning, and the category
1108 * groupings are just for convenience.
1110 version.feature_set = (unsigned long) (0L
1111 /* levels and/or topology (0..4) */
1112 /* monsters (5..9) */
1113 #ifdef MAIL
1114 | (1L << 6)
1115 #endif
1116 /* objects (10..14) */
1117 /* flag bits and/or other global variables (15..26) */
1118 #ifdef TEXTCOLOR
1119 | (1L << 17)
1120 #endif
1121 #ifdef INSURANCE
1122 | (1L << 18)
1123 #endif
1124 #ifdef SCORE_ON_BOTL
1125 | (1L << 19)
1126 #endif
1127 /* data format (27..31)
1128 * External compression methods such as COMPRESS and ZLIB_COMP
1129 * do not affect the contents and are thus excluded from here */
1130 #ifdef ZEROCOMP
1131 | (1L << 27)
1132 #endif
1133 #ifdef RLECOMP
1134 | (1L << 28)
1135 #endif
1138 * Value used for object & monster sanity check.
1139 * (NROFARTIFACTS<<24) | (NUM_OBJECTS<<12) | (NUMMONS<<0)
1141 for (i = 1; artifact_names[i]; i++)
1142 continue;
1143 version.entity_count = (unsigned long) (i - 1);
1144 for (i = 1; objects[i].oc_class != ILLOBJ_CLASS; i++)
1145 continue;
1146 version.entity_count = (version.entity_count << 12) | (unsigned long) i;
1147 for (i = 0; mons[i].mlet; i++)
1148 continue;
1149 version.entity_count = (version.entity_count << 12) | (unsigned long) i;
1151 * Value used for compiler (word size/field alignment/padding) check.
1153 version.struct_sizes1 =
1154 (((unsigned long) sizeof(struct context_info) << 24)
1155 | ((unsigned long) sizeof(struct obj) << 17)
1156 | ((unsigned long) sizeof(struct monst) << 10)
1157 | ((unsigned long) sizeof(struct you)));
1158 version.struct_sizes2 = (((unsigned long) sizeof(struct flag) << 10) |
1159 /* free bits in here */
1160 #ifdef SYSFLAGS
1161 ((unsigned long) sizeof(struct sysflag)));
1162 #else
1163 ((unsigned long) 0L));
1164 #endif
1165 return;
1168 /* REPRODUCIBLE_BUILD will change this to TRUE */
1169 static boolean date_via_env = FALSE;
1171 static char *
1172 version_string(outbuf, delim)
1173 char *outbuf;
1174 const char *delim;
1176 Sprintf(outbuf, "%d%s%d%s%d", VERSION_MAJOR, delim, VERSION_MINOR, delim,
1177 PATCHLEVEL);
1178 #ifdef BETA
1179 Sprintf(eos(outbuf), "-%d", EDITLEVEL);
1180 #endif
1181 return outbuf;
1184 static char *
1185 version_id_string(outbuf, build_date)
1186 char *outbuf;
1187 const char *build_date;
1189 char subbuf[64], versbuf[64];
1191 subbuf[0] = '\0';
1192 #ifdef PORT_SUB_ID
1193 subbuf[0] = ' ';
1194 Strcpy(&subbuf[1], PORT_SUB_ID);
1195 #endif
1196 #ifdef BETA
1197 Strcat(subbuf, " Beta");
1198 #endif
1200 Sprintf(outbuf, "%s NetHack%s Version %s - last %s %s.", PORT_ID,
1201 subbuf, version_string(versbuf, "."),
1202 date_via_env ? "revision" : "build", build_date);
1203 return outbuf;
1206 static char *
1207 bannerc_string(outbuf, build_date)
1208 char *outbuf;
1209 const char *build_date;
1211 char subbuf[64], versbuf[64];
1213 subbuf[0] = '\0';
1214 #ifdef PORT_SUB_ID
1215 subbuf[0] = ' ';
1216 Strcpy(&subbuf[1], PORT_SUB_ID);
1217 #endif
1218 #ifdef BETA
1219 Strcat(subbuf, " Beta");
1220 #endif
1222 Sprintf(outbuf, " Version %s %s%s, %s %s.",
1223 version_string(versbuf, "."), PORT_ID, subbuf,
1224 date_via_env ? "revised" : "built", &build_date[4]);
1225 #if 0
1226 Sprintf(outbuf, "%s NetHack%s %s Copyright 1985-%s (built %s)",
1227 PORT_ID, subbuf, version_string(versbuf,"."), RELEASE_YEAR,
1228 &build_date[4]);
1229 #endif
1230 return outbuf;
1233 void
1234 do_date()
1236 #ifdef KR1ED
1237 long clocktim = 0;
1238 #else
1239 time_t clocktim = 0;
1240 #endif
1241 char *c, cbuf[60], buf[BUFSZ];
1242 const char *ul_sfx;
1244 /* before creating date.h, make sure that xxx_GRAPHICS and
1245 DEFAULT_WINDOW_SYS have been set up in a viable fashion */
1246 windowing_sanity();
1248 filename[0] = '\0';
1249 #ifdef FILE_PREFIX
1250 Strcat(filename, file_prefix);
1251 #endif
1252 Sprintf(eos(filename), INCLUDE_TEMPLATE, DATE_FILE);
1253 if (!(ofp = fopen(filename, WRTMODE))) {
1254 perror(filename);
1255 exit(EXIT_FAILURE);
1257 /* NB: We've moved on from SCCS, but this way this line
1258 * won't get clobbered when downstream projects import
1259 * this file into something more modern. */
1260 Fprintf(ofp, "%s", Dont_Edit_Code);
1262 (void) time(&clocktim);
1263 #ifdef REPRODUCIBLE_BUILD
1266 * Use date+time of latest source file revision (set up in
1267 * our environment rather than derived by scanning sources)
1268 * instead of current date+time, so that later rebuilds of
1269 * the same sources specifying the same configuration will
1270 * produce the same result.
1272 * Changing the configuration should be done by modifying
1273 * config.h or <port>conf.h and setting SOURCE_DATE_EPOCH
1274 * based on whichever changed most recently, not by using
1275 * make CFLAGS='-Dthis -Dthat'
1276 * to make alterations on the fly.
1278 * Limited validation is performed to prevent dates in the
1279 * future (beyond a leeway of 24 hours) or distant past.
1281 * Assumes the value of time_t is in seconds, which is
1282 * fundamental for Unix and mandated by POSIX. For any ports
1283 * where that isn't true, leaving REPRODUCIBLE_BUILD disabled
1284 * is probably preferrable to hacking this code....
1286 static struct tm nh360; /* static init should yield UTC timezone */
1287 unsigned long sd_num, sd_earliest, sd_latest;
1288 const char *sd_str = getenv("SOURCE_DATE_EPOCH");
1290 if (sd_str) {
1291 sd_num = strtoul(sd_str, (char **) 0, 10);
1293 * Note: this does not need to be updated for future
1294 * releases. It serves as a sanity check for potentially
1295 * mis-set environment, not a hard baseline for when the
1296 * current version could have first been built.
1298 /* oldest date we'll accept: 7-Dec-2015 (release of 3.6.0) */
1299 nh360.tm_mday = 7;
1300 nh360.tm_mon = 12 - 1;
1301 nh360.tm_year = 2015 - 1900;
1302 sd_earliest = (unsigned long) mktime(&nh360);
1303 /* 'youngest' date we'll accept: 24 hours in the future */
1304 sd_latest = (unsigned long) clocktim + 24L * 60L * 60L;
1306 if (sd_num >= sd_earliest && sd_num <= sd_latest) {
1307 /* use SOURCE_DATE_EPOCH value */
1308 clocktim = (time_t) sd_num;
1309 date_via_env = TRUE;
1310 } else {
1311 Fprintf(stderr, "? Invalid value for SOURCE_DATE_EPOCH (%lu)",
1312 sd_num);
1313 if (sd_num > 0L && sd_num < sd_earliest)
1314 Fprintf(stderr, ", older than %lu", sd_earliest);
1315 else if (sd_num > sd_latest)
1316 Fprintf(stderr, ", newer than %lu", sd_latest);
1317 Fprintf(stderr, ".\n");
1318 Fprintf(stderr, ": Reverting to current date+time (%lu).\n",
1319 (unsigned long) clocktim);
1320 (void) fflush(stderr);
1322 } else {
1323 /* REPRODUCIBLE_BUILD enabled but SOURCE_DATE_EPOCH is missing */
1324 Fprintf(stderr, "? No value for SOURCE_DATE_EPOCH.\n");
1325 Fprintf(stderr, ": Using current date+time (%lu).\n",
1326 (unsigned long) clocktim);
1327 (void) fflush(stderr);
1329 Strcpy(cbuf, asctime(gmtime(&clocktim)));
1331 #else
1332 /* ordinary build: use current date+time */
1333 Strcpy(cbuf, ctime(&clocktim));
1334 #endif
1336 if ((c = index(cbuf, '\n')) != 0)
1337 *c = '\0'; /* strip off the '\n' */
1338 #ifdef NHSTDC
1339 ul_sfx = "UL";
1340 #else
1341 ul_sfx = "L";
1342 #endif
1343 if (date_via_env)
1344 Fprintf(ofp, "#define SOURCE_DATE_EPOCH (%lu%s) /* via getenv() */\n",
1345 (unsigned long) clocktim, ul_sfx);
1346 Fprintf(ofp, "#define BUILD_DATE \"%s\"\n", cbuf);
1347 if (date_via_env)
1348 Fprintf(ofp, "#define BUILD_TIME SOURCE_DATE_EPOCH\n");
1349 else
1350 Fprintf(ofp, "#define BUILD_TIME (%lu%s)\n",
1351 (unsigned long) clocktim, ul_sfx);
1352 Fprintf(ofp, "\n");
1353 Fprintf(ofp, "#define VERSION_NUMBER 0x%08lx%s\n", version.incarnation,
1354 ul_sfx);
1355 Fprintf(ofp, "#define VERSION_FEATURES 0x%08lx%s\n", version.feature_set,
1356 ul_sfx);
1357 #ifdef IGNORED_FEATURES
1358 Fprintf(ofp, "#define IGNORED_FEATURES 0x%08lx%s\n",
1359 (unsigned long) IGNORED_FEATURES, ul_sfx);
1360 #endif
1361 Fprintf(ofp, "#define VERSION_SANITY1 0x%08lx%s\n", version.entity_count,
1362 ul_sfx);
1363 Fprintf(ofp, "#define VERSION_SANITY2 0x%08lx%s\n", version.struct_sizes1,
1364 ul_sfx);
1365 Fprintf(ofp, "#define VERSION_SANITY3 0x%08lx%s\n", version.struct_sizes2,
1366 ul_sfx);
1367 Fprintf(ofp, "\n");
1368 Fprintf(ofp, "#define VERSION_STRING \"%s\"\n", version_string(buf, "."));
1369 Fprintf(ofp, "#define VERSION_ID \\\n \"%s\"\n",
1370 version_id_string(buf, cbuf));
1371 Fprintf(ofp, "#define COPYRIGHT_BANNER_C \\\n \"%s\"\n",
1372 bannerc_string(buf, cbuf));
1373 Fprintf(ofp, "\n");
1374 #ifdef AMIGA
1376 struct tm *tm = localtime((time_t *) &clocktim);
1378 Fprintf(ofp, "#define AMIGA_VERSION_STRING ");
1379 Fprintf(ofp, "\"\\0$VER: NetHack %d.%d.%d (%d.%d.%d)\"\n",
1380 VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL,
1381 tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900);
1383 #endif
1384 Fclose(ofp);
1385 return;
1388 static char save_bones_compat_buf[BUFSZ];
1390 static void
1391 build_savebones_compat_string()
1393 #ifdef VERSION_COMPATIBILITY
1394 unsigned long uver = VERSION_COMPATIBILITY;
1395 #endif
1396 Strcpy(save_bones_compat_buf,
1397 "save and bones files accepted from version");
1398 #ifdef VERSION_COMPATIBILITY
1399 Sprintf(eos(save_bones_compat_buf), "s %lu.%lu.%lu through %d.%d.%d",
1400 ((uver & 0xFF000000L) >> 24), ((uver & 0x00FF0000L) >> 16),
1401 ((uver & 0x0000FF00L) >> 8), VERSION_MAJOR, VERSION_MINOR,
1402 PATCHLEVEL);
1403 #else
1404 Sprintf(eos(save_bones_compat_buf), " %d.%d.%d only", VERSION_MAJOR,
1405 VERSION_MINOR, PATCHLEVEL);
1406 #endif
1409 static const char *build_opts[] = {
1410 #ifdef AMIGA_WBENCH
1411 "Amiga WorkBench support",
1412 #endif
1413 #ifdef ANSI_DEFAULT
1414 "ANSI default terminal",
1415 #endif
1416 #ifdef TEXTCOLOR
1417 "color",
1418 #endif
1419 #ifdef TTY_TILES_ESCCODES
1420 "console escape codes for tile hinting",
1421 #endif
1422 #ifdef COM_COMPL
1423 "command line completion",
1424 #endif
1425 #ifdef LIFE
1426 "Conway's Game of Life",
1427 #endif
1428 #ifdef COMPRESS
1429 "data file compression",
1430 #endif
1431 #ifdef ZLIB_COMP
1432 "ZLIB data file compression",
1433 #endif
1434 #ifdef DLB
1435 "data librarian",
1436 #endif
1437 #ifdef MFLOPPY
1438 "floppy drive support",
1439 #endif
1440 #ifdef INSURANCE
1441 "insurance files for recovering from crashes",
1442 #endif
1443 #ifdef HOLD_LOCKFILE_OPEN
1444 "exclusive lock on level 0 file",
1445 #endif
1446 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
1447 "external program as a message handler",
1448 #endif
1449 #ifdef LOGFILE
1450 "log file",
1451 #endif
1452 #ifdef MAIL
1453 "mail daemon",
1454 #endif
1455 #ifdef GNUDOS
1456 "MSDOS protected mode",
1457 #endif
1458 #ifdef NEWS
1459 "news file",
1460 #endif
1461 #ifdef OVERLAY
1462 #ifdef MOVERLAY
1463 "MOVE overlays",
1464 #else
1465 #ifdef VROOMM
1466 "VROOMM overlays",
1467 #else
1468 "overlays",
1469 #endif
1470 #endif
1471 #endif
1472 #ifdef SELECTSAVED
1473 "restore saved games via menu",
1474 #endif
1475 #ifdef SCORE_ON_BOTL
1476 "score on status line",
1477 #endif
1478 #ifdef CLIPPING
1479 "screen clipping",
1480 #endif
1481 #ifdef NO_TERMS
1482 #ifdef MAC
1483 "screen control via mactty",
1484 #endif
1485 #ifdef SCREEN_BIOS
1486 "screen control via BIOS",
1487 #endif
1488 #ifdef SCREEN_DJGPPFAST
1489 "screen control via DJGPP fast",
1490 #endif
1491 #ifdef SCREEN_VGA
1492 "screen control via VGA graphics",
1493 #endif
1494 #ifdef WIN32CON
1495 "screen control via WIN32 console I/O",
1496 #endif
1497 #endif
1498 #ifdef SHELL
1499 "shell command",
1500 #endif
1501 #ifdef STATUS_VIA_WINDOWPORT
1502 # ifdef STATUS_HILITES
1503 "status via windowport with highlighting",
1504 # else
1505 "status via windowport without highlighting",
1506 # endif
1507 #else
1508 "traditional status display",
1509 #endif
1510 #ifdef SUSPEND
1511 "suspend command",
1512 #endif
1513 #ifdef TERMINFO
1514 "terminal info library",
1515 #else
1516 #if defined(TERMLIB) \
1517 || ((!defined(MICRO) && !defined(WIN32)) && defined(TTY_GRAPHICS))
1518 "terminal capability library",
1519 #endif
1520 #endif
1521 #ifdef TIMED_DELAY
1522 "timed wait for display effects",
1523 #endif
1524 #ifdef USER_SOUNDS
1525 "user sounds",
1526 #endif
1527 #ifdef PREFIXES_IN_USE
1528 "variable playground",
1529 #endif
1530 #ifdef VISION_TABLES
1531 "vision tables",
1532 #endif
1533 #ifdef ZEROCOMP
1534 "zero-compressed save files",
1535 #endif
1536 #ifdef RLECOMP
1537 "run-length compression of map in save files",
1538 #endif
1539 #ifdef SYSCF
1540 "system configuration at run-time",
1541 #endif
1542 save_bones_compat_buf, "and basic NetHack features"
1545 struct win_info {
1546 const char *id, /* DEFAULT_WINDOW_SYS string */
1547 *name; /* description, often same as id */
1549 static struct win_info window_opts[] = {
1550 #ifdef TTY_GRAPHICS
1551 { "tty", "traditional tty-based graphics" },
1552 #endif
1553 #ifdef X11_GRAPHICS
1554 { "X11", "X11" },
1555 #endif
1556 #ifdef QT_GRAPHICS
1557 { "Qt", "Qt" },
1558 #endif
1559 #ifdef GNOME_GRAPHICS
1560 { "Gnome", "Gnome" },
1561 #endif
1562 #ifdef MAC
1563 { "mac", "Mac" },
1564 #endif
1565 #ifdef AMIGA_INTUITION
1566 { "amii", "Amiga Intuition" },
1567 #endif
1568 #ifdef GEM_GRAPHICS
1569 { "Gem", "Gem" },
1570 #endif
1571 #ifdef MSWIN_GRAPHICS
1572 { "mswin", "mswin" },
1573 #endif
1574 #ifdef BEOS_GRAPHICS
1575 { "BeOS", "BeOS InterfaceKit" },
1576 #endif
1577 { 0, 0 }
1580 static void
1581 windowing_sanity()
1583 #ifndef DEFAULT_WINDOW_SYS
1584 /* pre-standard compilers didn't support #error; wait til run-time */
1585 Fprintf(stderr,
1586 "Configuration error: DEFAULT_WINDOW_SYS is not defined.\n");
1587 exit(EXIT_FAILURE);
1588 /*NOTREACHED*/
1590 /* put in a dummy value so that do_options() will compile and makedefs
1591 will build, otherwise the message above won't ever get delivered */
1592 #define DEFAULT_WINDOW_SYS "<undefined>"
1593 #else /*DEFAULT_WINDOW_SYS*/
1595 if (!window_opts[0].id) {
1596 Fprintf(stderr, "Configuration error: no windowing systems "
1597 "(TTY_GRAPHICS, &c) enabled.\n");
1598 exit(EXIT_FAILURE);
1602 int i;
1604 for (i = 0; window_opts[i].id; ++i)
1605 if (!strcmp(window_opts[i].id, DEFAULT_WINDOW_SYS))
1606 break;
1607 if (!window_opts[i]
1608 .id) { /* went through whole list without a match */
1609 Fprintf(stderr, "Configuration error: DEFAULT_WINDOW_SYS (%s)\n",
1610 DEFAULT_WINDOW_SYS);
1611 Fprintf(stderr,
1612 " does not match any enabled windowing system (%s%s).\n",
1613 window_opts[0].id, window_opts[1].id ? ", &c" : "");
1614 exit(EXIT_FAILURE);
1617 #endif /*DEFAULT_WINDOW_SYS*/
1620 void
1621 do_options()
1623 static const char indent[] = " ";
1624 const char *str, *sep;
1625 char *word, buf[BUFSZ];
1626 int i, length, winsyscnt;
1628 windowing_sanity();
1630 filename[0] = '\0';
1631 #ifdef FILE_PREFIX
1632 Strcat(filename, file_prefix);
1633 #endif
1634 Sprintf(eos(filename), DATA_TEMPLATE, OPTIONS_FILE);
1635 if (!(ofp = fopen(filename, WRTMODE))) {
1636 perror(filename);
1637 exit(EXIT_FAILURE);
1640 build_savebones_compat_string();
1641 Fprintf(ofp,
1642 #ifdef BETA
1643 "\n NetHack version %d.%d.%d [beta]\n",
1644 #else
1645 "\n NetHack version %d.%d.%d\n",
1646 #endif
1647 VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL);
1649 Fprintf(ofp, "\nOptions compiled into this edition:\n");
1650 length = COLNO + 1; /* force 1st item onto new line */
1651 for (i = 0; i < SIZE(build_opts); i++) {
1652 str = strcpy(buf, build_opts[i]);
1653 while (*str) {
1654 word = index(str, ' ');
1655 if (word)
1656 *word = '\0';
1657 if (length + strlen(str) > COLNO - 5)
1658 Fprintf(ofp, "\n%s", indent), length = strlen(indent);
1659 else
1660 Fprintf(ofp, " "), length++;
1661 Fprintf(ofp, "%s", str), length += strlen(str);
1662 str += strlen(str) + (word ? 1 : 0);
1664 Fprintf(ofp, (i < SIZE(build_opts) - 1) ? "," : "."), length++;
1667 winsyscnt = SIZE(window_opts) - 1;
1668 Fprintf(ofp, "\n\nSupported windowing system%s:\n",
1669 (winsyscnt > 1) ? "s" : "");
1670 length = COLNO + 1; /* force 1st item onto new line */
1671 for (i = 0; i < winsyscnt; i++) {
1672 str = window_opts[i].name;
1673 if (length + strlen(str) > COLNO - 5)
1674 Fprintf(ofp, "\n%s", indent), length = strlen(indent);
1675 else
1676 Fprintf(ofp, " "), length++;
1677 Fprintf(ofp, "%s", str), length += strlen(str);
1678 sep = (winsyscnt == 1)
1679 ? "."
1680 : (winsyscnt == 2)
1681 ? ((i == 0) ? " and" : "")
1682 : (i < winsyscnt - 2)
1683 ? ","
1684 : ((i == winsyscnt - 2) ? ", and" : "");
1685 Fprintf(ofp, "%s", sep), length += strlen(sep);
1687 if (winsyscnt > 1)
1688 Fprintf(ofp, "\n%swith a default of %s.", indent, DEFAULT_WINDOW_SYS);
1689 Fprintf(ofp, "\n\n");
1691 Fclose(ofp);
1692 return;
1695 /* routine to decide whether to discard something from data.base */
1696 static boolean
1697 d_filter(line)
1698 char *line;
1700 if (*line == '#')
1701 return TRUE; /* ignore comment lines */
1702 return FALSE;
1707 New format (v3.1) of 'data' file which allows much faster lookups [pr]
1708 "do not edit" first record is a comment line
1709 01234567 hexadecimal formatted offset to text area
1710 name-a first name of interest
1711 123,4 offset to name's text, and number of lines for it
1712 name-b next name of interest
1713 name-c multiple names which share same description also
1714 456,7 share a single offset,count line
1715 . sentinel to mark end of names
1716 789,0 dummy record containing offset, count of EOF
1717 text-a 4 lines of descriptive text for name-a
1718 text-a at file position 0x01234567L + 123L
1719 text-a
1720 text-a
1721 text-b/text-c 7 lines of text for names-b and -c
1722 text-b/text-c at fseek(0x01234567L + 456L)
1727 void
1728 do_data()
1730 char infile[60], tempfile[60];
1731 boolean ok;
1732 long txt_offset;
1733 int entry_cnt, line_cnt;
1734 char *line;
1736 Sprintf(tempfile, DATA_TEMPLATE, "database.tmp");
1737 filename[0] = '\0';
1738 #ifdef FILE_PREFIX
1739 Strcat(filename, file_prefix);
1740 #endif
1741 Sprintf(eos(filename), DATA_TEMPLATE, DATA_FILE);
1742 Sprintf(infile, DATA_IN_TEMPLATE, DATA_FILE);
1743 #ifdef SHORT_FILENAMES
1744 Strcat(infile, ".bas");
1745 #else
1746 Strcat(infile, ".base");
1747 #endif
1748 if (!(ifp = fopen(infile, RDTMODE))) { /* data.base */
1749 perror(infile);
1750 exit(EXIT_FAILURE);
1752 if (!(ofp = fopen(filename, WRTMODE))) { /* data */
1753 perror(filename);
1754 Fclose(ifp);
1755 exit(EXIT_FAILURE);
1757 if (!(tfp = fopen(tempfile, WRTMODE))) { /* database.tmp */
1758 perror(tempfile);
1759 Fclose(ifp);
1760 Fclose(ofp);
1761 Unlink(filename);
1762 exit(EXIT_FAILURE);
1765 /* output a dummy header record; we'll rewind and overwrite it later */
1766 Fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, 0L);
1768 entry_cnt = line_cnt = 0;
1769 /* read through the input file and split it into two sections */
1770 while ((line = fgetline(ifp)) != 0) {
1771 if (d_filter(line)) {
1772 free(line);
1773 continue;
1775 if (*line > ' ') { /* got an entry name */
1776 /* first finish previous entry */
1777 if (line_cnt)
1778 Fprintf(ofp, "%d\n", line_cnt), line_cnt = 0;
1779 /* output the entry name */
1780 (void) fputs(line, ofp);
1781 entry_cnt++; /* update number of entries */
1782 } else if (entry_cnt) { /* got some descriptive text */
1783 /* update previous entry with current text offset */
1784 if (!line_cnt)
1785 Fprintf(ofp, "%ld,", ftell(tfp));
1786 /* save the text line in the scratch file */
1787 (void) fputs(line, tfp);
1788 line_cnt++; /* update line counter */
1790 free(line);
1792 /* output an end marker and then record the current position */
1793 if (line_cnt)
1794 Fprintf(ofp, "%d\n", line_cnt);
1795 Fprintf(ofp, ".\n%ld,%d\n", ftell(tfp), 0);
1796 txt_offset = ftell(ofp);
1797 Fclose(ifp); /* all done with original input file */
1799 /* reprocess the scratch file; 1st format an error msg, just in case */
1800 line = malloc(256);
1801 Sprintf(line, "rewind of \"%s\"", tempfile);
1802 if (rewind(tfp) != 0)
1803 goto dead_data;
1804 free(line);
1805 /* copy all lines of text from the scratch file into the output file */
1806 while ((line = fgetline(tfp)) != 0) {
1807 (void) fputs(line, ofp);
1808 free(line);
1811 /* finished with scratch file */
1812 Fclose(tfp);
1813 Unlink(tempfile); /* remove it */
1815 /* update the first record of the output file; prepare error msg 1st */
1816 line = malloc(256);
1817 Sprintf(line, "rewind of \"%s\"", filename);
1818 ok = (rewind(ofp) == 0);
1819 if (ok) {
1820 Sprintf(line, "header rewrite of \"%s\"", filename);
1821 ok = (fprintf(ofp, "%s%08lx\n", Dont_Edit_Data,
1822 (unsigned long) txt_offset) >= 0);
1824 if (!ok) {
1825 dead_data:
1826 perror(line); /* report the problem */
1827 free(line);
1828 /* close and kill the aborted output file, then give up */
1829 Fclose(ofp);
1830 Unlink(filename);
1831 exit(EXIT_FAILURE);
1833 free(line);
1835 /* all done */
1836 Fclose(ofp);
1838 return;
1841 /* routine to decide whether to discard something from oracles.txt */
1842 static boolean
1843 h_filter(line)
1844 char *line;
1846 static boolean skip = FALSE;
1847 char *tag;
1849 SpinCursor(3);
1851 if (*line == '#')
1852 return TRUE; /* ignore comment lines */
1854 tag = malloc(strlen(line));
1855 if (sscanf(line, "----- %s", tag) == 1) {
1856 skip = FALSE;
1857 } else if (skip && !strncmp(line, "-----", 5))
1858 skip = FALSE;
1859 free(tag);
1860 return skip;
1863 static const char *special_oracle[] = {
1864 "\"...it is rather disconcerting to be confronted with the",
1865 "following theorem from [Baker, Gill, and Solovay, 1975].", "",
1866 "Theorem 7.18 There exist recursive languages A and B such that",
1867 " (1) P(A) == NP(A), and", " (2) P(B) != NP(B)", "",
1868 "This provides impressive evidence that the techniques that are",
1869 "currently available will not suffice for proving that P != NP or "
1870 " ",
1871 "that P == NP.\" [Garey and Johnson, p. 185.]"
1875 The oracle file consists of a "do not edit" comment, a decimal count N
1876 and set of N+1 hexadecimal fseek offsets, followed by N multiple-line
1877 records, separated by "---" lines. The first oracle is a special case.
1878 The input data contains just those multi-line records, separated by
1879 "-----" lines.
1882 void
1883 do_oracles()
1885 char infile[60], tempfile[60];
1886 boolean in_oracle, ok;
1887 long fpos;
1888 unsigned long txt_offset, offset;
1889 int oracle_cnt;
1890 register int i;
1891 char *line;
1893 Sprintf(tempfile, DATA_TEMPLATE, "oracles.tmp");
1894 filename[0] = '\0';
1895 #ifdef FILE_PREFIX
1896 Strcat(filename, file_prefix);
1897 #endif
1898 Sprintf(eos(filename), DATA_TEMPLATE, ORACLE_FILE);
1899 Sprintf(infile, DATA_IN_TEMPLATE, ORACLE_FILE);
1900 Strcat(infile, ".txt");
1901 if (!(ifp = fopen(infile, RDTMODE))) {
1902 perror(infile);
1903 exit(EXIT_FAILURE);
1905 if (!(ofp = fopen(filename, WRTMODE))) {
1906 perror(filename);
1907 Fclose(ifp);
1908 exit(EXIT_FAILURE);
1910 if (!(tfp = fopen(tempfile, WRTMODE))) { /* oracles.tmp */
1911 perror(tempfile);
1912 Fclose(ifp);
1913 Fclose(ofp);
1914 Unlink(filename);
1915 exit(EXIT_FAILURE);
1918 /* output a dummy header record; we'll rewind and overwrite it later */
1919 Fprintf(ofp, "%s%5d\n", Dont_Edit_Data, 0);
1921 /* handle special oracle; it must come first */
1922 (void) fputs("---\n", tfp);
1923 offset = (unsigned long) ftell(tfp);
1924 Fprintf(ofp, "%05lx\n", offset); /* start pos of special oracle */
1925 for (i = 0; i < SIZE(special_oracle); i++) {
1926 (void) fputs(xcrypt(special_oracle[i]), tfp);
1927 (void) fputc('\n', tfp);
1929 SpinCursor(3);
1931 oracle_cnt = 1;
1932 (void) fputs("---\n", tfp);
1933 offset = (unsigned long) ftell(tfp);
1934 Fprintf(ofp, "%05lx\n", offset); /* start pos of first oracle */
1935 in_oracle = FALSE;
1937 while ((line = fgetline(ifp)) != 0) {
1938 SpinCursor(3);
1940 if (h_filter(line)) {
1941 free(line);
1942 continue;
1944 if (!strncmp(line, "-----", 5)) {
1945 if (!in_oracle) {
1946 free(line);
1947 continue;
1949 in_oracle = FALSE;
1950 oracle_cnt++;
1951 (void) fputs("---\n", tfp);
1952 offset = (unsigned long) ftell(tfp);
1953 Fprintf(ofp, "%05lx\n", offset); /* start pos of this oracle */
1954 } else {
1955 in_oracle = TRUE;
1956 (void) fputs(xcrypt(line), tfp);
1958 free(line);
1961 if (in_oracle) { /* need to terminate last oracle */
1962 oracle_cnt++;
1963 (void) fputs("---\n", tfp);
1964 offset = (unsigned long) ftell(tfp);
1965 Fprintf(ofp, "%05lx\n", offset); /* eof position */
1968 /* record the current position */
1969 txt_offset = (unsigned long) ftell(ofp);
1970 Fclose(ifp); /* all done with original input file */
1972 /* reprocess the scratch file; 1st format an error msg, just in case */
1973 line = malloc(256);
1974 Sprintf(line, "rewind of \"%s\"", tempfile);
1975 if (rewind(tfp) != 0)
1976 goto dead_data;
1977 free(line);
1978 /* copy all lines of text from the scratch file into the output file */
1979 while ((line = fgetline(tfp)) != 0) {
1980 (void) fputs(line, ofp);
1981 free(line);
1984 /* finished with scratch file */
1985 Fclose(tfp);
1986 Unlink(tempfile); /* remove it */
1988 /* update the first record of the output file; prepare error msg 1st */
1989 line = malloc(256);
1990 Sprintf(line, "rewind of \"%s\"", filename);
1991 ok = (rewind(ofp) == 0);
1992 if (ok) {
1993 Sprintf(line, "header rewrite of \"%s\"", filename);
1994 ok = (fprintf(ofp, "%s%5d\n", Dont_Edit_Data, oracle_cnt) >= 0);
1996 if (ok) {
1997 Sprintf(line, "data rewrite of \"%s\"", filename);
1998 for (i = 0; i <= oracle_cnt; i++) {
1999 #ifndef VMS /* alpha/vms v1.0; this fflush seems to confuse ftell */
2000 if (!(ok = (fflush(ofp) == 0)))
2001 break;
2002 #endif
2003 if (!(ok = (fpos = ftell(ofp)) >= 0))
2004 break;
2005 if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0)))
2006 break;
2007 if (!(ok = (fscanf(ofp, "%5lx", &offset) == 1)))
2008 break;
2009 #ifdef MAC
2010 #ifdef __MWERKS__
2012 MetroWerks CodeWarrior Pro 1's (AKA CW12) version of MSL
2013 (ANSI C Libraries) needs this rewind or else the fprintf
2014 stops working. This may also be true for CW11, but has
2015 never been checked.
2017 rewind(ofp);
2018 #endif
2019 #endif
2020 if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0)))
2021 break;
2022 offset += txt_offset;
2023 if (!(ok = (fprintf(ofp, "%05lx\n", offset) >= 0)))
2024 break;
2027 if (!ok) {
2028 dead_data:
2029 perror(line); /* report the problem */
2030 free(line);
2031 /* close and kill the aborted output file, then give up */
2032 Fclose(ofp);
2033 Unlink(filename);
2034 exit(EXIT_FAILURE);
2036 free(line);
2038 /* all done */
2039 Fclose(ofp);
2041 return;
2044 void
2045 do_dungeon()
2047 char *line;
2049 Sprintf(filename, DATA_IN_TEMPLATE, DGN_I_FILE);
2050 if (!(ifp = fopen(filename, RDTMODE))) {
2051 perror(filename);
2052 exit(EXIT_FAILURE);
2054 filename[0] = '\0';
2055 #ifdef FILE_PREFIX
2056 Strcat(filename, file_prefix);
2057 #endif
2058 Sprintf(eos(filename), DGN_TEMPLATE, DGN_O_FILE);
2059 if (!(ofp = fopen(filename, WRTMODE))) {
2060 perror(filename);
2061 exit(EXIT_FAILURE);
2063 Fprintf(ofp, "%s", Dont_Edit_Data);
2065 tfp = getfp(DATA_TEMPLATE, "grep.tmp", WRTMODE);
2066 grep0(ifp, tfp);
2067 ifp = getfp(DATA_TEMPLATE, "grep.tmp", RDTMODE);
2069 while ((line = fgetline(ifp)) != 0) {
2070 SpinCursor(3);
2072 if (line[0] == '#') {
2073 free(line);
2074 continue; /* discard comments */
2076 (void) fputs(line, ofp);
2077 free(line);
2079 Fclose(ifp);
2080 Fclose(ofp);
2082 delete_file(DATA_TEMPLATE, "grep.tmp");
2083 return;
2086 static boolean
2087 ranged_attk(ptr) /* returns TRUE if monster can attack at range */
2088 register struct permonst *ptr;
2090 register int i, j;
2091 register int atk_mask = (1 << AT_BREA) | (1 << AT_SPIT) | (1 << AT_GAZE);
2093 for (i = 0; i < NATTK; i++) {
2094 if ((j = ptr->mattk[i].aatyp) >= AT_WEAP || (atk_mask & (1 << j)))
2095 return TRUE;
2098 return FALSE;
2101 /* This routine is designed to return an integer value which represents
2102 * an approximation of monster strength. It uses a similar method of
2103 * determination as "experience()" to arrive at the strength.
2105 static int
2106 mstrength(ptr)
2107 struct permonst *ptr;
2109 int i, tmp2, n, tmp = ptr->mlevel;
2111 if (tmp > 49) /* special fixed hp monster */
2112 tmp = 2 * (tmp - 6) / 4;
2114 /* For creation in groups */
2115 n = (!!(ptr->geno & G_SGROUP));
2116 n += (!!(ptr->geno & G_LGROUP)) << 1;
2118 /* For ranged attacks */
2119 if (ranged_attk(ptr))
2120 n++;
2122 /* For higher ac values */
2123 n += (ptr->ac < 4);
2124 n += (ptr->ac < 0);
2126 /* For very fast monsters */
2127 n += (ptr->mmove >= 18);
2129 /* For each attack and "special" attack */
2130 for (i = 0; i < NATTK; i++) {
2131 tmp2 = ptr->mattk[i].aatyp;
2132 n += (tmp2 > 0);
2133 n += (tmp2 == AT_MAGC);
2134 n += (tmp2 == AT_WEAP && (ptr->mflags2 & M2_STRONG));
2137 /* For each "special" damage type */
2138 for (i = 0; i < NATTK; i++) {
2139 tmp2 = ptr->mattk[i].adtyp;
2140 if ((tmp2 == AD_DRLI) || (tmp2 == AD_STON) || (tmp2 == AD_DRST)
2141 || (tmp2 == AD_DRDX) || (tmp2 == AD_DRCO) || (tmp2 == AD_WERE))
2142 n += 2;
2143 else if (strcmp(ptr->mname, "grid bug"))
2144 n += (tmp2 != AD_PHYS);
2145 n += ((int) (ptr->mattk[i].damd * ptr->mattk[i].damn) > 23);
2148 /* Leprechauns are special cases. They have many hit dice so they can
2149 hit and are hard to kill, but they don't really do much damage. */
2150 if (!strcmp(ptr->mname, "leprechaun"))
2151 n -= 2;
2153 /* Finally, adjust the monster level 0 <= n <= 24 (approx.) */
2154 if (n == 0)
2155 tmp--;
2156 else if (n >= 6)
2157 tmp += (n / 2);
2158 else
2159 tmp += (n / 3 + 1);
2161 return (tmp >= 0) ? tmp : 0;
2164 void
2165 do_monstr()
2167 register struct permonst *ptr;
2168 register int i, j;
2171 * create the source file, "monstr.c"
2173 filename[0] = '\0';
2174 #ifdef FILE_PREFIX
2175 Strcat(filename, file_prefix);
2176 #endif
2177 Sprintf(eos(filename), SOURCE_TEMPLATE, MON_STR_C);
2178 if (!(ofp = fopen(filename, WRTMODE))) {
2179 perror(filename);
2180 exit(EXIT_FAILURE);
2182 Fprintf(ofp, "%s", Dont_Edit_Code);
2183 Fprintf(ofp, "#include \"config.h\"\n");
2184 Fprintf(ofp, "\nconst int monstr[] = {\n");
2185 for (ptr = &mons[0], j = 0; ptr->mlet; ptr++) {
2186 SpinCursor(3);
2188 i = mstrength(ptr);
2189 Fprintf(ofp, "%2d,%c", i, (++j & 15) ? ' ' : '\n');
2191 /* might want to insert a final 0 entry here instead of just newline */
2192 Fprintf(ofp, "%s};\n", (j & 15) ? "\n" : "");
2194 Fprintf(ofp, "\nvoid NDECL(monstr_init);\n");
2195 Fprintf(ofp, "\nvoid\n");
2196 Fprintf(ofp, "monstr_init()\n");
2197 Fprintf(ofp, "{\n");
2198 Fprintf(ofp, " return;\n");
2199 Fprintf(ofp, "}\n");
2200 Fprintf(ofp, "\n/*monstr.c*/\n");
2202 Fclose(ofp);
2203 return;
2206 void
2207 do_permonst()
2209 int i;
2210 char *c, *nam;
2212 filename[0] = '\0';
2213 #ifdef FILE_PREFIX
2214 Strcat(filename, file_prefix);
2215 #endif
2216 Sprintf(eos(filename), INCLUDE_TEMPLATE, MONST_FILE);
2217 if (!(ofp = fopen(filename, WRTMODE))) {
2218 perror(filename);
2219 exit(EXIT_FAILURE);
2221 Fprintf(ofp, "%s", Dont_Edit_Code);
2222 Fprintf(ofp, "#ifndef PM_H\n#define PM_H\n");
2224 if (strcmp(mons[0].mname, "playermon") != 0)
2225 Fprintf(ofp, "\n#define\tPM_PLAYERMON\t(-1)");
2227 for (i = 0; mons[i].mlet; i++) {
2228 SpinCursor(3);
2230 Fprintf(ofp, "\n#define\tPM_");
2231 if (mons[i].mlet == S_HUMAN && !strncmp(mons[i].mname, "were", 4))
2232 Fprintf(ofp, "HUMAN_");
2233 for (nam = c = tmpdup(mons[i].mname); *c; c++)
2234 if (*c >= 'a' && *c <= 'z')
2235 *c -= (char) ('a' - 'A');
2236 else if (*c < 'A' || *c > 'Z')
2237 *c = '_';
2238 Fprintf(ofp, "%s\t%d", nam, i);
2240 Fprintf(ofp, "\n\n#define\tNUMMONS\t%d\n", i);
2241 Fprintf(ofp, "\n#endif /* PM_H */\n");
2242 Fclose(ofp);
2243 return;
2246 /* Start of Quest text file processing. */
2247 #include "qtext.h"
2249 static struct qthdr qt_hdr;
2250 static struct msghdr msg_hdr[N_HDR];
2251 static struct qtmsg *curr_msg;
2253 static int qt_line;
2255 static boolean in_msg;
2256 #define NO_MSG 1 /* strlen of a null line returned by fgets() */
2258 static boolean
2259 qt_comment(s)
2260 char *s;
2262 if (s[0] == '#')
2263 return TRUE;
2264 return (boolean) (!in_msg && strlen(s) == NO_MSG);
2267 static boolean
2268 qt_control(s)
2269 char *s;
2271 return (boolean) (s[0] == '%' && (s[1] == 'C' || s[1] == 'E'));
2274 static int
2275 get_hdr(code)
2276 char *code;
2278 int i;
2280 for (i = 0; i < qt_hdr.n_hdr; i++)
2281 if (!strncmp(code, qt_hdr.id[i], LEN_HDR))
2282 return ++i;
2284 return 0;
2287 static boolean
2288 new_id(code)
2289 char *code;
2291 if (qt_hdr.n_hdr >= N_HDR) {
2292 Fprintf(stderr, OUT_OF_HEADERS, qt_line);
2293 return FALSE;
2296 strncpy(&qt_hdr.id[qt_hdr.n_hdr][0], code, LEN_HDR);
2297 msg_hdr[qt_hdr.n_hdr].n_msg = 0;
2298 qt_hdr.offset[qt_hdr.n_hdr++] = 0L;
2299 return TRUE;
2302 static boolean
2303 known_msg(num, id)
2304 int num, id;
2306 int i;
2308 for (i = 0; i < msg_hdr[num].n_msg; i++)
2309 if (msg_hdr[num].qt_msg[i].msgnum == id)
2310 return TRUE;
2312 return FALSE;
2315 static void
2316 new_msg(s, num, id)
2317 char *s;
2318 int num, id;
2320 struct qtmsg *qt_msg;
2322 if (msg_hdr[num].n_msg >= N_MSG) {
2323 Fprintf(stderr, OUT_OF_MESSAGES, qt_line);
2324 } else {
2325 qt_msg = &(msg_hdr[num].qt_msg[msg_hdr[num].n_msg++]);
2326 qt_msg->msgnum = id;
2327 qt_msg->delivery = s[2];
2328 qt_msg->offset = qt_msg->size = qt_msg->summary_size = 0L;
2330 curr_msg = qt_msg;
2334 /* check %E record for "[summary text]" that nethack can stuff into the
2335 message history buffer when delivering text via window instead of pline */
2336 static char *
2337 valid_qt_summary(s, parsing)
2338 char *s; /* end record: "%E" optionally followed by " [summary]" */
2339 boolean parsing; /* curr_msg is valid iff this is True */
2341 static char summary[BUFSZ];
2342 char *p;
2344 if (*s != '%' || *(s + 1) != 'E')
2345 return (char *) 0;
2346 if ((p = index(s, '[')) == 0)
2347 return (char *) 0;
2348 /* note: opening '[' and closing ']' will be retained in the output;
2349 anything after ']' will be discarded by putting a newline there */
2350 Strcpy(summary, p);
2352 /* have an opening bracket; summary[] holds it and all text that follows
2354 p = eos(summary);
2355 /* find closing bracket */
2356 while (p > summary && *(p - 1) != ']')
2357 --p;
2359 if (p == summary) {
2360 /* we backed up all the way to the start without finding a bracket */
2361 if (parsing) /* malformed summary */
2362 Fprintf(stderr, MAL_SUM, qt_line);
2363 } else if (p == summary + 1) {
2364 ; /* ignore empty [] */
2365 } else { /* got something */
2366 /* p points one spot past ']', usually to '\n';
2367 we need to include the \n as part of the size */
2368 if (parsing) {
2369 /* during the writing pass we won't be able to recheck
2370 delivery, so any useless summary for a pline mode
2371 message has to be carried along to the output file */
2372 if (curr_msg->delivery == 'p')
2373 Fprintf(stderr, DUMB_SUM, qt_line);
2374 /* +1 is for terminating newline */
2375 curr_msg->summary_size = (long) (p - summary) + 1L;
2376 } else {
2377 /* caller is writing rather than just parsing;
2378 force newline after the closing bracket */
2379 Strcpy(p, "\n");
2381 return summary;
2383 return (char *) 0;
2386 static void
2387 do_qt_control(s)
2388 char *s;
2390 char code[BUFSZ];
2391 int num, id = 0;
2393 if (!index(s, '\n'))
2394 Fprintf(stderr, CTRL_TRUNC, qt_line);
2396 switch (s[1]) {
2397 case 'C':
2398 if (in_msg) {
2399 Fprintf(stderr, CREC_IN_MSG, qt_line);
2400 break;
2401 } else {
2402 in_msg = TRUE;
2403 if (sscanf(&s[4], "%s %5d", code, &id) != 2) {
2404 Fprintf(stderr, UNREC_CREC, qt_line);
2405 break;
2407 num = get_hdr(code);
2408 if (!num && !new_id(code))
2409 break;
2410 num = get_hdr(code) - 1;
2411 if (known_msg(num, id))
2412 Fprintf(stderr, DUP_MSG, qt_line);
2413 else
2414 new_msg(s, num, id);
2416 break;
2418 case 'E':
2419 if (!in_msg) {
2420 Fprintf(stderr, END_NOT_IN_MSG, qt_line);
2421 } else {
2422 /* sets curr_msg->summary_size if applicable */
2423 (void) valid_qt_summary(s, TRUE);
2424 in_msg = FALSE;
2426 break;
2428 default:
2429 Fprintf(stderr, UNREC_CREC, qt_line);
2430 break;
2434 static void
2435 do_qt_text(s)
2436 char *s;
2438 if (!in_msg) {
2439 Fprintf(stderr, TEXT_NOT_IN_MSG, qt_line);
2440 } else if (!index(s, '\n')) {
2441 Fprintf(stderr, TEXT_TRUNC, qt_line);
2444 curr_msg->size += strlen(s);
2445 return;
2448 static void
2449 adjust_qt_hdrs()
2451 int i, j;
2452 long count = 0L, hdr_offset = sizeof(int)
2453 + (sizeof(char) * LEN_HDR + sizeof(long))
2454 * qt_hdr.n_hdr;
2456 for (i = 0; i < qt_hdr.n_hdr; i++) {
2457 qt_hdr.offset[i] = hdr_offset;
2458 hdr_offset += sizeof(int) + sizeof(struct qtmsg) * msg_hdr[i].n_msg;
2461 for (i = 0; i < qt_hdr.n_hdr; i++)
2462 for (j = 0; j < msg_hdr[i].n_msg; j++) {
2463 msg_hdr[i].qt_msg[j].offset = hdr_offset + count;
2464 count +=
2465 msg_hdr[i].qt_msg[j].size + msg_hdr[i].qt_msg[j].summary_size;
2467 return;
2470 static void
2471 put_qt_hdrs()
2473 int i;
2476 * The main header record.
2478 if (debug)
2479 Fprintf(stderr, "%ld: header info.\n", ftell(ofp));
2480 (void) fwrite((genericptr_t) & (qt_hdr.n_hdr), sizeof(int), 1, ofp);
2481 (void) fwrite((genericptr_t) & (qt_hdr.id[0][0]), sizeof(char) * LEN_HDR,
2482 qt_hdr.n_hdr, ofp);
2483 (void) fwrite((genericptr_t) & (qt_hdr.offset[0]), sizeof(long),
2484 qt_hdr.n_hdr, ofp);
2485 if (debug) {
2486 for (i = 0; i < qt_hdr.n_hdr; i++)
2487 Fprintf(stderr, "%s @ %ld, ", qt_hdr.id[i], qt_hdr.offset[i]);
2488 Fprintf(stderr, "\n");
2492 * The individual class headers.
2494 for (i = 0; i < qt_hdr.n_hdr; i++) {
2495 if (debug)
2496 Fprintf(stderr, "%ld: %s header info.\n", ftell(ofp),
2497 qt_hdr.id[i]);
2498 (void) fwrite((genericptr_t) & (msg_hdr[i].n_msg), sizeof(int), 1,
2499 ofp);
2500 (void) fwrite((genericptr_t) & (msg_hdr[i].qt_msg[0]),
2501 sizeof(struct qtmsg), msg_hdr[i].n_msg, ofp);
2502 if (debug) {
2503 int j;
2505 for (j = 0; j < msg_hdr[i].n_msg; j++) {
2506 Fprintf(stderr, "msg %d @ %ld (%ld)",
2507 msg_hdr[i].qt_msg[j].msgnum,
2508 msg_hdr[i].qt_msg[j].offset,
2509 msg_hdr[i].qt_msg[j].size);
2510 if (msg_hdr[i].qt_msg[j].summary_size)
2511 Fprintf(stderr, " [%ld]",
2512 msg_hdr[i].qt_msg[j].summary_size);
2513 Fprintf(stderr, "\n");
2519 void
2520 do_questtxt()
2522 char *line;
2524 Sprintf(filename, DATA_IN_TEMPLATE, QTXT_I_FILE);
2525 if (!(ifp = fopen(filename, RDTMODE))) {
2526 perror(filename);
2527 exit(EXIT_FAILURE);
2530 filename[0] = '\0';
2531 #ifdef FILE_PREFIX
2532 Strcat(filename, file_prefix);
2533 #endif
2534 Sprintf(eos(filename), DATA_TEMPLATE, QTXT_O_FILE);
2535 if (!(ofp = fopen(filename, WRBMODE))) {
2536 perror(filename);
2537 Fclose(ifp);
2538 exit(EXIT_FAILURE);
2541 qt_hdr.n_hdr = 0;
2542 qt_line = 0;
2543 in_msg = FALSE;
2545 while ((line = fgetline(ifp)) != 0) {
2546 SpinCursor(3);
2548 qt_line++;
2549 if (qt_control(line))
2550 do_qt_control(line);
2551 else if (qt_comment(line)) {
2552 free(line);
2553 continue;
2554 } else
2555 do_qt_text(line);
2556 free(line);
2559 (void) rewind(ifp);
2560 in_msg = FALSE;
2561 adjust_qt_hdrs();
2562 put_qt_hdrs();
2563 while ((line = fgetline(ifp)) != 0) {
2564 if (qt_control(line)) {
2565 char *summary_p = 0;
2567 in_msg = (line[1] == 'C');
2568 if (!in_msg)
2569 summary_p = valid_qt_summary(line, FALSE);
2570 /* don't write anything unless we've got a summary */
2571 if (!summary_p) {
2572 free(line);
2573 continue;
2575 /* we have summary text; replace raw %E record with it */
2576 Strcpy(line, summary_p); /* (guaranteed to fit) */
2577 } else if (qt_comment(line)) {
2578 free(line);
2579 continue;
2581 if (debug)
2582 Fprintf(stderr, "%ld: %s", ftell(stdout), line);
2583 (void) fputs(xcrypt(line), ofp);
2584 free(line);
2586 Fclose(ifp);
2587 Fclose(ofp);
2588 return;
2591 static char temp[32];
2593 static char *limit(name, pref) /* limit a name to 30 characters length */
2594 char *name;
2595 int pref;
2597 (void) strncpy(temp, name, pref ? 26 : 30);
2598 temp[pref ? 26 : 30] = 0;
2599 return temp;
2602 void
2603 do_objs()
2605 int i, sum = 0;
2606 char *c, *objnam;
2607 int nspell = 0;
2608 int prefix = 0;
2609 char class = '\0';
2610 boolean sumerr = FALSE;
2612 filename[0] = '\0';
2613 #ifdef FILE_PREFIX
2614 Strcat(filename, file_prefix);
2615 #endif
2616 Sprintf(eos(filename), INCLUDE_TEMPLATE, ONAME_FILE);
2617 if (!(ofp = fopen(filename, WRTMODE))) {
2618 perror(filename);
2619 exit(EXIT_FAILURE);
2621 Fprintf(ofp, "%s", Dont_Edit_Code);
2622 Fprintf(ofp, "#ifndef ONAMES_H\n#define ONAMES_H\n\n");
2624 for (i = 0; !i || objects[i].oc_class != ILLOBJ_CLASS; i++) {
2625 SpinCursor(3);
2627 objects[i].oc_name_idx = objects[i].oc_descr_idx = i; /* init */
2628 if (!(objnam = tmpdup(OBJ_NAME(objects[i]))))
2629 continue;
2631 /* make sure probabilities add up to 1000 */
2632 if (objects[i].oc_class != class) {
2633 if (sum && sum != 1000) {
2634 Fprintf(stderr, "prob error for class %d (%d%%)", class, sum);
2635 (void) fflush(stderr);
2636 sumerr = TRUE;
2638 class = objects[i].oc_class;
2639 sum = 0;
2642 for (c = objnam; *c; c++)
2643 if (*c >= 'a' && *c <= 'z')
2644 *c -= (char) ('a' - 'A');
2645 else if (*c < 'A' || *c > 'Z')
2646 *c = '_';
2648 switch (class) {
2649 case WAND_CLASS:
2650 Fprintf(ofp, "#define\tWAN_");
2651 prefix = 1;
2652 break;
2653 case RING_CLASS:
2654 Fprintf(ofp, "#define\tRIN_");
2655 prefix = 1;
2656 break;
2657 case POTION_CLASS:
2658 Fprintf(ofp, "#define\tPOT_");
2659 prefix = 1;
2660 break;
2661 case SPBOOK_CLASS:
2662 Fprintf(ofp, "#define\tSPE_");
2663 prefix = 1;
2664 nspell++;
2665 break;
2666 case SCROLL_CLASS:
2667 Fprintf(ofp, "#define\tSCR_");
2668 prefix = 1;
2669 break;
2670 case AMULET_CLASS:
2671 /* avoid trouble with stupid C preprocessors */
2672 Fprintf(ofp, "#define\t");
2673 if (objects[i].oc_material == PLASTIC) {
2674 Fprintf(ofp, "FAKE_AMULET_OF_YENDOR\t%d\n", i);
2675 prefix = -1;
2676 break;
2678 break;
2679 case GEM_CLASS:
2680 /* avoid trouble with stupid C preprocessors */
2681 if (objects[i].oc_material == GLASS) {
2682 Fprintf(ofp, "/* #define\t%s\t%d */\n", objnam, i);
2683 prefix = -1;
2684 break;
2686 default:
2687 Fprintf(ofp, "#define\t");
2689 if (prefix >= 0)
2690 Fprintf(ofp, "%s\t%d\n", limit(objnam, prefix), i);
2691 prefix = 0;
2693 sum += objects[i].oc_prob;
2696 /* check last set of probabilities */
2697 if (sum && sum != 1000) {
2698 Fprintf(stderr, "prob error for class %d (%d%%)", class, sum);
2699 (void) fflush(stderr);
2700 sumerr = TRUE;
2703 Fprintf(ofp, "#define\tLAST_GEM\t(JADE)\n");
2704 Fprintf(ofp, "#define\tMAXSPELL\t%d\n", nspell + 1);
2705 Fprintf(ofp, "#define\tNUM_OBJECTS\t%d\n", i);
2707 Fprintf(ofp, "\n/* Artifacts (unique objects) */\n\n");
2709 for (i = 1; artifact_names[i]; i++) {
2710 SpinCursor(3);
2712 for (c = objnam = tmpdup(artifact_names[i]); *c; c++)
2713 if (*c >= 'a' && *c <= 'z')
2714 *c -= (char) ('a' - 'A');
2715 else if (*c < 'A' || *c > 'Z')
2716 *c = '_';
2718 if (!strncmp(objnam, "THE_", 4))
2719 objnam += 4;
2720 /* fudge _platinum_ YENDORIAN EXPRESS CARD */
2721 if (!strncmp(objnam, "PLATINUM_", 9))
2722 objnam += 9;
2723 Fprintf(ofp, "#define\tART_%s\t%d\n", limit(objnam, 1), i);
2726 Fprintf(ofp, "#define\tNROFARTIFACTS\t%d\n", i - 1);
2727 Fprintf(ofp, "\n#endif /* ONAMES_H */\n");
2728 Fclose(ofp);
2729 if (sumerr)
2730 exit(EXIT_FAILURE);
2731 return;
2734 /* Read one line from input, up to and including the next newline
2735 * character. Returns a pointer to the heap-allocated string, or a
2736 * null pointer if no characters were read.
2738 static char *
2739 fgetline(fd)
2740 FILE *fd;
2742 static const int inc = 256;
2743 int len = inc;
2744 char *c = malloc(len), *ret;
2746 for (;;) {
2747 ret = fgets(c + len - inc, inc, fd);
2748 if (!ret) {
2749 free(c);
2750 c = NULL;
2751 break;
2752 } else if (index(c, '\n')) {
2753 /* normal case: we have a full line */
2754 break;
2756 len += inc;
2757 c = realloc(c, len);
2759 return c;
2762 static char *
2763 tmpdup(str)
2764 const char *str;
2766 static char buf[128];
2768 if (!str)
2769 return (char *) 0;
2770 (void) strncpy(buf, str, 127);
2771 return buf;
2774 static char *
2775 eos(str)
2776 char *str;
2778 while (*str)
2779 str++;
2780 return str;
2784 * macro used to control vision algorithms:
2785 * VISION_TABLES => generate tables
2788 void
2789 do_vision()
2791 #ifdef VISION_TABLES
2792 int i, j;
2794 /* Everything is clear. xclear may be malloc'ed.
2795 * Block the upper left corner (BLOCK_HEIGHTxBLOCK_WIDTH)
2797 for (i = 0; i < MAX_ROW; i++)
2798 for (j = 0; j < MAX_COL; j++)
2799 if (i < BLOCK_HEIGHT && j < BLOCK_WIDTH)
2800 xclear[i][j] = '\000';
2801 else
2802 xclear[i][j] = '\001';
2803 #endif /* VISION_TABLES */
2805 SpinCursor(3);
2808 * create the include file, "vis_tab.h"
2810 filename[0] = '\0';
2811 #ifdef FILE_PREFIX
2812 Strcat(filename, file_prefix);
2813 #endif
2814 Sprintf(filename, INCLUDE_TEMPLATE, VIS_TAB_H);
2815 if (!(ofp = fopen(filename, WRTMODE))) {
2816 perror(filename);
2817 exit(EXIT_FAILURE);
2819 Fprintf(ofp, "%s", Dont_Edit_Code);
2820 Fprintf(ofp, "#ifdef VISION_TABLES\n");
2821 #ifdef VISION_TABLES
2822 H_close_gen();
2823 H_far_gen();
2824 #endif /* VISION_TABLES */
2825 Fprintf(ofp, "\n#endif /* VISION_TABLES */\n");
2826 Fclose(ofp);
2828 SpinCursor(3);
2831 * create the source file, "vis_tab.c"
2833 filename[0] = '\0';
2834 #ifdef FILE_PREFIX
2835 Strcat(filename, file_prefix);
2836 #endif
2837 Sprintf(filename, SOURCE_TEMPLATE, VIS_TAB_C);
2838 if (!(ofp = fopen(filename, WRTMODE))) {
2839 perror(filename);
2840 Sprintf(filename, INCLUDE_TEMPLATE, VIS_TAB_H);
2841 Unlink(filename);
2842 exit(EXIT_FAILURE);
2844 Fprintf(ofp, "%s", Dont_Edit_Code);
2845 Fprintf(ofp, "#include \"config.h\"\n");
2846 Fprintf(ofp, "#ifdef VISION_TABLES\n");
2847 Fprintf(ofp, "#include \"vis_tab.h\"\n");
2849 SpinCursor(3);
2851 #ifdef VISION_TABLES
2852 C_close_gen();
2853 C_far_gen();
2854 Fprintf(ofp, "\nvoid vis_tab_init() { return; }\n");
2855 #endif /* VISION_TABLES */
2857 SpinCursor(3);
2859 Fprintf(ofp, "\n#endif /* VISION_TABLES */\n");
2860 Fprintf(ofp, "\n/*vis_tab.c*/\n");
2862 Fclose(ofp);
2863 return;
2866 #ifdef VISION_TABLES
2868 /*-------------- vision tables --------------*\
2870 * Generate the close and far tables. This is done by setting up a
2871 * fake dungeon and moving our source to different positions relative
2872 * to a block and finding the first/last visible position. The fake
2873 * dungeon is all clear execpt for the upper left corner (BLOCK_HEIGHT
2874 * by BLOCK_WIDTH) is blocked. Then we move the source around relative
2875 * to the corner of the block. For each new position of the source
2876 * we check positions on rows "kittycorner" from the source. We check
2877 * positions until they are either in sight or out of sight (depends on
2878 * which table we are generating). The picture below shows the setup
2879 * for the generation of the close table. The generation of the far
2880 * table would switch the quadrants of the '@' and the "Check rows
2881 * here".
2884 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2885 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2886 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,, Check rows here ,,,,,,,,,,,,
2887 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2888 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXB,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2889 * ...............................
2890 * ...............................
2891 * .........@.....................
2892 * ...............................
2894 * Table generation figure (close_table). The 'X's are blocked points.
2895 * The 'B' is a special blocked point. The '@' is the source. The ','s
2896 * are the target area. The '.' are just open areas.
2899 * Example usage of close_table[][][].
2901 * The table is as follows:
2903 * dy = |row of '@' - row of 'B'| - 1
2904 * dx = |col of '@' - col of 'B'|
2906 * The first indices are the deltas from the source '@' and the block 'B'.
2907 * You must check for the value inside the abs value bars being zero. If
2908 * so then the block is on the same row and you don't need to do a table
2909 * lookup. The last value:
2911 * dcy = |row of block - row to be checked|
2913 * Is the value of the first visible spot on the check row from the
2914 * block column. So
2916 * first visible col = close_table[dy][dx][dcy] + col of 'B'
2918 \*-------------- vision tables --------------*/
2920 static void
2921 H_close_gen()
2923 Fprintf(ofp, "\n/* Close */\n");
2924 Fprintf(ofp,
2925 "#define CLOSE_MAX_SB_DY %2d\t/* |src row - block row| - 1\t*/\n",
2926 TEST_HEIGHT - 1);
2927 Fprintf(ofp,
2928 "#define CLOSE_MAX_SB_DX %2d\t/* |src col - block col|\t*/\n",
2929 TEST_WIDTH);
2930 Fprintf(ofp,
2931 "#define CLOSE_MAX_BC_DY %2d\t/* |block row - check row|\t*/\n",
2932 TEST_HEIGHT);
2933 Fprintf(ofp, "typedef struct {\n");
2934 Fprintf(ofp,
2935 " unsigned char close[CLOSE_MAX_SB_DX][CLOSE_MAX_BC_DY];\n");
2936 Fprintf(ofp, "} close2d;\n");
2937 Fprintf(ofp, "extern close2d close_table[CLOSE_MAX_SB_DY];\n");
2938 return;
2941 static void
2942 H_far_gen()
2944 Fprintf(ofp, "\n/* Far */\n");
2945 Fprintf(ofp, "#define FAR_MAX_SB_DY %2d\t/* |src row - block row|\t*/\n",
2946 TEST_HEIGHT);
2947 Fprintf(ofp,
2948 "#define FAR_MAX_SB_DX %2d\t/* |src col - block col| - 1\t*/\n",
2949 TEST_WIDTH - 1);
2950 Fprintf(ofp,
2951 "#define FAR_MAX_BC_DY %2d\t/* |block row - check row| - 1\t*/\n",
2952 TEST_HEIGHT - 1);
2953 Fprintf(ofp, "typedef struct {\n");
2954 Fprintf(ofp, " unsigned char far_q[FAR_MAX_SB_DX][FAR_MAX_BC_DY];\n");
2955 Fprintf(ofp, "} far2d;\n");
2956 Fprintf(ofp, "extern far2d far_table[FAR_MAX_SB_DY];\n");
2957 return;
2960 static void
2961 C_close_gen()
2963 int i, dx, dy;
2964 int src_row, src_col; /* source */
2965 int block_row, block_col; /* block */
2966 int this_row;
2967 int no_more;
2968 const char *delim;
2970 block_row = BLOCK_HEIGHT - 1;
2971 block_col = BLOCK_WIDTH - 1;
2973 Fprintf(ofp, "\n#ifndef FAR_TABLE_ONLY\n");
2974 Fprintf(ofp, "\nclose2d close_table[CLOSE_MAX_SB_DY] = {\n");
2975 #ifndef no_vision_progress
2976 Fprintf(stderr, "\nclose:");
2977 #endif
2979 for (dy = 1; dy < TEST_HEIGHT; dy++) {
2980 src_row = block_row + dy;
2981 Fprintf(ofp, "/* DY = %2d (- 1)*/\n {{\n", dy);
2982 #ifndef no_vision_progress
2983 Fprintf(stderr, " %2d", dy), (void) fflush(stderr);
2984 #endif
2985 for (dx = 0; dx < TEST_WIDTH; dx++) {
2986 src_col = block_col - dx;
2987 Fprintf(ofp, " /*%2d*/ {", dx);
2989 no_more = 0;
2990 for (this_row = 0; this_row < TEST_HEIGHT; this_row++) {
2991 delim = (this_row < TEST_HEIGHT - 1) ? "," : "";
2992 if (no_more) {
2993 Fprintf(ofp, "%s%s", CLOSE_OFF_TABLE_STRING, delim);
2994 continue;
2996 SpinCursor(3);
2998 /* Find the first column that we can see. */
2999 for (i = block_col + 1; i < MAX_COL; i++) {
3000 if (clear_path(src_row, src_col, block_row - this_row, i))
3001 break;
3004 if (i == MAX_COL)
3005 no_more = 1;
3006 Fprintf(ofp, "%2d%s", i - block_col, delim);
3008 Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n");
3010 Fprintf(ofp, " }},\n");
3013 Fprintf(ofp, "}; /* close_table[] */\n"); /* closing brace for table */
3014 Fprintf(ofp, "#endif /* !FAR_TABLE_ONLY */\n");
3015 #ifndef no_vision_progress
3016 Fprintf(stderr, "\n");
3017 #endif
3018 return;
3021 static void
3022 C_far_gen()
3024 int i, dx, dy;
3025 int src_row, src_col; /* source */
3026 int block_row, block_col; /* block */
3027 int this_row;
3028 const char *delim;
3030 block_row = BLOCK_HEIGHT - 1;
3031 block_col = BLOCK_WIDTH - 1;
3033 Fprintf(ofp, "\n#ifndef CLOSE_TABLE_ONLY\n");
3034 Fprintf(ofp, "\nfar2d far_table[FAR_MAX_SB_DY] = {\n");
3035 #ifndef no_vision_progress
3036 Fprintf(stderr, "\n_far_:");
3037 #endif
3039 for (dy = 0; dy < TEST_HEIGHT; dy++) {
3040 src_row = block_row - dy;
3041 Fprintf(ofp, "/* DY = %2d */\n {{\n", dy);
3042 #ifndef no_vision_progress
3043 Fprintf(stderr, " %2d", dy), (void) fflush(stderr);
3044 #endif
3045 for (dx = 1; dx < TEST_WIDTH; dx++) {
3046 src_col = block_col + dx;
3047 Fprintf(ofp, " /*%2d(-1)*/ {", dx);
3049 for (this_row = block_row + 1; this_row < block_row + TEST_HEIGHT;
3050 this_row++) {
3051 delim = (this_row < block_row + TEST_HEIGHT - 1) ? "," : "";
3053 SpinCursor(3);
3054 /* Find first col that we can see. */
3055 for (i = 0; i <= block_col; i++) {
3056 if (clear_path(src_row, src_col, this_row, i))
3057 break;
3060 if (block_col - i < 0)
3061 Fprintf(ofp, "%s%s", FAR_OFF_TABLE_STRING, delim);
3062 else
3063 Fprintf(ofp, "%2d%s", block_col - i, delim);
3065 Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n");
3067 Fprintf(ofp, " }},\n");
3070 Fprintf(ofp, "}; /* far_table[] */\n"); /* closing brace for table */
3071 Fprintf(ofp, "#endif /* !CLOSE_TABLE_ONLY */\n");
3072 #ifndef no_vision_progress
3073 Fprintf(stderr, "\n");
3074 #endif
3075 return;
3079 * "Draw" a line from the hero to the given location. Stop if we hit a
3080 * wall.
3082 * Generalized integer Bresenham's algorithm (fast line drawing) for
3083 * all quadrants. From _Procedural Elements for Computer Graphics_, by
3084 * David F. Rogers. McGraw-Hill, 1985.
3086 * I have tried a little bit of optimization by pulling compares out of
3087 * the inner loops.
3089 * NOTE: This had better *not* be called from a position on the
3090 * same row as the hero.
3092 static int
3093 clear_path(you_row, you_col, y2, x2)
3094 int you_row, you_col, y2, x2;
3096 int dx, dy, s1, s2;
3097 register int i, error, x, y, dxs, dys;
3099 x = you_col;
3100 y = you_row;
3101 dx = abs(x2 - you_col);
3102 dy = abs(y2 - you_row);
3103 s1 = sign(x2 - you_col);
3104 s2 = sign(y2 - you_row);
3106 if (s1 == 0) { /* same column */
3107 if (s2 == 1) { /* below (larger y2 value) */
3108 for (i = you_row + 1; i < y2; i++)
3109 if (!xclear[i][you_col])
3110 return 0;
3111 } else { /* above (smaller y2 value) */
3112 for (i = y2 + 1; i < you_row; i++)
3113 if (!xclear[i][you_col])
3114 return 0;
3116 return 1;
3120 * Lines at 0 and 90 degrees have been weeded out.
3122 if (dy > dx) {
3123 error = dx;
3124 dx = dy;
3125 dy = error; /* swap the values */
3126 dxs = dx << 1; /* save the shifted values */
3127 dys = dy << 1;
3128 error = dys - dx; /* NOTE: error is used as a temporary above */
3130 for (i = 0; i < dx; i++) {
3131 if (!xclear[y][x])
3132 return 0; /* plot point */
3134 while (error >= 0) {
3135 x += s1;
3136 error -= dxs;
3138 y += s2;
3139 error += dys;
3141 } else {
3142 dxs = dx << 1; /* save the shifted values */
3143 dys = dy << 1;
3144 error = dys - dx;
3146 for (i = 0; i < dx; i++) {
3147 if (!xclear[y][x])
3148 return 0; /* plot point */
3150 while (error >= 0) {
3151 y += s2;
3152 error -= dxs;
3154 x += s1;
3155 error += dys;
3158 return 1;
3160 #endif /* VISION_TABLES */
3162 #ifdef STRICT_REF_DEF
3163 NEARDATA struct flag flags;
3164 #ifdef ATTRIB_H
3165 struct attribs attrmax, attrmin;
3166 #endif
3167 #endif /* STRICT_REF_DEF */
3169 /*makedefs.c*/