NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / util / makedefs.c
blob3945e3e0cb861bc0abcc6263a5f651a31ceac1ac
1 /* aNetHack 0.0.1 makedefs.c $ANH-Date: 1459208813 2016/03/28 23:46:53 $ $ANH-Branch: master $:$ANH-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 /* aNetHack 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 aNetHack%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 aNetHack%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: aNetHack %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 DUMPLOG
1438 "end-of-game dumplogs",
1439 #endif
1440 #ifdef MFLOPPY
1441 "floppy drive support",
1442 #endif
1443 #ifdef INSURANCE
1444 "insurance files for recovering from crashes",
1445 #endif
1446 #ifdef HOLD_LOCKFILE_OPEN
1447 "exclusive lock on level 0 file",
1448 #endif
1449 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
1450 "external program as a message handler",
1451 #endif
1452 #ifdef LOGFILE
1453 "log file",
1454 #endif
1455 #ifdef MAIL
1456 "mail daemon",
1457 #endif
1458 #ifdef GNUDOS
1459 "MSDOS protected mode",
1460 #endif
1461 #ifdef NEWS
1462 "news file",
1463 #endif
1464 #ifdef OVERLAY
1465 #ifdef MOVERLAY
1466 "MOVE overlays",
1467 #else
1468 #ifdef VROOMM
1469 "VROOMM overlays",
1470 #else
1471 "overlays",
1472 #endif
1473 #endif
1474 #endif
1475 #ifdef SELECTSAVED
1476 "restore saved games via menu",
1477 #endif
1478 #ifdef SCORE_ON_BOTL
1479 "score on status line",
1480 #endif
1481 #ifdef CLIPPING
1482 "screen clipping",
1483 #endif
1484 #ifdef NO_TERMS
1485 #ifdef MAC
1486 "screen control via mactty",
1487 #endif
1488 #ifdef SCREEN_BIOS
1489 "screen control via BIOS",
1490 #endif
1491 #ifdef SCREEN_DJGPPFAST
1492 "screen control via DJGPP fast",
1493 #endif
1494 #ifdef SCREEN_VGA
1495 "screen control via VGA graphics",
1496 #endif
1497 #ifdef WIN32CON
1498 "screen control via WIN32 console I/O",
1499 #endif
1500 #endif
1501 #ifdef SHELL
1502 "shell command",
1503 #endif
1504 #ifdef STATUS_VIA_WINDOWPORT
1505 # ifdef STATUS_HILITES
1506 "status via windowport with highlighting",
1507 # else
1508 "status via windowport without highlighting",
1509 # endif
1510 #else
1511 "traditional status display",
1512 #endif
1513 #ifdef SUSPEND
1514 "suspend command",
1515 #endif
1516 #ifdef TERMINFO
1517 "terminal info library",
1518 #else
1519 #if defined(TERMLIB) \
1520 || ((!defined(MICRO) && !defined(WIN32)) && defined(TTY_GRAPHICS))
1521 "terminal capability library",
1522 #endif
1523 #endif
1524 #ifdef TIMED_DELAY
1525 "timed wait for display effects",
1526 #endif
1527 #ifdef USER_SOUNDS
1528 "user sounds",
1529 #endif
1530 #ifdef PREFIXES_IN_USE
1531 "variable playground",
1532 #endif
1533 #ifdef VISION_TABLES
1534 "vision tables",
1535 #endif
1536 #ifdef ZEROCOMP
1537 "zero-compressed save files",
1538 #endif
1539 #ifdef RLECOMP
1540 "run-length compression of map in save files",
1541 #endif
1542 #ifdef SYSCF
1543 "system configuration at run-time",
1544 #endif
1545 save_bones_compat_buf, "and basic aNetHack features"
1548 struct win_info {
1549 const char *id, /* DEFAULT_WINDOW_SYS string */
1550 *name; /* description, often same as id */
1552 static struct win_info window_opts[] = {
1553 #ifdef TTY_GRAPHICS
1554 { "tty", "traditional tty-based graphics" },
1555 #endif
1556 #ifdef X11_GRAPHICS
1557 { "X11", "X11" },
1558 #endif
1559 #ifdef QT_GRAPHICS
1560 { "Qt", "Qt" },
1561 #endif
1562 #ifdef GNOME_GRAPHICS
1563 { "Gnome", "Gnome" },
1564 #endif
1565 #ifdef MAC
1566 { "mac", "Mac" },
1567 #endif
1568 #ifdef AMIGA_INTUITION
1569 { "amii", "Amiga Intuition" },
1570 #endif
1571 #ifdef GEM_GRAPHICS
1572 { "Gem", "Gem" },
1573 #endif
1574 #ifdef MSWIN_GRAPHICS
1575 { "mswin", "mswin" },
1576 #endif
1577 #ifdef BEOS_GRAPHICS
1578 { "BeOS", "BeOS InterfaceKit" },
1579 #endif
1580 { 0, 0 }
1583 static void
1584 windowing_sanity()
1586 #ifndef DEFAULT_WINDOW_SYS
1587 /* pre-standard compilers didn't support #error; wait til run-time */
1588 Fprintf(stderr,
1589 "Configuration error: DEFAULT_WINDOW_SYS is not defined.\n");
1590 exit(EXIT_FAILURE);
1591 /*NOTREACHED*/
1593 /* put in a dummy value so that do_options() will compile and makedefs
1594 will build, otherwise the message above won't ever get delivered */
1595 #define DEFAULT_WINDOW_SYS "<undefined>"
1596 #else /*DEFAULT_WINDOW_SYS*/
1598 if (!window_opts[0].id) {
1599 Fprintf(stderr, "Configuration error: no windowing systems "
1600 "(TTY_GRAPHICS, &c) enabled.\n");
1601 exit(EXIT_FAILURE);
1605 int i;
1607 for (i = 0; window_opts[i].id; ++i)
1608 if (!strcmp(window_opts[i].id, DEFAULT_WINDOW_SYS))
1609 break;
1610 if (!window_opts[i]
1611 .id) { /* went through whole list without a match */
1612 Fprintf(stderr, "Configuration error: DEFAULT_WINDOW_SYS (%s)\n",
1613 DEFAULT_WINDOW_SYS);
1614 Fprintf(stderr,
1615 " does not match any enabled windowing system (%s%s).\n",
1616 window_opts[0].id, window_opts[1].id ? ", &c" : "");
1617 exit(EXIT_FAILURE);
1620 #endif /*DEFAULT_WINDOW_SYS*/
1623 void
1624 do_options()
1626 static const char indent[] = " ";
1627 const char *str, *sep;
1628 char *word, buf[BUFSZ];
1629 int i, length, winsyscnt;
1631 windowing_sanity();
1633 filename[0] = '\0';
1634 #ifdef FILE_PREFIX
1635 Strcat(filename, file_prefix);
1636 #endif
1637 Sprintf(eos(filename), DATA_TEMPLATE, OPTIONS_FILE);
1638 if (!(ofp = fopen(filename, WRTMODE))) {
1639 perror(filename);
1640 exit(EXIT_FAILURE);
1643 build_savebones_compat_string();
1644 Fprintf(ofp,
1645 #ifdef BETA
1646 "\n aNetHack version %d.%d.%d [beta]\n",
1647 #else
1648 "\n aNetHack version %d.%d.%d\n",
1649 #endif
1650 VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL);
1652 Fprintf(ofp, "\nOptions compiled into this edition:\n");
1653 length = COLNO + 1; /* force 1st item onto new line */
1654 for (i = 0; i < SIZE(build_opts); i++) {
1655 str = strcpy(buf, build_opts[i]);
1656 while (*str) {
1657 word = index(str, ' ');
1658 if (word)
1659 *word = '\0';
1660 if (length + strlen(str) > COLNO - 5)
1661 Fprintf(ofp, "\n%s", indent), length = strlen(indent);
1662 else
1663 Fprintf(ofp, " "), length++;
1664 Fprintf(ofp, "%s", str), length += strlen(str);
1665 str += strlen(str) + (word ? 1 : 0);
1667 Fprintf(ofp, (i < SIZE(build_opts) - 1) ? "," : "."), length++;
1670 winsyscnt = SIZE(window_opts) - 1;
1671 Fprintf(ofp, "\n\nSupported windowing system%s:\n",
1672 (winsyscnt > 1) ? "s" : "");
1673 length = COLNO + 1; /* force 1st item onto new line */
1674 for (i = 0; i < winsyscnt; i++) {
1675 str = window_opts[i].name;
1676 if (length + strlen(str) > COLNO - 5)
1677 Fprintf(ofp, "\n%s", indent), length = strlen(indent);
1678 else
1679 Fprintf(ofp, " "), length++;
1680 Fprintf(ofp, "%s", str), length += strlen(str);
1681 sep = (winsyscnt == 1)
1682 ? "."
1683 : (winsyscnt == 2)
1684 ? ((i == 0) ? " and" : "")
1685 : (i < winsyscnt - 2)
1686 ? ","
1687 : ((i == winsyscnt - 2) ? ", and" : "");
1688 Fprintf(ofp, "%s", sep), length += strlen(sep);
1690 if (winsyscnt > 1)
1691 Fprintf(ofp, "\n%swith a default of %s.", indent, DEFAULT_WINDOW_SYS);
1692 Fprintf(ofp, "\n\n");
1694 Fclose(ofp);
1695 return;
1698 /* routine to decide whether to discard something from data.base */
1699 static boolean
1700 d_filter(line)
1701 char *line;
1703 if (*line == '#')
1704 return TRUE; /* ignore comment lines */
1705 return FALSE;
1710 New format (v3.1) of 'data' file which allows much faster lookups [pr]
1711 "do not edit" first record is a comment line
1712 01234567 hexadecimal formatted offset to text area
1713 name-a first name of interest
1714 123,4 offset to name's text, and number of lines for it
1715 name-b next name of interest
1716 name-c multiple names which share same description also
1717 456,7 share a single offset,count line
1718 . sentinel to mark end of names
1719 789,0 dummy record containing offset, count of EOF
1720 text-a 4 lines of descriptive text for name-a
1721 text-a at file position 0x01234567L + 123L
1722 text-a
1723 text-a
1724 text-b/text-c 7 lines of text for names-b and -c
1725 text-b/text-c at fseek(0x01234567L + 456L)
1730 void
1731 do_data()
1733 char infile[60], tempfile[60];
1734 boolean ok;
1735 long txt_offset;
1736 int entry_cnt, line_cnt;
1737 char *line;
1739 Sprintf(tempfile, DATA_TEMPLATE, "database.tmp");
1740 filename[0] = '\0';
1741 #ifdef FILE_PREFIX
1742 Strcat(filename, file_prefix);
1743 #endif
1744 Sprintf(eos(filename), DATA_TEMPLATE, DATA_FILE);
1745 Sprintf(infile, DATA_IN_TEMPLATE, DATA_FILE);
1746 #ifdef SHORT_FILENAMES
1747 Strcat(infile, ".bas");
1748 #else
1749 Strcat(infile, ".base");
1750 #endif
1751 if (!(ifp = fopen(infile, RDTMODE))) { /* data.base */
1752 perror(infile);
1753 exit(EXIT_FAILURE);
1755 if (!(ofp = fopen(filename, WRTMODE))) { /* data */
1756 perror(filename);
1757 Fclose(ifp);
1758 exit(EXIT_FAILURE);
1760 if (!(tfp = fopen(tempfile, WRTMODE))) { /* database.tmp */
1761 perror(tempfile);
1762 Fclose(ifp);
1763 Fclose(ofp);
1764 Unlink(filename);
1765 exit(EXIT_FAILURE);
1768 /* output a dummy header record; we'll rewind and overwrite it later */
1769 Fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, 0L);
1771 entry_cnt = line_cnt = 0;
1772 /* read through the input file and split it into two sections */
1773 while ((line = fgetline(ifp)) != 0) {
1774 if (d_filter(line)) {
1775 free(line);
1776 continue;
1778 if (*line > ' ') { /* got an entry name */
1779 /* first finish previous entry */
1780 if (line_cnt)
1781 Fprintf(ofp, "%d\n", line_cnt), line_cnt = 0;
1782 /* output the entry name */
1783 (void) fputs(line, ofp);
1784 entry_cnt++; /* update number of entries */
1785 } else if (entry_cnt) { /* got some descriptive text */
1786 /* update previous entry with current text offset */
1787 if (!line_cnt)
1788 Fprintf(ofp, "%ld,", ftell(tfp));
1789 /* save the text line in the scratch file */
1790 (void) fputs(line, tfp);
1791 line_cnt++; /* update line counter */
1793 free(line);
1795 /* output an end marker and then record the current position */
1796 if (line_cnt)
1797 Fprintf(ofp, "%d\n", line_cnt);
1798 Fprintf(ofp, ".\n%ld,%d\n", ftell(tfp), 0);
1799 txt_offset = ftell(ofp);
1800 Fclose(ifp); /* all done with original input file */
1802 /* reprocess the scratch file; 1st format an error msg, just in case */
1803 line = malloc(256);
1804 Sprintf(line, "rewind of \"%s\"", tempfile);
1805 if (rewind(tfp) != 0)
1806 goto dead_data;
1807 free(line);
1808 /* copy all lines of text from the scratch file into the output file */
1809 while ((line = fgetline(tfp)) != 0) {
1810 (void) fputs(line, ofp);
1811 free(line);
1814 /* finished with scratch file */
1815 Fclose(tfp);
1816 Unlink(tempfile); /* remove it */
1818 /* update the first record of the output file; prepare error msg 1st */
1819 line = malloc(256);
1820 Sprintf(line, "rewind of \"%s\"", filename);
1821 ok = (rewind(ofp) == 0);
1822 if (ok) {
1823 Sprintf(line, "header rewrite of \"%s\"", filename);
1824 ok = (fprintf(ofp, "%s%08lx\n", Dont_Edit_Data,
1825 (unsigned long) txt_offset) >= 0);
1827 if (!ok) {
1828 dead_data:
1829 perror(line); /* report the problem */
1830 free(line);
1831 /* close and kill the aborted output file, then give up */
1832 Fclose(ofp);
1833 Unlink(filename);
1834 exit(EXIT_FAILURE);
1836 free(line);
1838 /* all done */
1839 Fclose(ofp);
1841 return;
1844 /* routine to decide whether to discard something from oracles.txt */
1845 static boolean
1846 h_filter(line)
1847 char *line;
1849 static boolean skip = FALSE;
1850 char *tag;
1852 SpinCursor(3);
1854 if (*line == '#')
1855 return TRUE; /* ignore comment lines */
1857 tag = malloc(strlen(line));
1858 if (sscanf(line, "----- %s", tag) == 1) {
1859 skip = FALSE;
1860 } else if (skip && !strncmp(line, "-----", 5))
1861 skip = FALSE;
1862 free(tag);
1863 return skip;
1866 static const char *special_oracle[] = {
1867 "\"...it is rather disconcerting to be confronted with the",
1868 "following theorem from [Baker, Gill, and Solovay, 1975].", "",
1869 "Theorem 7.18 There exist recursive languages A and B such that",
1870 " (1) P(A) == NP(A), and", " (2) P(B) != NP(B)", "",
1871 "This provides impressive evidence that the techniques that are",
1872 "currently available will not suffice for proving that P != NP or "
1873 " ",
1874 "that P == NP.\" [Garey and Johnson, p. 185.]"
1878 The oracle file consists of a "do not edit" comment, a decimal count N
1879 and set of N+1 hexadecimal fseek offsets, followed by N multiple-line
1880 records, separated by "---" lines. The first oracle is a special case.
1881 The input data contains just those multi-line records, separated by
1882 "-----" lines.
1885 void
1886 do_oracles()
1888 char infile[60], tempfile[60];
1889 boolean in_oracle, ok;
1890 long fpos;
1891 unsigned long txt_offset, offset;
1892 int oracle_cnt;
1893 register int i;
1894 char *line;
1896 Sprintf(tempfile, DATA_TEMPLATE, "oracles.tmp");
1897 filename[0] = '\0';
1898 #ifdef FILE_PREFIX
1899 Strcat(filename, file_prefix);
1900 #endif
1901 Sprintf(eos(filename), DATA_TEMPLATE, ORACLE_FILE);
1902 Sprintf(infile, DATA_IN_TEMPLATE, ORACLE_FILE);
1903 Strcat(infile, ".txt");
1904 if (!(ifp = fopen(infile, RDTMODE))) {
1905 perror(infile);
1906 exit(EXIT_FAILURE);
1908 if (!(ofp = fopen(filename, WRTMODE))) {
1909 perror(filename);
1910 Fclose(ifp);
1911 exit(EXIT_FAILURE);
1913 if (!(tfp = fopen(tempfile, WRTMODE))) { /* oracles.tmp */
1914 perror(tempfile);
1915 Fclose(ifp);
1916 Fclose(ofp);
1917 Unlink(filename);
1918 exit(EXIT_FAILURE);
1921 /* output a dummy header record; we'll rewind and overwrite it later */
1922 Fprintf(ofp, "%s%5d\n", Dont_Edit_Data, 0);
1924 /* handle special oracle; it must come first */
1925 (void) fputs("---\n", tfp);
1926 offset = (unsigned long) ftell(tfp);
1927 Fprintf(ofp, "%05lx\n", offset); /* start pos of special oracle */
1928 for (i = 0; i < SIZE(special_oracle); i++) {
1929 (void) fputs(xcrypt(special_oracle[i]), tfp);
1930 (void) fputc('\n', tfp);
1932 SpinCursor(3);
1934 oracle_cnt = 1;
1935 (void) fputs("---\n", tfp);
1936 offset = (unsigned long) ftell(tfp);
1937 Fprintf(ofp, "%05lx\n", offset); /* start pos of first oracle */
1938 in_oracle = FALSE;
1940 while ((line = fgetline(ifp)) != 0) {
1941 SpinCursor(3);
1943 if (h_filter(line)) {
1944 free(line);
1945 continue;
1947 if (!strncmp(line, "-----", 5)) {
1948 if (!in_oracle) {
1949 free(line);
1950 continue;
1952 in_oracle = FALSE;
1953 oracle_cnt++;
1954 (void) fputs("---\n", tfp);
1955 offset = (unsigned long) ftell(tfp);
1956 Fprintf(ofp, "%05lx\n", offset); /* start pos of this oracle */
1957 } else {
1958 in_oracle = TRUE;
1959 (void) fputs(xcrypt(line), tfp);
1961 free(line);
1964 if (in_oracle) { /* need to terminate last oracle */
1965 oracle_cnt++;
1966 (void) fputs("---\n", tfp);
1967 offset = (unsigned long) ftell(tfp);
1968 Fprintf(ofp, "%05lx\n", offset); /* eof position */
1971 /* record the current position */
1972 txt_offset = (unsigned long) ftell(ofp);
1973 Fclose(ifp); /* all done with original input file */
1975 /* reprocess the scratch file; 1st format an error msg, just in case */
1976 line = malloc(256);
1977 Sprintf(line, "rewind of \"%s\"", tempfile);
1978 if (rewind(tfp) != 0)
1979 goto dead_data;
1980 free(line);
1981 /* copy all lines of text from the scratch file into the output file */
1982 while ((line = fgetline(tfp)) != 0) {
1983 (void) fputs(line, ofp);
1984 free(line);
1987 /* finished with scratch file */
1988 Fclose(tfp);
1989 Unlink(tempfile); /* remove it */
1991 /* update the first record of the output file; prepare error msg 1st */
1992 line = malloc(256);
1993 Sprintf(line, "rewind of \"%s\"", filename);
1994 ok = (rewind(ofp) == 0);
1995 if (ok) {
1996 Sprintf(line, "header rewrite of \"%s\"", filename);
1997 ok = (fprintf(ofp, "%s%5d\n", Dont_Edit_Data, oracle_cnt) >= 0);
1999 if (ok) {
2000 Sprintf(line, "data rewrite of \"%s\"", filename);
2001 for (i = 0; i <= oracle_cnt; i++) {
2002 #ifndef VMS /* alpha/vms v1.0; this fflush seems to confuse ftell */
2003 if (!(ok = (fflush(ofp) == 0)))
2004 break;
2005 #endif
2006 if (!(ok = (fpos = ftell(ofp)) >= 0))
2007 break;
2008 if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0)))
2009 break;
2010 if (!(ok = (fscanf(ofp, "%5lx", &offset) == 1)))
2011 break;
2012 #ifdef MAC
2013 #ifdef __MWERKS__
2015 MetroWerks CodeWarrior Pro 1's (AKA CW12) version of MSL
2016 (ANSI C Libraries) needs this rewind or else the fprintf
2017 stops working. This may also be true for CW11, but has
2018 never been checked.
2020 rewind(ofp);
2021 #endif
2022 #endif
2023 if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0)))
2024 break;
2025 offset += txt_offset;
2026 if (!(ok = (fprintf(ofp, "%05lx\n", offset) >= 0)))
2027 break;
2030 if (!ok) {
2031 dead_data:
2032 perror(line); /* report the problem */
2033 free(line);
2034 /* close and kill the aborted output file, then give up */
2035 Fclose(ofp);
2036 Unlink(filename);
2037 exit(EXIT_FAILURE);
2039 free(line);
2041 /* all done */
2042 Fclose(ofp);
2044 return;
2047 void
2048 do_dungeon()
2050 char *line;
2052 Sprintf(filename, DATA_IN_TEMPLATE, DGN_I_FILE);
2053 if (!(ifp = fopen(filename, RDTMODE))) {
2054 perror(filename);
2055 exit(EXIT_FAILURE);
2057 filename[0] = '\0';
2058 #ifdef FILE_PREFIX
2059 Strcat(filename, file_prefix);
2060 #endif
2061 Sprintf(eos(filename), DGN_TEMPLATE, DGN_O_FILE);
2062 if (!(ofp = fopen(filename, WRTMODE))) {
2063 perror(filename);
2064 exit(EXIT_FAILURE);
2066 Fprintf(ofp, "%s", Dont_Edit_Data);
2068 tfp = getfp(DATA_TEMPLATE, "grep.tmp", WRTMODE);
2069 grep0(ifp, tfp);
2070 ifp = getfp(DATA_TEMPLATE, "grep.tmp", RDTMODE);
2072 while ((line = fgetline(ifp)) != 0) {
2073 SpinCursor(3);
2075 if (line[0] == '#') {
2076 free(line);
2077 continue; /* discard comments */
2079 (void) fputs(line, ofp);
2080 free(line);
2082 Fclose(ifp);
2083 Fclose(ofp);
2085 delete_file(DATA_TEMPLATE, "grep.tmp");
2086 return;
2089 static boolean
2090 ranged_attk(ptr) /* returns TRUE if monster can attack at range */
2091 register struct permonst *ptr;
2093 register int i, j;
2094 register int atk_mask = (1 << AT_BREA) | (1 << AT_SPIT) | (1 << AT_GAZE);
2096 for (i = 0; i < NATTK; i++) {
2097 if ((j = ptr->mattk[i].aatyp) >= AT_WEAP || (atk_mask & (1 << j)))
2098 return TRUE;
2101 return FALSE;
2104 /* This routine is designed to return an integer value which represents
2105 * an approximation of monster strength. It uses a similar method of
2106 * determination as "experience()" to arrive at the strength.
2108 static int
2109 mstrength(ptr)
2110 struct permonst *ptr;
2112 int i, tmp2, n, tmp = ptr->mlevel;
2114 if (tmp > 49) /* special fixed hp monster */
2115 tmp = 2 * (tmp - 6) / 4;
2117 /* For creation in groups */
2118 n = (!!(ptr->geno & G_SGROUP));
2119 n += (!!(ptr->geno & G_LGROUP)) << 1;
2121 /* For ranged attacks */
2122 if (ranged_attk(ptr))
2123 n++;
2125 /* For higher ac values */
2126 n += (ptr->ac < 4);
2127 n += (ptr->ac < 0);
2129 /* For very fast monsters */
2130 n += (ptr->mmove >= 18);
2132 /* For each attack and "special" attack */
2133 for (i = 0; i < NATTK; i++) {
2134 tmp2 = ptr->mattk[i].aatyp;
2135 n += (tmp2 > 0);
2136 n += (tmp2 == AT_MAGC);
2137 n += (tmp2 == AT_WEAP && (ptr->mflags2 & M2_STRONG));
2140 /* For each "special" damage type */
2141 for (i = 0; i < NATTK; i++) {
2142 tmp2 = ptr->mattk[i].adtyp;
2143 if ((tmp2 == AD_DRLI) || (tmp2 == AD_STON) || (tmp2 == AD_DRST)
2144 || (tmp2 == AD_DRDX) || (tmp2 == AD_DRCO) || (tmp2 == AD_WERE))
2145 n += 2;
2146 else if (strcmp(ptr->mname, "grid bug"))
2147 n += (tmp2 != AD_PHYS);
2148 n += ((int) (ptr->mattk[i].damd * ptr->mattk[i].damn) > 23);
2151 /* Leprechauns are special cases. They have many hit dice so they can
2152 hit and are hard to kill, but they don't really do much damage. */
2153 if (!strcmp(ptr->mname, "leprechaun"))
2154 n -= 2;
2156 /* Finally, adjust the monster level 0 <= n <= 24 (approx.) */
2157 if (n == 0)
2158 tmp--;
2159 else if (n >= 6)
2160 tmp += (n / 2);
2161 else
2162 tmp += (n / 3 + 1);
2164 return (tmp >= 0) ? tmp : 0;
2167 void
2168 do_monstr()
2170 register struct permonst *ptr;
2171 register int i, j;
2174 * create the source file, "monstr.c"
2176 filename[0] = '\0';
2177 #ifdef FILE_PREFIX
2178 Strcat(filename, file_prefix);
2179 #endif
2180 Sprintf(eos(filename), SOURCE_TEMPLATE, MON_STR_C);
2181 if (!(ofp = fopen(filename, WRTMODE))) {
2182 perror(filename);
2183 exit(EXIT_FAILURE);
2185 Fprintf(ofp, "%s", Dont_Edit_Code);
2186 Fprintf(ofp, "#include \"config.h\"\n");
2187 Fprintf(ofp, "\nconst int monstr[] = {\n");
2188 for (ptr = &mons[0], j = 0; ptr->mlet; ptr++) {
2189 SpinCursor(3);
2191 i = mstrength(ptr);
2192 Fprintf(ofp, "%2d,%c", i, (++j & 15) ? ' ' : '\n');
2194 /* might want to insert a final 0 entry here instead of just newline */
2195 Fprintf(ofp, "%s};\n", (j & 15) ? "\n" : "");
2197 Fprintf(ofp, "\nvoid NDECL(monstr_init);\n");
2198 Fprintf(ofp, "\nvoid\n");
2199 Fprintf(ofp, "monstr_init()\n");
2200 Fprintf(ofp, "{\n");
2201 Fprintf(ofp, " return;\n");
2202 Fprintf(ofp, "}\n");
2203 Fprintf(ofp, "\n/*monstr.c*/\n");
2205 Fclose(ofp);
2206 return;
2209 void
2210 do_permonst()
2212 int i;
2213 char *c, *nam;
2215 filename[0] = '\0';
2216 #ifdef FILE_PREFIX
2217 Strcat(filename, file_prefix);
2218 #endif
2219 Sprintf(eos(filename), INCLUDE_TEMPLATE, MONST_FILE);
2220 if (!(ofp = fopen(filename, WRTMODE))) {
2221 perror(filename);
2222 exit(EXIT_FAILURE);
2224 Fprintf(ofp, "%s", Dont_Edit_Code);
2225 Fprintf(ofp, "#ifndef PM_H\n#define PM_H\n");
2227 if (strcmp(mons[0].mname, "playermon") != 0)
2228 Fprintf(ofp, "\n#define\tPM_PLAYERMON\t(-1)");
2230 for (i = 0; mons[i].mlet; i++) {
2231 SpinCursor(3);
2233 Fprintf(ofp, "\n#define\tPM_");
2234 if (mons[i].mlet == S_HUMAN && !strncmp(mons[i].mname, "were", 4))
2235 Fprintf(ofp, "HUMAN_");
2236 for (nam = c = tmpdup(mons[i].mname); *c; c++)
2237 if (*c >= 'a' && *c <= 'z')
2238 *c -= (char) ('a' - 'A');
2239 else if (*c < 'A' || *c > 'Z')
2240 *c = '_';
2241 Fprintf(ofp, "%s\t%d", nam, i);
2243 Fprintf(ofp, "\n\n#define\tNUMMONS\t%d\n", i);
2244 Fprintf(ofp, "\n#endif /* PM_H */\n");
2245 Fclose(ofp);
2246 return;
2249 /* Start of Quest text file processing. */
2250 #include "qtext.h"
2252 static struct qthdr qt_hdr;
2253 static struct msghdr msg_hdr[N_HDR];
2254 static struct qtmsg *curr_msg;
2256 static int qt_line;
2258 static boolean in_msg;
2259 #define NO_MSG 1 /* strlen of a null line returned by fgets() */
2261 static boolean
2262 qt_comment(s)
2263 char *s;
2265 if (s[0] == '#')
2266 return TRUE;
2267 return (boolean) (!in_msg && strlen(s) == NO_MSG);
2270 static boolean
2271 qt_control(s)
2272 char *s;
2274 return (boolean) (s[0] == '%' && (s[1] == 'C' || s[1] == 'E'));
2277 static int
2278 get_hdr(code)
2279 char *code;
2281 int i;
2283 for (i = 0; i < qt_hdr.n_hdr; i++)
2284 if (!strncmp(code, qt_hdr.id[i], LEN_HDR))
2285 return ++i;
2287 return 0;
2290 static boolean
2291 new_id(code)
2292 char *code;
2294 if (qt_hdr.n_hdr >= N_HDR) {
2295 Fprintf(stderr, OUT_OF_HEADERS, qt_line);
2296 return FALSE;
2299 strncpy(&qt_hdr.id[qt_hdr.n_hdr][0], code, LEN_HDR);
2300 msg_hdr[qt_hdr.n_hdr].n_msg = 0;
2301 qt_hdr.offset[qt_hdr.n_hdr++] = 0L;
2302 return TRUE;
2305 static boolean
2306 known_msg(num, id)
2307 int num, id;
2309 int i;
2311 for (i = 0; i < msg_hdr[num].n_msg; i++)
2312 if (msg_hdr[num].qt_msg[i].msgnum == id)
2313 return TRUE;
2315 return FALSE;
2318 static void
2319 new_msg(s, num, id)
2320 char *s;
2321 int num, id;
2323 struct qtmsg *qt_msg;
2325 if (msg_hdr[num].n_msg >= N_MSG) {
2326 Fprintf(stderr, OUT_OF_MESSAGES, qt_line);
2327 } else {
2328 qt_msg = &(msg_hdr[num].qt_msg[msg_hdr[num].n_msg++]);
2329 qt_msg->msgnum = id;
2330 qt_msg->delivery = s[2];
2331 qt_msg->offset = qt_msg->size = qt_msg->summary_size = 0L;
2333 curr_msg = qt_msg;
2337 /* check %E record for "[summary text]" that anethack can stuff into the
2338 message history buffer when delivering text via window instead of pline */
2339 static char *
2340 valid_qt_summary(s, parsing)
2341 char *s; /* end record: "%E" optionally followed by " [summary]" */
2342 boolean parsing; /* curr_msg is valid iff this is True */
2344 static char summary[BUFSZ];
2345 char *p;
2347 if (*s != '%' || *(s + 1) != 'E')
2348 return (char *) 0;
2349 if ((p = index(s, '[')) == 0)
2350 return (char *) 0;
2351 /* note: opening '[' and closing ']' will be retained in the output;
2352 anything after ']' will be discarded by putting a newline there */
2353 Strcpy(summary, p);
2355 /* have an opening bracket; summary[] holds it and all text that follows
2357 p = eos(summary);
2358 /* find closing bracket */
2359 while (p > summary && *(p - 1) != ']')
2360 --p;
2362 if (p == summary) {
2363 /* we backed up all the way to the start without finding a bracket */
2364 if (parsing) /* malformed summary */
2365 Fprintf(stderr, MAL_SUM, qt_line);
2366 } else if (p == summary + 1) {
2367 ; /* ignore empty [] */
2368 } else { /* got something */
2369 /* p points one spot past ']', usually to '\n';
2370 we need to include the \n as part of the size */
2371 if (parsing) {
2372 /* during the writing pass we won't be able to recheck
2373 delivery, so any useless summary for a pline mode
2374 message has to be carried along to the output file */
2375 if (curr_msg->delivery == 'p')
2376 Fprintf(stderr, DUMB_SUM, qt_line);
2377 /* +1 is for terminating newline */
2378 curr_msg->summary_size = (long) (p - summary) + 1L;
2379 } else {
2380 /* caller is writing rather than just parsing;
2381 force newline after the closing bracket */
2382 Strcpy(p, "\n");
2384 return summary;
2386 return (char *) 0;
2389 static void
2390 do_qt_control(s)
2391 char *s;
2393 char code[BUFSZ];
2394 int num, id = 0;
2396 if (!index(s, '\n'))
2397 Fprintf(stderr, CTRL_TRUNC, qt_line);
2399 switch (s[1]) {
2400 case 'C':
2401 if (in_msg) {
2402 Fprintf(stderr, CREC_IN_MSG, qt_line);
2403 break;
2404 } else {
2405 in_msg = TRUE;
2406 if (sscanf(&s[4], "%s %5d", code, &id) != 2) {
2407 Fprintf(stderr, UNREC_CREC, qt_line);
2408 break;
2410 num = get_hdr(code);
2411 if (!num && !new_id(code))
2412 break;
2413 num = get_hdr(code) - 1;
2414 if (known_msg(num, id))
2415 Fprintf(stderr, DUP_MSG, qt_line);
2416 else
2417 new_msg(s, num, id);
2419 break;
2421 case 'E':
2422 if (!in_msg) {
2423 Fprintf(stderr, END_NOT_IN_MSG, qt_line);
2424 } else {
2425 /* sets curr_msg->summary_size if applicable */
2426 (void) valid_qt_summary(s, TRUE);
2427 in_msg = FALSE;
2429 break;
2431 default:
2432 Fprintf(stderr, UNREC_CREC, qt_line);
2433 break;
2437 static void
2438 do_qt_text(s)
2439 char *s;
2441 if (!in_msg) {
2442 Fprintf(stderr, TEXT_NOT_IN_MSG, qt_line);
2443 } else if (!index(s, '\n')) {
2444 Fprintf(stderr, TEXT_TRUNC, qt_line);
2447 curr_msg->size += strlen(s);
2448 return;
2451 static void
2452 adjust_qt_hdrs()
2454 int i, j;
2455 long count = 0L, hdr_offset = sizeof(int)
2456 + (sizeof(char) * LEN_HDR + sizeof(long))
2457 * qt_hdr.n_hdr;
2459 for (i = 0; i < qt_hdr.n_hdr; i++) {
2460 qt_hdr.offset[i] = hdr_offset;
2461 hdr_offset += sizeof(int) + sizeof(struct qtmsg) * msg_hdr[i].n_msg;
2464 for (i = 0; i < qt_hdr.n_hdr; i++)
2465 for (j = 0; j < msg_hdr[i].n_msg; j++) {
2466 msg_hdr[i].qt_msg[j].offset = hdr_offset + count;
2467 count +=
2468 msg_hdr[i].qt_msg[j].size + msg_hdr[i].qt_msg[j].summary_size;
2470 return;
2473 static void
2474 put_qt_hdrs()
2476 int i;
2479 * The main header record.
2481 if (debug)
2482 Fprintf(stderr, "%ld: header info.\n", ftell(ofp));
2483 (void) fwrite((genericptr_t) & (qt_hdr.n_hdr), sizeof(int), 1, ofp);
2484 (void) fwrite((genericptr_t) & (qt_hdr.id[0][0]), sizeof(char) * LEN_HDR,
2485 qt_hdr.n_hdr, ofp);
2486 (void) fwrite((genericptr_t) & (qt_hdr.offset[0]), sizeof(long),
2487 qt_hdr.n_hdr, ofp);
2488 if (debug) {
2489 for (i = 0; i < qt_hdr.n_hdr; i++)
2490 Fprintf(stderr, "%s @ %ld, ", qt_hdr.id[i], qt_hdr.offset[i]);
2491 Fprintf(stderr, "\n");
2495 * The individual class headers.
2497 for (i = 0; i < qt_hdr.n_hdr; i++) {
2498 if (debug)
2499 Fprintf(stderr, "%ld: %s header info.\n", ftell(ofp),
2500 qt_hdr.id[i]);
2501 (void) fwrite((genericptr_t) & (msg_hdr[i].n_msg), sizeof(int), 1,
2502 ofp);
2503 (void) fwrite((genericptr_t) & (msg_hdr[i].qt_msg[0]),
2504 sizeof(struct qtmsg), msg_hdr[i].n_msg, ofp);
2505 if (debug) {
2506 int j;
2508 for (j = 0; j < msg_hdr[i].n_msg; j++) {
2509 Fprintf(stderr, "msg %d @ %ld (%ld)",
2510 msg_hdr[i].qt_msg[j].msgnum,
2511 msg_hdr[i].qt_msg[j].offset,
2512 msg_hdr[i].qt_msg[j].size);
2513 if (msg_hdr[i].qt_msg[j].summary_size)
2514 Fprintf(stderr, " [%ld]",
2515 msg_hdr[i].qt_msg[j].summary_size);
2516 Fprintf(stderr, "\n");
2522 void
2523 do_questtxt()
2525 char *line;
2527 Sprintf(filename, DATA_IN_TEMPLATE, QTXT_I_FILE);
2528 if (!(ifp = fopen(filename, RDTMODE))) {
2529 perror(filename);
2530 exit(EXIT_FAILURE);
2533 filename[0] = '\0';
2534 #ifdef FILE_PREFIX
2535 Strcat(filename, file_prefix);
2536 #endif
2537 Sprintf(eos(filename), DATA_TEMPLATE, QTXT_O_FILE);
2538 if (!(ofp = fopen(filename, WRBMODE))) {
2539 perror(filename);
2540 Fclose(ifp);
2541 exit(EXIT_FAILURE);
2544 qt_hdr.n_hdr = 0;
2545 qt_line = 0;
2546 in_msg = FALSE;
2548 while ((line = fgetline(ifp)) != 0) {
2549 SpinCursor(3);
2551 qt_line++;
2552 if (qt_control(line))
2553 do_qt_control(line);
2554 else if (qt_comment(line)) {
2555 free(line);
2556 continue;
2557 } else
2558 do_qt_text(line);
2559 free(line);
2562 (void) rewind(ifp);
2563 in_msg = FALSE;
2564 adjust_qt_hdrs();
2565 put_qt_hdrs();
2566 while ((line = fgetline(ifp)) != 0) {
2567 if (qt_control(line)) {
2568 char *summary_p = 0;
2570 in_msg = (line[1] == 'C');
2571 if (!in_msg)
2572 summary_p = valid_qt_summary(line, FALSE);
2573 /* don't write anything unless we've got a summary */
2574 if (!summary_p) {
2575 free(line);
2576 continue;
2578 /* we have summary text; replace raw %E record with it */
2579 Strcpy(line, summary_p); /* (guaranteed to fit) */
2580 } else if (qt_comment(line)) {
2581 free(line);
2582 continue;
2584 if (debug)
2585 Fprintf(stderr, "%ld: %s", ftell(stdout), line);
2586 (void) fputs(xcrypt(line), ofp);
2587 free(line);
2589 Fclose(ifp);
2590 Fclose(ofp);
2591 return;
2594 static char temp[32];
2596 static char *limit(name, pref) /* limit a name to 30 characters length */
2597 char *name;
2598 int pref;
2600 (void) strncpy(temp, name, pref ? 26 : 30);
2601 temp[pref ? 26 : 30] = 0;
2602 return temp;
2605 void
2606 do_objs()
2608 int i, sum = 0;
2609 char *c, *objnam;
2610 int nspell = 0;
2611 int prefix = 0;
2612 char class = '\0';
2613 boolean sumerr = FALSE;
2615 filename[0] = '\0';
2616 #ifdef FILE_PREFIX
2617 Strcat(filename, file_prefix);
2618 #endif
2619 Sprintf(eos(filename), INCLUDE_TEMPLATE, ONAME_FILE);
2620 if (!(ofp = fopen(filename, WRTMODE))) {
2621 perror(filename);
2622 exit(EXIT_FAILURE);
2624 Fprintf(ofp, "%s", Dont_Edit_Code);
2625 Fprintf(ofp, "#ifndef ONAMES_H\n#define ONAMES_H\n\n");
2627 for (i = 0; !i || objects[i].oc_class != ILLOBJ_CLASS; i++) {
2628 SpinCursor(3);
2630 objects[i].oc_name_idx = objects[i].oc_descr_idx = i; /* init */
2631 if (!(objnam = tmpdup(OBJ_NAME(objects[i]))))
2632 continue;
2634 /* make sure probabilities add up to 1000 */
2635 if (objects[i].oc_class != class) {
2636 if (sum && sum != 1000) {
2637 Fprintf(stderr, "prob error for class %d (%d%%)", class, sum);
2638 (void) fflush(stderr);
2639 sumerr = TRUE;
2641 class = objects[i].oc_class;
2642 sum = 0;
2645 for (c = objnam; *c; c++)
2646 if (*c >= 'a' && *c <= 'z')
2647 *c -= (char) ('a' - 'A');
2648 else if (*c < 'A' || *c > 'Z')
2649 *c = '_';
2651 switch (class) {
2652 case WAND_CLASS:
2653 Fprintf(ofp, "#define\tWAN_");
2654 prefix = 1;
2655 break;
2656 case RING_CLASS:
2657 Fprintf(ofp, "#define\tRIN_");
2658 prefix = 1;
2659 break;
2660 case POTION_CLASS:
2661 Fprintf(ofp, "#define\tPOT_");
2662 prefix = 1;
2663 break;
2664 case SPBOOK_CLASS:
2665 Fprintf(ofp, "#define\tSPE_");
2666 prefix = 1;
2667 nspell++;
2668 break;
2669 case SCROLL_CLASS:
2670 Fprintf(ofp, "#define\tSCR_");
2671 prefix = 1;
2672 break;
2673 case AMULET_CLASS:
2674 /* avoid trouble with stupid C preprocessors */
2675 Fprintf(ofp, "#define\t");
2676 if (objects[i].oc_material == PLASTIC) {
2677 Fprintf(ofp, "FAKE_AMULET_OF_YENDOR\t%d\n", i);
2678 prefix = -1;
2679 break;
2681 break;
2682 case GEM_CLASS:
2683 /* avoid trouble with stupid C preprocessors */
2684 if (objects[i].oc_material == GLASS) {
2685 Fprintf(ofp, "/* #define\t%s\t%d */\n", objnam, i);
2686 prefix = -1;
2687 break;
2689 default:
2690 Fprintf(ofp, "#define\t");
2692 if (prefix >= 0)
2693 Fprintf(ofp, "%s\t%d\n", limit(objnam, prefix), i);
2694 prefix = 0;
2696 sum += objects[i].oc_prob;
2699 /* check last set of probabilities */
2700 if (sum && sum != 1000) {
2701 Fprintf(stderr, "prob error for class %d (%d%%)", class, sum);
2702 (void) fflush(stderr);
2703 sumerr = TRUE;
2706 Fprintf(ofp, "#define\tLAST_GEM\t(JADE)\n");
2707 Fprintf(ofp, "#define\tMAXSPELL\t%d\n", nspell + 1);
2708 Fprintf(ofp, "#define\tNUM_OBJECTS\t%d\n", i);
2710 Fprintf(ofp, "\n/* Artifacts (unique objects) */\n\n");
2712 for (i = 1; artifact_names[i]; i++) {
2713 SpinCursor(3);
2715 for (c = objnam = tmpdup(artifact_names[i]); *c; c++)
2716 if (*c >= 'a' && *c <= 'z')
2717 *c -= (char) ('a' - 'A');
2718 else if (*c < 'A' || *c > 'Z')
2719 *c = '_';
2721 if (!strncmp(objnam, "THE_", 4))
2722 objnam += 4;
2723 /* fudge _platinum_ YENDORIAN EXPRESS CARD */
2724 if (!strncmp(objnam, "PLATINUM_", 9))
2725 objnam += 9;
2726 Fprintf(ofp, "#define\tART_%s\t%d\n", limit(objnam, 1), i);
2729 Fprintf(ofp, "#define\tNROFARTIFACTS\t%d\n", i - 1);
2730 Fprintf(ofp, "\n#endif /* ONAMES_H */\n");
2731 Fclose(ofp);
2732 if (sumerr)
2733 exit(EXIT_FAILURE);
2734 return;
2737 /* Read one line from input, up to and including the next newline
2738 * character. Returns a pointer to the heap-allocated string, or a
2739 * null pointer if no characters were read.
2741 static char *
2742 fgetline(fd)
2743 FILE *fd;
2745 static const int inc = 256;
2746 int len = inc;
2747 char *c = malloc(len), *ret;
2749 for (;;) {
2750 ret = fgets(c + len - inc, inc, fd);
2751 if (!ret) {
2752 free(c);
2753 c = NULL;
2754 break;
2755 } else if (index(c, '\n')) {
2756 /* normal case: we have a full line */
2757 break;
2759 len += inc;
2760 c = realloc(c, len);
2762 return c;
2765 static char *
2766 tmpdup(str)
2767 const char *str;
2769 static char buf[128];
2771 if (!str)
2772 return (char *) 0;
2773 (void) strncpy(buf, str, 127);
2774 return buf;
2777 static char *
2778 eos(str)
2779 char *str;
2781 while (*str)
2782 str++;
2783 return str;
2787 * macro used to control vision algorithms:
2788 * VISION_TABLES => generate tables
2791 void
2792 do_vision()
2794 #ifdef VISION_TABLES
2795 int i, j;
2797 /* Everything is clear. xclear may be malloc'ed.
2798 * Block the upper left corner (BLOCK_HEIGHTxBLOCK_WIDTH)
2800 for (i = 0; i < MAX_ROW; i++)
2801 for (j = 0; j < MAX_COL; j++)
2802 if (i < BLOCK_HEIGHT && j < BLOCK_WIDTH)
2803 xclear[i][j] = '\000';
2804 else
2805 xclear[i][j] = '\001';
2806 #endif /* VISION_TABLES */
2808 SpinCursor(3);
2811 * create the include file, "vis_tab.h"
2813 filename[0] = '\0';
2814 #ifdef FILE_PREFIX
2815 Strcat(filename, file_prefix);
2816 #endif
2817 Sprintf(filename, INCLUDE_TEMPLATE, VIS_TAB_H);
2818 if (!(ofp = fopen(filename, WRTMODE))) {
2819 perror(filename);
2820 exit(EXIT_FAILURE);
2822 Fprintf(ofp, "%s", Dont_Edit_Code);
2823 Fprintf(ofp, "#ifdef VISION_TABLES\n");
2824 #ifdef VISION_TABLES
2825 H_close_gen();
2826 H_far_gen();
2827 #endif /* VISION_TABLES */
2828 Fprintf(ofp, "\n#endif /* VISION_TABLES */\n");
2829 Fclose(ofp);
2831 SpinCursor(3);
2834 * create the source file, "vis_tab.c"
2836 filename[0] = '\0';
2837 #ifdef FILE_PREFIX
2838 Strcat(filename, file_prefix);
2839 #endif
2840 Sprintf(filename, SOURCE_TEMPLATE, VIS_TAB_C);
2841 if (!(ofp = fopen(filename, WRTMODE))) {
2842 perror(filename);
2843 Sprintf(filename, INCLUDE_TEMPLATE, VIS_TAB_H);
2844 Unlink(filename);
2845 exit(EXIT_FAILURE);
2847 Fprintf(ofp, "%s", Dont_Edit_Code);
2848 Fprintf(ofp, "#include \"config.h\"\n");
2849 Fprintf(ofp, "#ifdef VISION_TABLES\n");
2850 Fprintf(ofp, "#include \"vis_tab.h\"\n");
2852 SpinCursor(3);
2854 #ifdef VISION_TABLES
2855 C_close_gen();
2856 C_far_gen();
2857 Fprintf(ofp, "\nvoid vis_tab_init() { return; }\n");
2858 #endif /* VISION_TABLES */
2860 SpinCursor(3);
2862 Fprintf(ofp, "\n#endif /* VISION_TABLES */\n");
2863 Fprintf(ofp, "\n/*vis_tab.c*/\n");
2865 Fclose(ofp);
2866 return;
2869 #ifdef VISION_TABLES
2871 /*-------------- vision tables --------------*\
2873 * Generate the close and far tables. This is done by setting up a
2874 * fake dungeon and moving our source to different positions relative
2875 * to a block and finding the first/last visible position. The fake
2876 * dungeon is all clear execpt for the upper left corner (BLOCK_HEIGHT
2877 * by BLOCK_WIDTH) is blocked. Then we move the source around relative
2878 * to the corner of the block. For each new position of the source
2879 * we check positions on rows "kittycorner" from the source. We check
2880 * positions until they are either in sight or out of sight (depends on
2881 * which table we are generating). The picture below shows the setup
2882 * for the generation of the close table. The generation of the far
2883 * table would switch the quadrants of the '@' and the "Check rows
2884 * here".
2887 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2888 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2889 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,, Check rows here ,,,,,,,,,,,,
2890 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2891 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXB,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2892 * ...............................
2893 * ...............................
2894 * .........@.....................
2895 * ...............................
2897 * Table generation figure (close_table). The 'X's are blocked points.
2898 * The 'B' is a special blocked point. The '@' is the source. The ','s
2899 * are the target area. The '.' are just open areas.
2902 * Example usage of close_table[][][].
2904 * The table is as follows:
2906 * dy = |row of '@' - row of 'B'| - 1
2907 * dx = |col of '@' - col of 'B'|
2909 * The first indices are the deltas from the source '@' and the block 'B'.
2910 * You must check for the value inside the abs value bars being zero. If
2911 * so then the block is on the same row and you don't need to do a table
2912 * lookup. The last value:
2914 * dcy = |row of block - row to be checked|
2916 * Is the value of the first visible spot on the check row from the
2917 * block column. So
2919 * first visible col = close_table[dy][dx][dcy] + col of 'B'
2921 \*-------------- vision tables --------------*/
2923 static void
2924 H_close_gen()
2926 Fprintf(ofp, "\n/* Close */\n");
2927 Fprintf(ofp,
2928 "#define CLOSE_MAX_SB_DY %2d\t/* |src row - block row| - 1\t*/\n",
2929 TEST_HEIGHT - 1);
2930 Fprintf(ofp,
2931 "#define CLOSE_MAX_SB_DX %2d\t/* |src col - block col|\t*/\n",
2932 TEST_WIDTH);
2933 Fprintf(ofp,
2934 "#define CLOSE_MAX_BC_DY %2d\t/* |block row - check row|\t*/\n",
2935 TEST_HEIGHT);
2936 Fprintf(ofp, "typedef struct {\n");
2937 Fprintf(ofp,
2938 " unsigned char close[CLOSE_MAX_SB_DX][CLOSE_MAX_BC_DY];\n");
2939 Fprintf(ofp, "} close2d;\n");
2940 Fprintf(ofp, "extern close2d close_table[CLOSE_MAX_SB_DY];\n");
2941 return;
2944 static void
2945 H_far_gen()
2947 Fprintf(ofp, "\n/* Far */\n");
2948 Fprintf(ofp, "#define FAR_MAX_SB_DY %2d\t/* |src row - block row|\t*/\n",
2949 TEST_HEIGHT);
2950 Fprintf(ofp,
2951 "#define FAR_MAX_SB_DX %2d\t/* |src col - block col| - 1\t*/\n",
2952 TEST_WIDTH - 1);
2953 Fprintf(ofp,
2954 "#define FAR_MAX_BC_DY %2d\t/* |block row - check row| - 1\t*/\n",
2955 TEST_HEIGHT - 1);
2956 Fprintf(ofp, "typedef struct {\n");
2957 Fprintf(ofp, " unsigned char far_q[FAR_MAX_SB_DX][FAR_MAX_BC_DY];\n");
2958 Fprintf(ofp, "} far2d;\n");
2959 Fprintf(ofp, "extern far2d far_table[FAR_MAX_SB_DY];\n");
2960 return;
2963 static void
2964 C_close_gen()
2966 int i, dx, dy;
2967 int src_row, src_col; /* source */
2968 int block_row, block_col; /* block */
2969 int this_row;
2970 int no_more;
2971 const char *delim;
2973 block_row = BLOCK_HEIGHT - 1;
2974 block_col = BLOCK_WIDTH - 1;
2976 Fprintf(ofp, "\n#ifndef FAR_TABLE_ONLY\n");
2977 Fprintf(ofp, "\nclose2d close_table[CLOSE_MAX_SB_DY] = {\n");
2978 #ifndef no_vision_progress
2979 Fprintf(stderr, "\nclose:");
2980 #endif
2982 for (dy = 1; dy < TEST_HEIGHT; dy++) {
2983 src_row = block_row + dy;
2984 Fprintf(ofp, "/* DY = %2d (- 1)*/\n {{\n", dy);
2985 #ifndef no_vision_progress
2986 Fprintf(stderr, " %2d", dy), (void) fflush(stderr);
2987 #endif
2988 for (dx = 0; dx < TEST_WIDTH; dx++) {
2989 src_col = block_col - dx;
2990 Fprintf(ofp, " /*%2d*/ {", dx);
2992 no_more = 0;
2993 for (this_row = 0; this_row < TEST_HEIGHT; this_row++) {
2994 delim = (this_row < TEST_HEIGHT - 1) ? "," : "";
2995 if (no_more) {
2996 Fprintf(ofp, "%s%s", CLOSE_OFF_TABLE_STRING, delim);
2997 continue;
2999 SpinCursor(3);
3001 /* Find the first column that we can see. */
3002 for (i = block_col + 1; i < MAX_COL; i++) {
3003 if (clear_path(src_row, src_col, block_row - this_row, i))
3004 break;
3007 if (i == MAX_COL)
3008 no_more = 1;
3009 Fprintf(ofp, "%2d%s", i - block_col, delim);
3011 Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n");
3013 Fprintf(ofp, " }},\n");
3016 Fprintf(ofp, "}; /* close_table[] */\n"); /* closing brace for table */
3017 Fprintf(ofp, "#endif /* !FAR_TABLE_ONLY */\n");
3018 #ifndef no_vision_progress
3019 Fprintf(stderr, "\n");
3020 #endif
3021 return;
3024 static void
3025 C_far_gen()
3027 int i, dx, dy;
3028 int src_row, src_col; /* source */
3029 int block_row, block_col; /* block */
3030 int this_row;
3031 const char *delim;
3033 block_row = BLOCK_HEIGHT - 1;
3034 block_col = BLOCK_WIDTH - 1;
3036 Fprintf(ofp, "\n#ifndef CLOSE_TABLE_ONLY\n");
3037 Fprintf(ofp, "\nfar2d far_table[FAR_MAX_SB_DY] = {\n");
3038 #ifndef no_vision_progress
3039 Fprintf(stderr, "\n_far_:");
3040 #endif
3042 for (dy = 0; dy < TEST_HEIGHT; dy++) {
3043 src_row = block_row - dy;
3044 Fprintf(ofp, "/* DY = %2d */\n {{\n", dy);
3045 #ifndef no_vision_progress
3046 Fprintf(stderr, " %2d", dy), (void) fflush(stderr);
3047 #endif
3048 for (dx = 1; dx < TEST_WIDTH; dx++) {
3049 src_col = block_col + dx;
3050 Fprintf(ofp, " /*%2d(-1)*/ {", dx);
3052 for (this_row = block_row + 1; this_row < block_row + TEST_HEIGHT;
3053 this_row++) {
3054 delim = (this_row < block_row + TEST_HEIGHT - 1) ? "," : "";
3056 SpinCursor(3);
3057 /* Find first col that we can see. */
3058 for (i = 0; i <= block_col; i++) {
3059 if (clear_path(src_row, src_col, this_row, i))
3060 break;
3063 if (block_col - i < 0)
3064 Fprintf(ofp, "%s%s", FAR_OFF_TABLE_STRING, delim);
3065 else
3066 Fprintf(ofp, "%2d%s", block_col - i, delim);
3068 Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n");
3070 Fprintf(ofp, " }},\n");
3073 Fprintf(ofp, "}; /* far_table[] */\n"); /* closing brace for table */
3074 Fprintf(ofp, "#endif /* !CLOSE_TABLE_ONLY */\n");
3075 #ifndef no_vision_progress
3076 Fprintf(stderr, "\n");
3077 #endif
3078 return;
3082 * "Draw" a line from the hero to the given location. Stop if we hit a
3083 * wall.
3085 * Generalized integer Bresenham's algorithm (fast line drawing) for
3086 * all quadrants. From _Procedural Elements for Computer Graphics_, by
3087 * David F. Rogers. McGraw-Hill, 1985.
3089 * I have tried a little bit of optimization by pulling compares out of
3090 * the inner loops.
3092 * NOTE: This had better *not* be called from a position on the
3093 * same row as the hero.
3095 static int
3096 clear_path(you_row, you_col, y2, x2)
3097 int you_row, you_col, y2, x2;
3099 int dx, dy, s1, s2;
3100 register int i, error, x, y, dxs, dys;
3102 x = you_col;
3103 y = you_row;
3104 dx = abs(x2 - you_col);
3105 dy = abs(y2 - you_row);
3106 s1 = sign(x2 - you_col);
3107 s2 = sign(y2 - you_row);
3109 if (s1 == 0) { /* same column */
3110 if (s2 == 1) { /* below (larger y2 value) */
3111 for (i = you_row + 1; i < y2; i++)
3112 if (!xclear[i][you_col])
3113 return 0;
3114 } else { /* above (smaller y2 value) */
3115 for (i = y2 + 1; i < you_row; i++)
3116 if (!xclear[i][you_col])
3117 return 0;
3119 return 1;
3123 * Lines at 0 and 90 degrees have been weeded out.
3125 if (dy > dx) {
3126 error = dx;
3127 dx = dy;
3128 dy = error; /* swap the values */
3129 dxs = dx << 1; /* save the shifted values */
3130 dys = dy << 1;
3131 error = dys - dx; /* NOTE: error is used as a temporary above */
3133 for (i = 0; i < dx; i++) {
3134 if (!xclear[y][x])
3135 return 0; /* plot point */
3137 while (error >= 0) {
3138 x += s1;
3139 error -= dxs;
3141 y += s2;
3142 error += dys;
3144 } else {
3145 dxs = dx << 1; /* save the shifted values */
3146 dys = dy << 1;
3147 error = dys - dx;
3149 for (i = 0; i < dx; i++) {
3150 if (!xclear[y][x])
3151 return 0; /* plot point */
3153 while (error >= 0) {
3154 y += s2;
3155 error -= dxs;
3157 x += s1;
3158 error += dys;
3161 return 1;
3163 #endif /* VISION_TABLES */
3165 #ifdef STRICT_REF_DEF
3166 NEARDATA struct flag flags;
3167 #ifdef ATTRIB_H
3168 struct attribs attrmax, attrmin;
3169 #endif
3170 #endif /* STRICT_REF_DEF */
3172 /*makedefs.c*/