Allow quickly moving cursor on monsters
[aNetHack.git] / util / makedefs.c
blob0e340b47850611d1ce0e7f20faa54decf7aa9a8d
1 /* NetHack 3.6 makedefs.c $NHDT-Date: 1447062431 2015/11/09 09:47:11 $ $NHDT-Branch: master $:$NHDT-Revision: 1.105 $ */
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.5\t2004/02/01";
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(make_version);
168 static char *FDECL(version_string, (char *, const char *));
169 static char *FDECL(version_id_string, (char *, const char *));
170 static char *FDECL(bannerc_string, (char *, const char *));
171 static char *FDECL(xcrypt, (const char *));
172 static unsigned long FDECL(read_rumors_file,
173 (const char *, int *, long *, unsigned long));
174 static void FDECL(do_rnd_access_file, (const char *));
175 static boolean FDECL(d_filter, (char *));
176 static boolean FDECL(h_filter, (char *));
177 static boolean FDECL(ranged_attk, (struct permonst *));
178 static int FDECL(mstrength, (struct permonst *));
179 static void NDECL(build_savebones_compat_string);
180 static void FDECL(do_ext_makedefs, (int, char **));
181 static void NDECL(windowing_sanity);
183 static boolean FDECL(qt_comment, (char *));
184 static boolean FDECL(qt_control, (char *));
185 static int FDECL(get_hdr, (char *));
186 static boolean FDECL(new_id, (char *));
187 static boolean FDECL(known_msg, (int, int));
188 static void FDECL(new_msg, (char *, int, int));
189 static char *FDECL(valid_qt_summary, (char *, BOOLEAN_P));
190 static void FDECL(do_qt_control, (char *));
191 static void FDECL(do_qt_text, (char *));
192 static void NDECL(adjust_qt_hdrs);
193 static void NDECL(put_qt_hdrs);
195 #ifdef VISION_TABLES
196 static void NDECL(H_close_gen);
197 static void NDECL(H_far_gen);
198 static void NDECL(C_close_gen);
199 static void NDECL(C_far_gen);
200 static int FDECL(clear_path, (int, int, int, int));
201 #endif
203 static char *FDECL(fgetline, (FILE*));
204 static char *FDECL(tmpdup, (const char *));
205 static char *FDECL(limit, (char *, int));
206 static char *FDECL(eos, (char *));
208 /* input, output, tmp */
209 static FILE *ifp, *ofp, *tfp;
211 #if defined(__BORLANDC__) && !defined(_WIN32)
212 extern unsigned _stklen = STKSIZ;
213 #endif
215 #ifdef MACsansMPWTOOL
217 main(void)
219 const char *def_options = "odemvpqrshz";
220 char buf[100];
221 int len;
223 printf("Enter options to run: [%s] ", def_options);
224 fflush(stdout);
225 fgets(buf, 100, stdin);
226 len = strlen(buf);
227 if (len <= 1)
228 Strcpy(buf, def_options);
229 else
230 buf[len - 1] = 0; /* remove return */
232 if (buf[0] == '-' && buf[1] == '-') {
233 #if 0
234 split up buf into words
235 do_ext_makedefs(fakeargc, fakeargv);
236 #else
237 printf("extended makedefs not implemented for Mac OS9\n");
238 exit(EXIT_FAILURE);
239 #endif
242 do_makedefs(buf);
243 exit(EXIT_SUCCESS);
244 return 0;
247 #else /* ! MAC */
250 main(argc, argv)
251 int argc;
252 char *argv[];
254 if ((argc != 2)
255 #ifdef FILE_PREFIX
256 && (argc != 3)
257 #endif
258 && !(argv[1][0] == '-' && argv[1][1] == '-')) {
259 Fprintf(stderr, "Bad arg count (%d).\n", argc - 1);
260 (void) fflush(stderr);
261 return 1;
264 #ifdef FILE_PREFIX
265 if (argc >= 2 && argv[1][0] != '-') {
266 file_prefix = argv[1];
267 argc--;
268 argv++;
270 #endif
272 if (argv[1][0] == '-' && argv[1][1] == '-') {
273 do_ext_makedefs(argc, argv);
274 } else {
275 do_makedefs(&argv[1][1]);
277 exit(EXIT_SUCCESS);
278 /*NOTREACHED*/
279 return 0;
282 #endif
284 static void
285 link_sanity_check()
287 /* Note: these initializers don't do anything except guarantee that
288 we're linked properly.
290 monst_init();
291 objects_init();
294 void
295 do_makedefs(options)
296 char *options;
298 boolean more_than_one;
300 link_sanity_check();
302 /* construct the current version number */
303 make_version();
305 more_than_one = strlen(options) > 1;
306 while (*options) {
307 if (more_than_one)
308 Fprintf(stderr, "makedefs -%c\n", *options);
310 switch (*options) {
311 case 'o':
312 case 'O':
313 do_objs();
314 break;
315 case 'd':
316 case 'D':
317 do_data();
318 break;
319 case 'e':
320 case 'E':
321 do_dungeon();
322 break;
323 case 'm':
324 case 'M':
325 do_monstr();
326 break;
327 case 'v':
328 case 'V':
329 do_date();
330 do_options();
331 break;
332 case 'p':
333 case 'P':
334 do_permonst();
335 break;
336 case 'q':
337 case 'Q':
338 do_questtxt();
339 break;
340 case 'r':
341 case 'R':
342 do_rumors();
343 break;
344 case 's':
345 case 'S':
346 do_rnd_access_file(EPITAPHFILE);
347 do_rnd_access_file(ENGRAVEFILE);
348 do_rnd_access_file(BOGUSMONFILE);
349 break;
350 case 'h':
351 case 'H':
352 do_oracles();
353 break;
354 case 'z':
355 case 'Z':
356 do_vision();
357 break;
359 default:
360 Fprintf(stderr, "Unknown option '%c'.\n", *options);
361 (void) fflush(stderr);
362 exit(EXIT_FAILURE);
364 options++;
366 if (more_than_one)
367 Fprintf(stderr, "Completed.\n"); /* feedback */
370 static char namebuf[1000];
371 static char *
372 name_file(template, tag)
373 char *template;
374 char *tag;
376 Sprintf(namebuf, template, tag);
377 return namebuf;
380 static void
381 delete_file(template, tag)
382 char *template;
383 char *tag;
385 char *name = name_file(template, tag);
386 Unlink(name);
389 static FILE *
390 getfp(template, tag, mode)
391 char *template;
392 char *tag;
393 char *mode;
395 char *name = name_file(template, tag);
396 FILE *rv = fopen(name, mode);
397 if (!rv) {
398 Fprintf(stderr, "Can't open '%s'.\n", name);
399 exit(EXIT_FAILURE);
401 return rv;
404 static boolean debug = FALSE;
406 static FILE *inputfp;
407 static FILE *outputfp;
409 struct grep_var {
410 const char *name;
411 int is_defined; /* 0 undef; 1 defined */
413 /* struct grep_var grep_vars[] and TODO_* constants in include file: */
414 #include "mdgrep.h"
416 static void NDECL(do_grep);
417 static void NDECL(do_grep_showvars);
418 static struct grep_var *FDECL(grepsearch, (char *));
419 static int grep_trace = 0;
421 static void
422 do_ext_makedefs(int argc, char **argv)
424 int todo = 0;
426 link_sanity_check();
428 argc--;
429 argv++; /* skip program name */
431 while (argc) {
432 if (argv[0][0] != '-')
433 break;
434 if (argv[0][1] != '-') {
435 Fprintf(stderr, "Can't mix - and -- options.\n");
436 exit(EXIT_FAILURE);
438 #define IS_OPTION(str) if (!strcmp(&argv[0][2], str))
439 #define CONTINUE \
440 argv++, argc--; \
441 continue
442 #define CONSUME \
443 argv++, argc--; \
444 if (argc == 0) { \
445 Fprintf(stderr, "missing option\n"); \
446 exit(EXIT_FAILURE); \
448 IS_OPTION("svs")
450 /* short version string for packaging - note
451 * no \n */
452 char buf[100];
453 char delim[10];
454 argv++; /* not CONSUME */
455 delim[0] = '\0';
456 if (argv[0])
457 strcpy(delim, argv[0]);
458 Fprintf(stdout, "%s", version_string(buf, delim));
459 exit(EXIT_SUCCESS);
461 IS_OPTION("debug")
463 debug = TRUE;
464 CONTINUE;
466 IS_OPTION("make")
468 CONSUME;
469 do_makedefs(argv[0]);
470 exit(EXIT_SUCCESS);
472 IS_OPTION("input")
474 CONSUME;
475 if (!strcmp(argv[0], "-")) {
476 inputfp = stdin;
477 } else {
478 inputfp = fopen(argv[0], RDTMODE);
479 if (!inputfp) {
480 Fprintf(stderr, "Can't open '%s'.\n", argv[0]);
481 exit(EXIT_FAILURE);
484 CONTINUE;
486 IS_OPTION("output")
488 CONSUME;
489 if (!strcmp(argv[0], "-")) {
490 outputfp = stdout;
491 } else {
492 outputfp = fopen(argv[0], WRTMODE);
493 if (!outputfp) {
494 Fprintf(stderr, "Can't open '%s'.\n", argv[0]);
495 exit(EXIT_FAILURE);
498 CONTINUE;
500 IS_OPTION("grep")
502 if (todo) {
503 Fprintf(stderr, "Can't do grep and something else.\n");
504 exit(EXIT_FAILURE);
506 todo = TODO_GREP;
507 CONTINUE;
509 IS_OPTION("grep-showvars")
511 do_grep_showvars();
512 exit(EXIT_SUCCESS);
514 IS_OPTION("grep-trace")
516 grep_trace = 1;
517 CONTINUE;
519 IS_OPTION("grep-define")
521 struct grep_var *p;
522 CONSUME;
523 p = grepsearch(argv[0]);
524 if (p) {
525 p->is_defined = 1;
526 } else {
527 Fprintf(stderr, "Unknown symbol '%s'\n", argv[0]);
528 exit(EXIT_FAILURE);
530 CONTINUE;
532 IS_OPTION("grep-undef")
534 struct grep_var *p;
535 CONSUME;
536 p = grepsearch(argv[0]);
537 if (p) {
538 p->is_defined = 0;
539 } else {
540 Fprintf(stderr, "Unknown symbol '%s'\n", argv[0]);
541 exit(EXIT_FAILURE);
543 CONTINUE;
545 #ifdef notyet
546 IS_OPTION("help")
549 #endif
550 #undef IS_OPTION
551 Fprintf(stderr, "Unknown option '%s'.\n", argv[0]);
552 exit(EXIT_FAILURE);
554 if (argc) {
555 Fprintf(stderr, "unexpected argument '%s'.\n", argv[0]);
556 exit(EXIT_FAILURE);
559 switch (todo) {
560 default:
561 Fprintf(stderr, "Confused about what to do?\n");
562 exit(EXIT_FAILURE);
563 case 0:
564 Fprintf(stderr, "Nothing to do?\n");
565 exit(EXIT_FAILURE);
566 case TODO_GREP:
567 do_grep();
568 break;
573 Filtering syntax:
574 Any line NOT starting with a caret is either suppressed or passed through
575 unchanged depending on the current conditional state.
577 The default conditional state is printing on.
579 Conditionals may be nested.
581 makedefs will exit with a EXIT_FAILURE if any errors are detected; as many
582 errors as possible are detected before giving up.
584 Unknown identifiers are treated as TRUE and also as an error to allow
585 processing to continue past the unknown identifier (note that "#undef" is
586 different than unknown).
588 Any line starting with a caret is a control line; as in C, zero or more
589 spaces
590 may be embedded in the line almost anywhere; the caret MUST be in column 1.
591 (XXX for the moment, no white space is allowed after the caret because
592 existing lines in the docs look like that)
594 Control lines:
595 ^^ a line starting with a (single) literal caret
596 ^# a comment - the line is ignored
597 ^?ID if defined(ID)
598 ^!ID if !defined(ID)
599 ^: else
600 ^. endif
603 #define GREP_MAGIC '^'
604 #define GREP_STACK_SIZE 100
605 #ifdef notyet
606 static int grep_rewrite = 0; /* need to (possibly) rewrite lines */
607 #endif
608 static int grep_writing = 1; /* need to copy lines to output */
609 static int grep_errors = 0;
610 static int grep_sp = 0;
611 #define ST_LD(old, opp) (!!(old) | (!!(opp) << 1))
612 #define ST_OLD(v) ((v) &1)
613 #define ST_OPP(v) !!((v) &2)
614 #define ST_ELSE 4
615 static int grep_stack[GREP_STACK_SIZE] = { ST_LD(1, 0) };
616 static int grep_lineno = 0;
618 static void
619 do_grep_showvars()
621 int x;
622 for (x = 0; x < SIZE(grep_vars) - 1; x++) {
623 printf("%d\t%s\n", grep_vars[x].is_defined, grep_vars[x].name);
627 static struct grep_var *
628 grepsearch(name)
629 char *name;
631 /* XXX make into binary search */
632 int x = 0;
633 while (x < SIZE(grep_vars) - 1) {
634 if (!strcmp(grep_vars[x].name, name))
635 return &grep_vars[x];
636 x++;
638 return 0;
641 static int
642 grep_check_id(id)
643 char *id;
645 struct grep_var *rv;
646 while (*id && isspace(*id))
647 id++;
648 if (!*id) {
649 Fprintf(stderr, "missing identifier in line %d", grep_lineno);
650 grep_errors++;
651 return 0;
653 rv = grepsearch(id);
654 if (rv) {
655 if (grep_trace) {
656 Fprintf(outputfp, "ID %d %s\n", rv->is_defined, id);
658 return rv->is_defined;
661 if (grep_trace) {
662 Fprintf(outputfp, "ID U %s\n", id);
664 Fprintf(stderr, "unknown identifier '%s' in line %d.\n", id, grep_lineno);
665 grep_errors++;
666 return 2; /* So new features can be checked before makedefs
667 * is rebuilt. */
670 static void
671 grep_show_wstack(tag)
672 char *tag;
674 int x;
676 if (!grep_trace)
677 return;
679 Fprintf(outputfp, "%s w=%d sp=%d\t", tag, grep_writing, grep_sp);
680 for (x = grep_sp; x >= 0 && x > grep_sp - 6; x--) {
681 Fprintf(outputfp, "[%d]=%d ", x, grep_stack[x]);
683 Fprintf(outputfp, "\n");
686 static char *
687 do_grep_control(buf)
688 char *buf;
690 int isif = 1;
691 char *buf0 = buf;
692 #if 1
693 if (isspace(buf[0]))
694 return &buf[-1]; /* XXX see docs above */
695 #else
696 while (buf[0] && isspace(buf[0]))
697 buf++;
698 #endif
699 switch (buf[0]) {
700 case '#': /* comment */
701 break;
702 case '.': /* end of if level */
703 if (grep_sp == 0) {
704 Fprintf(stderr, "unmatched ^. (endif) at line %d.\n",
705 grep_lineno);
706 grep_errors++;
707 } else {
708 grep_writing = ST_OLD(grep_stack[grep_sp--]);
709 grep_show_wstack("pop");
711 break;
712 case '!': /* if not ID */
713 isif = 0;
714 /* FALLTHROUGH */
715 case '?': /* if ID */
716 if (grep_sp == GREP_STACK_SIZE - 2) {
717 Fprintf(stderr, "stack overflow at line %d.", grep_lineno);
718 exit(EXIT_FAILURE);
720 if (grep_writing) {
721 isif = grep_check_id(&buf[1]) ? isif : !isif;
722 grep_stack[++grep_sp] = ST_LD(grep_writing, !isif);
723 grep_writing = isif;
724 } else {
725 grep_stack[++grep_sp] = ST_LD(0, 0);
726 /* grep_writing = 0; */
728 grep_show_wstack("push");
729 break;
730 case ':': /* else */
731 if (ST_ELSE & grep_stack[grep_sp]) {
732 Fprintf(stderr, "multiple : for same conditional at line %d.\n",
733 grep_lineno);
734 grep_errors++;
736 grep_writing = ST_OPP(grep_stack[grep_sp]);
737 grep_stack[grep_sp] |= ST_ELSE;
738 break;
739 #if defined(notyet)
740 case '(': /* start of expression */
741 #endif
742 case GREP_MAGIC: /* ^^ -> ^ */
743 return buf0;
744 default: {
745 char str[10];
746 if (isprint(buf[0])) {
747 str[0] = buf[0];
748 str[1] = '\0';
749 } else {
750 sprintf(str, "0x%02x", buf[0]);
752 Fprintf(stderr, "unknown control ^%s at line %d.\n", str,
753 grep_lineno);
754 grep_errors++;
755 } break;
757 return NULL;
760 #ifdef notyet
761 static void
762 do_grep_rewrite(buf)
763 char *buf;
765 /* no language features use this yet */
766 return;
768 #endif
770 static void grep0(FILE *, FILE *);
772 static void
773 do_grep()
775 if (!inputfp) {
776 Fprintf(stderr, "--grep requires --input\n");
778 if (!outputfp) {
779 Fprintf(stderr, "--grep requires --output\n");
781 if (!inputfp || !outputfp) {
782 exit(EXIT_FAILURE);
785 grep0(inputfp, outputfp);
788 static void
789 grep0(inputfp0, outputfp0)
790 FILE *inputfp0;
791 FILE *outputfp0;
793 char buf[16384]; /* looong, just in case */
795 while (!feof(inputfp0) && !ferror(inputfp0)) {
796 char *tmp;
797 char *buf1;
799 if (fgets(buf, sizeof(buf), inputfp0) == 0)
800 break;
801 if ((tmp = strchr(buf, '\n')))
802 *tmp = '\0';
803 grep_lineno++;
804 if (grep_trace) {
805 Fprintf(outputfp0, "%04d %c >%s\n", grep_lineno,
806 grep_writing ? ' ' : '#', buf);
809 if (buf[0] == GREP_MAGIC) {
810 buf1 = do_grep_control(&buf[1]);
811 if (!buf1)
812 continue;
813 } else {
814 buf1 = buf;
816 #ifdef notyet
817 if (grep_rewrite)
818 do_grep_rewrite(buf1);
819 #endif
820 if (grep_writing)
821 Fprintf(outputfp0, "%s\n", buf1);
823 if (ferror(inputfp0)) {
824 Fprintf(stderr, "read error!\n");
825 exit(EXIT_FAILURE);
827 if (ferror(outputfp0)) {
828 Fprintf(stderr, "write error!\n");
829 exit(EXIT_FAILURE);
831 fclose(inputfp0);
832 fclose(outputfp0);
833 if (grep_sp) {
834 Fprintf(stderr, "%d unterminated conditional level%s\n", grep_sp,
835 grep_sp == 1 ? "" : "s");
836 grep_errors++;
838 if (grep_errors) {
839 Fprintf(stderr, "%d error%s detected.\n", grep_errors,
840 grep_errors == 1 ? "" : "s");
841 exit(EXIT_FAILURE);
845 /* trivial text encryption routine which can't be broken with `tr' */
846 static char *
847 xcrypt(str)
848 const char *str;
849 { /* duplicated in src/hacklib.c */
850 static char buf[BUFSZ];
851 register const char *p;
852 register char *q;
853 register int bitmask;
855 for (bitmask = 1, p = str, q = buf; *p; q++) {
856 *q = *p++;
857 if (*q & (32 | 64))
858 *q ^= bitmask;
859 if ((bitmask <<= 1) >= 32)
860 bitmask = 1;
862 *q = '\0';
863 return buf;
866 #define PAD_RUMORS_TO 60
867 /* common code for do_rumors(). Return 0 on error. */
868 static unsigned long
869 read_rumors_file(file_ext, rumor_count, rumor_size, old_rumor_offset)
870 const char *file_ext;
871 int *rumor_count;
872 long *rumor_size;
873 unsigned long old_rumor_offset;
875 char infile[600];
876 char *line;
877 unsigned long rumor_offset;
879 Sprintf(infile, DATA_IN_TEMPLATE, RUMOR_FILE);
880 Strcat(infile, file_ext);
881 if (!(ifp = fopen(infile, RDTMODE))) {
882 perror(infile);
883 return 0L;
886 /* copy the rumors */
887 while ((line = fgetline(ifp)) != 0) {
888 #ifdef PAD_RUMORS_TO
889 /* rumor selection is accomplished by seeking to a random
890 position in the file, advancing to newline, and taking
891 the next line; therefore, rumors which follow long-line
892 rumors are most likely to be chosen and rumors which
893 follow short-line rumors are least likely to be chosen;
894 we ameliorate the latter by padding the shortest lines,
895 increasing the chance of the random seek landing in them */
896 int len = (int) strlen(line);
898 if (len <= PAD_RUMORS_TO) {
899 char *base = index(line, '\n');
900 /* this is only safe because fgetline() overallocates */
901 while (len++ < PAD_RUMORS_TO) {
902 *base++ = '_';
904 *base++ = '\n';
905 *base = '\0';
907 #endif
908 (*rumor_count)++;
909 #if 0
910 /*[if we forced binary output, this would be sufficient]*/
911 *rumor_size += strlen(line); /* includes newline */
912 #endif
913 (void) fputs(xcrypt(line), tfp);
914 free(line);
916 /* record the current position; next rumors section will start here */
917 rumor_offset = (unsigned long) ftell(tfp);
918 Fclose(ifp); /* all done with rumors.file_ext */
920 /* the calculated value for *_rumor_count assumes that
921 a single-byte line terminator is in use; for platforms
922 which use two byte CR+LF, we need to override that value
923 [it's much simpler to do so unconditionally, rendering
924 the loop's accumulation above obsolete] */
925 *rumor_size = (long) (rumor_offset - old_rumor_offset);
926 return rumor_offset;
929 void
930 do_rnd_access_file(fname)
931 const char *fname;
933 char *line;
935 Sprintf(filename, DATA_IN_TEMPLATE, fname);
936 Strcat(filename, ".txt");
937 if (!(ifp = fopen(filename, RDTMODE))) {
938 perror(filename);
939 exit(EXIT_FAILURE);
941 filename[0] = '\0';
942 #ifdef FILE_PREFIX
943 Strcat(filename, file_prefix);
944 #endif
945 Sprintf(eos(filename), DATA_TEMPLATE, fname);
946 if (!(ofp = fopen(filename, WRTMODE))) {
947 perror(filename);
948 exit(EXIT_FAILURE);
950 Fprintf(ofp, "%s", Dont_Edit_Data);
952 tfp = getfp(DATA_TEMPLATE, "grep.tmp", WRTMODE);
953 grep0(ifp, tfp);
954 ifp = getfp(DATA_TEMPLATE, "grep.tmp", RDTMODE);
956 while ((line = fgetline(ifp)) != 0) {
957 if (line[0] != '#' && line[0] != '\n')
958 (void) fputs(xcrypt(line), ofp);
959 free(line);
961 Fclose(ifp);
962 Fclose(ofp);
964 delete_file(DATA_TEMPLATE, "grep.tmp");
965 return;
968 void
969 do_rumors()
971 char *line;
972 static const char rumors_header[] =
973 "%s%04d,%06ld,%06lx;%04d,%06ld,%06lx;0,0,%06lx\n";
974 char tempfile[600];
975 int true_rumor_count, false_rumor_count;
976 long true_rumor_size, false_rumor_size;
977 unsigned long true_rumor_offset, false_rumor_offset, eof_offset;
979 Sprintf(tempfile, DATA_TEMPLATE, "rumors.tmp");
980 filename[0] = '\0';
981 #ifdef FILE_PREFIX
982 Strcat(filename, file_prefix);
983 #endif
984 Sprintf(eos(filename), DATA_TEMPLATE, RUMOR_FILE);
985 if (!(ofp = fopen(filename, WRTMODE))) {
986 perror(filename);
987 exit(EXIT_FAILURE);
989 if (!(tfp = fopen(tempfile, WRTMODE))) {
990 perror(tempfile);
991 Fclose(ofp);
992 exit(EXIT_FAILURE);
995 true_rumor_count = false_rumor_count = 0;
996 true_rumor_size = false_rumor_size = 0L;
997 true_rumor_offset = false_rumor_offset = eof_offset = 0L;
999 /* output a dummy header record; we'll replace it in final output */
1000 Fprintf(tfp, rumors_header, Dont_Edit_Data, true_rumor_count,
1001 true_rumor_size, true_rumor_offset, false_rumor_count,
1002 false_rumor_size, false_rumor_offset, eof_offset);
1003 /* record the current position; true rumors will start here */
1004 true_rumor_offset = ftell(tfp);
1006 false_rumor_offset = read_rumors_file(
1007 ".tru", &true_rumor_count, &true_rumor_size, true_rumor_offset);
1008 if (!false_rumor_offset)
1009 goto rumors_failure;
1011 eof_offset = read_rumors_file(".fal", &false_rumor_count,
1012 &false_rumor_size, false_rumor_offset);
1013 if (!eof_offset)
1014 goto rumors_failure;
1016 /* get ready to transfer the contents of temp file to output file */
1017 line = malloc(256);
1018 Sprintf(line, "rewind of \"%s\"", tempfile);
1019 if (rewind(tfp) != 0) {
1020 perror(line);
1021 free(line);
1022 goto rumors_failure;
1024 free(line);
1026 /* output the header record */
1027 Fprintf(ofp, rumors_header, Dont_Edit_Data, true_rumor_count,
1028 true_rumor_size, true_rumor_offset, false_rumor_count,
1029 false_rumor_size, false_rumor_offset, eof_offset);
1030 /* skip the temp file's dummy header */
1031 if (!(line = fgetline(tfp))) { /* "Don't Edit" */
1032 perror(tempfile);
1033 goto rumors_failure;
1035 free(line);
1036 if (!(line = fgetline(tfp))) { /* count,size,offset */
1037 perror(tempfile);
1038 goto rumors_failure;
1040 free(line);
1041 /* copy the rest of the temp file into the final output file */
1042 while ((line = fgetline(tfp)) != 0) {
1043 (void) fputs(line, ofp);
1044 free(line);
1046 /* all done; delete temp file */
1047 Fclose(tfp);
1048 Unlink(tempfile);
1049 Fclose(ofp);
1050 return;
1052 rumors_failure:
1053 Fclose(ofp);
1054 Unlink(filename); /* kill empty or incomplete output file */
1055 Fclose(tfp);
1056 Unlink(tempfile); /* and temporary file */
1057 exit(EXIT_FAILURE);
1061 * Use this to explicitly mask out features during version checks.
1063 * ZEROCOMP, RLECOMP, and ZLIB_COMP describe compression features
1064 * that the port/plaform which wrote the savefile was capable of
1065 * dealing with. Don't reject a savefile just because the port
1066 * reading the savefile doesn't match on all/some of them.
1067 * The actual compression features used to produce the savefile are
1068 * recorded in the savefile_info structure immediately following the
1069 * version_info, and that is what needs to be checked against the
1070 * feature set of the port that is reading the savefile back in.
1071 * That check is done in src/restore.c now.
1074 #define IGNORED_FEATURES \
1075 (0L | (1L << 19) /* SCORE_ON_BOTL */ \
1076 | (1L << 27) /* ZEROCOMP */ \
1077 | (1L << 28) /* RLECOMP */ \
1080 static void
1081 make_version()
1083 register int i;
1086 * integer version number
1088 version.incarnation = ((unsigned long) VERSION_MAJOR << 24)
1089 | ((unsigned long) VERSION_MINOR << 16)
1090 | ((unsigned long) PATCHLEVEL << 8)
1091 | ((unsigned long) EDITLEVEL);
1093 * encoded feature list
1094 * Note: if any of these magic numbers are changed or reassigned,
1095 * EDITLEVEL in patchlevel.h should be incremented at the same time.
1096 * The actual values have no special meaning, and the category
1097 * groupings are just for convenience.
1099 version.feature_set = (unsigned long) (0L
1100 /* levels and/or topology (0..4) */
1101 /* monsters (5..9) */
1102 #ifdef MAIL
1103 | (1L << 6)
1104 #endif
1105 /* objects (10..14) */
1106 /* flag bits and/or other global variables (15..26) */
1107 #ifdef TEXTCOLOR
1108 | (1L << 17)
1109 #endif
1110 #ifdef INSURANCE
1111 | (1L << 18)
1112 #endif
1113 #ifdef SCORE_ON_BOTL
1114 | (1L << 19)
1115 #endif
1116 /* data format (27..31)
1117 * External compression methods such as COMPRESS and ZLIB_COMP
1118 * do not affect the contents and are thus excluded from here */
1119 #ifdef ZEROCOMP
1120 | (1L << 27)
1121 #endif
1122 #ifdef RLECOMP
1123 | (1L << 28)
1124 #endif
1127 * Value used for object & monster sanity check.
1128 * (NROFARTIFACTS<<24) | (NUM_OBJECTS<<12) | (NUMMONS<<0)
1130 for (i = 1; artifact_names[i]; i++)
1131 continue;
1132 version.entity_count = (unsigned long) (i - 1);
1133 for (i = 1; objects[i].oc_class != ILLOBJ_CLASS; i++)
1134 continue;
1135 version.entity_count = (version.entity_count << 12) | (unsigned long) i;
1136 for (i = 0; mons[i].mlet; i++)
1137 continue;
1138 version.entity_count = (version.entity_count << 12) | (unsigned long) i;
1140 * Value used for compiler (word size/field alignment/padding) check.
1142 version.struct_sizes1 =
1143 (((unsigned long) sizeof(struct context_info) << 24)
1144 | ((unsigned long) sizeof(struct obj) << 17)
1145 | ((unsigned long) sizeof(struct monst) << 10)
1146 | ((unsigned long) sizeof(struct you)));
1147 version.struct_sizes2 = (((unsigned long) sizeof(struct flag) << 10) |
1148 /* free bits in here */
1149 #ifdef SYSFLAGS
1150 ((unsigned long) sizeof(struct sysflag)));
1151 #else
1152 ((unsigned long) 0L));
1153 #endif
1154 return;
1157 static char *
1158 version_string(outbuf, delim)
1159 char *outbuf;
1160 const char *delim;
1162 Sprintf(outbuf, "%d%s%d%s%d", VERSION_MAJOR, delim, VERSION_MINOR, delim,
1163 PATCHLEVEL);
1164 #ifdef BETA
1165 Sprintf(eos(outbuf), "-%d", EDITLEVEL);
1166 #endif
1167 return outbuf;
1170 static char *
1171 version_id_string(outbuf, build_date)
1172 char *outbuf;
1173 const char *build_date;
1175 char subbuf[64], versbuf[64];
1177 subbuf[0] = '\0';
1178 #ifdef PORT_SUB_ID
1179 subbuf[0] = ' ';
1180 Strcpy(&subbuf[1], PORT_SUB_ID);
1181 #endif
1182 #ifdef BETA
1183 Strcat(subbuf, " Beta");
1184 #endif
1186 Sprintf(outbuf, "%s NetHack%s Version %s - last build %s.", PORT_ID,
1187 subbuf, version_string(versbuf, "."), build_date);
1188 return outbuf;
1191 static char *
1192 bannerc_string(outbuf, build_date)
1193 char *outbuf;
1194 const char *build_date;
1196 char subbuf[64], versbuf[64];
1198 subbuf[0] = '\0';
1199 #ifdef PORT_SUB_ID
1200 subbuf[0] = ' ';
1201 Strcpy(&subbuf[1], PORT_SUB_ID);
1202 #endif
1203 #ifdef BETA
1204 Strcat(subbuf, " Beta");
1205 #endif
1207 Sprintf(outbuf, " Version %s %s%s, built %s.",
1208 version_string(versbuf, "."), PORT_ID, subbuf, &build_date[4]);
1209 #if 0
1210 Sprintf(outbuf, "%s NetHack%s %s Copyright 1985-%s (built %s)",
1211 PORT_ID, subbuf, version_string(versbuf,"."), RELEASE_YEAR,
1212 &build_date[4]);
1213 #endif
1214 return outbuf;
1217 void
1218 do_date()
1220 #ifdef KR1ED
1221 long clocktim = 0;
1222 #else
1223 time_t clocktim = 0;
1224 #endif
1225 char *c, cbuf[60], buf[BUFSZ];
1226 const char *ul_sfx;
1228 /* before creating date.h, make sure that xxx_GRAPHICS and
1229 DEFAULT_WINDOW_SYS have been set up in a viable fashion */
1230 windowing_sanity();
1232 filename[0] = '\0';
1233 #ifdef FILE_PREFIX
1234 Strcat(filename, file_prefix);
1235 #endif
1236 Sprintf(eos(filename), INCLUDE_TEMPLATE, DATE_FILE);
1237 if (!(ofp = fopen(filename, WRTMODE))) {
1238 perror(filename);
1239 exit(EXIT_FAILURE);
1241 /* NB: We've moved on from SCCS, but this way this line
1242 * won't get clobbered when downstream projects import
1243 * this file into something more modern. */
1244 Fprintf(ofp, "%s", Dont_Edit_Code);
1246 (void) time(&clocktim);
1247 Strcpy(cbuf, ctime(&clocktim));
1249 for (c = cbuf; *c; c++)
1250 if (*c == '\n')
1251 break;
1252 *c = '\0'; /* strip off the '\n' */
1253 Fprintf(ofp, "#define BUILD_DATE \"%s\"\n", cbuf);
1254 Fprintf(ofp, "#define BUILD_TIME (%ldL)\n", (long) clocktim);
1255 Fprintf(ofp, "\n");
1256 #ifdef NHSTDC
1257 ul_sfx = "UL";
1258 #else
1259 ul_sfx = "L";
1260 #endif
1261 Fprintf(ofp, "#define VERSION_NUMBER 0x%08lx%s\n", version.incarnation,
1262 ul_sfx);
1263 Fprintf(ofp, "#define VERSION_FEATURES 0x%08lx%s\n", version.feature_set,
1264 ul_sfx);
1265 #ifdef IGNORED_FEATURES
1266 Fprintf(ofp, "#define IGNORED_FEATURES 0x%08lx%s\n",
1267 (unsigned long) IGNORED_FEATURES, ul_sfx);
1268 #endif
1269 Fprintf(ofp, "#define VERSION_SANITY1 0x%08lx%s\n", version.entity_count,
1270 ul_sfx);
1271 Fprintf(ofp, "#define VERSION_SANITY2 0x%08lx%s\n", version.struct_sizes1,
1272 ul_sfx);
1273 Fprintf(ofp, "#define VERSION_SANITY3 0x%08lx%s\n", version.struct_sizes2,
1274 ul_sfx);
1275 Fprintf(ofp, "\n");
1276 Fprintf(ofp, "#define VERSION_STRING \"%s\"\n", version_string(buf, "."));
1277 Fprintf(ofp, "#define VERSION_ID \\\n \"%s\"\n",
1278 version_id_string(buf, cbuf));
1279 Fprintf(ofp, "#define COPYRIGHT_BANNER_C \\\n \"%s\"\n",
1280 bannerc_string(buf, cbuf));
1281 Fprintf(ofp, "\n");
1282 #ifdef AMIGA
1284 struct tm *tm = localtime((time_t *) &clocktim);
1285 Fprintf(ofp, "#define AMIGA_VERSION_STRING ");
1286 Fprintf(ofp, "\"\\0$VER: NetHack %d.%d.%d (%d.%d.%d)\"\n",
1287 VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL, tm->tm_mday,
1288 tm->tm_mon + 1, tm->tm_year + 1900);
1290 #endif
1291 Fclose(ofp);
1292 return;
1295 static char save_bones_compat_buf[BUFSZ];
1297 static void
1298 build_savebones_compat_string()
1300 #ifdef VERSION_COMPATIBILITY
1301 unsigned long uver = VERSION_COMPATIBILITY;
1302 #endif
1303 Strcpy(save_bones_compat_buf,
1304 "save and bones files accepted from version");
1305 #ifdef VERSION_COMPATIBILITY
1306 Sprintf(eos(save_bones_compat_buf), "s %lu.%lu.%lu through %d.%d.%d",
1307 ((uver & 0xFF000000L) >> 24), ((uver & 0x00FF0000L) >> 16),
1308 ((uver & 0x0000FF00L) >> 8), VERSION_MAJOR, VERSION_MINOR,
1309 PATCHLEVEL);
1310 #else
1311 Sprintf(eos(save_bones_compat_buf), " %d.%d.%d only", VERSION_MAJOR,
1312 VERSION_MINOR, PATCHLEVEL);
1313 #endif
1316 static const char *build_opts[] = {
1317 #ifdef AMIGA_WBENCH
1318 "Amiga WorkBench support",
1319 #endif
1320 #ifdef ANSI_DEFAULT
1321 "ANSI default terminal",
1322 #endif
1323 #ifdef TEXTCOLOR
1324 "color",
1325 #endif
1326 #ifdef COM_COMPL
1327 "command line completion",
1328 #endif
1329 #ifdef LIFE
1330 "Conway's Game of Life",
1331 #endif
1332 #ifdef COMPRESS
1333 "data file compression",
1334 #endif
1335 #ifdef ZLIB_COMP
1336 "ZLIB data file compression",
1337 #endif
1338 #ifdef DLB
1339 "data librarian",
1340 #endif
1341 #ifdef MFLOPPY
1342 "floppy drive support",
1343 #endif
1344 #ifdef INSURANCE
1345 "insurance files for recovering from crashes",
1346 #endif
1347 #ifdef HOLD_LOCKFILE_OPEN
1348 "exclusive lock on level 0 file",
1349 #endif
1350 #ifdef LOGFILE
1351 "log file",
1352 #endif
1353 #ifdef MAIL
1354 "mail daemon",
1355 #endif
1356 #ifdef GNUDOS
1357 "MSDOS protected mode",
1358 #endif
1359 #ifdef NEWS
1360 "news file",
1361 #endif
1362 #ifdef OVERLAY
1363 #ifdef MOVERLAY
1364 "MOVE overlays",
1365 #else
1366 #ifdef VROOMM
1367 "VROOMM overlays",
1368 #else
1369 "overlays",
1370 #endif
1371 #endif
1372 #endif
1373 #ifdef SELECTSAVED
1374 "restore saved games via menu",
1375 #endif
1376 #ifdef SCORE_ON_BOTL
1377 "score on status line",
1378 #endif
1379 #ifdef CLIPPING
1380 "screen clipping",
1381 #endif
1382 #ifdef NO_TERMS
1383 #ifdef MAC
1384 "screen control via mactty",
1385 #endif
1386 #ifdef SCREEN_BIOS
1387 "screen control via BIOS",
1388 #endif
1389 #ifdef SCREEN_DJGPPFAST
1390 "screen control via DJGPP fast",
1391 #endif
1392 #ifdef SCREEN_VGA
1393 "screen control via VGA graphics",
1394 #endif
1395 #ifdef WIN32CON
1396 "screen control via WIN32 console I/O",
1397 #endif
1398 #endif
1399 #ifdef SHELL
1400 "shell command",
1401 #endif
1402 #ifdef SUSPEND
1403 "suspend command",
1404 #endif
1405 #ifdef TERMINFO
1406 "terminal info library",
1407 #else
1408 #if defined(TERMLIB) \
1409 || ((!defined(MICRO) && !defined(WIN32)) && defined(TTY_GRAPHICS))
1410 "terminal capability library",
1411 #endif
1412 #endif
1413 #ifdef TIMED_DELAY
1414 "timed wait for display effects",
1415 #endif
1416 #ifdef USER_SOUNDS
1417 "user sounds",
1418 #endif
1419 #ifdef PREFIXES_IN_USE
1420 "variable playground",
1421 #endif
1422 #ifdef VISION_TABLES
1423 "vision tables",
1424 #endif
1425 #ifdef ZEROCOMP
1426 "zero-compressed save files",
1427 #endif
1428 #ifdef RLECOMP
1429 "run-length compression of map in save files",
1430 #endif
1431 #ifdef SYSCF
1432 "system configuration at run-time",
1433 #endif
1434 save_bones_compat_buf, "and basic NetHack features"
1437 struct win_info {
1438 const char *id, /* DEFAULT_WINDOW_SYS string */
1439 *name; /* description, often same as id */
1441 static struct win_info window_opts[] = {
1442 #ifdef TTY_GRAPHICS
1443 { "tty", "traditional tty-based graphics" },
1444 #endif
1445 #ifdef X11_GRAPHICS
1446 { "X11", "X11" },
1447 #endif
1448 #ifdef QT_GRAPHICS
1449 { "Qt", "Qt" },
1450 #endif
1451 #ifdef GNOME_GRAPHICS
1452 { "Gnome", "Gnome" },
1453 #endif
1454 #ifdef MAC
1455 { "mac", "Mac" },
1456 #endif
1457 #ifdef AMIGA_INTUITION
1458 { "amii", "Amiga Intuition" },
1459 #endif
1460 #ifdef GEM_GRAPHICS
1461 { "Gem", "Gem" },
1462 #endif
1463 #ifdef MSWIN_GRAPHICS
1464 { "mswin", "mswin" },
1465 #endif
1466 #ifdef BEOS_GRAPHICS
1467 { "BeOS", "BeOS InterfaceKit" },
1468 #endif
1469 { 0, 0 }
1472 static void
1473 windowing_sanity()
1475 #ifndef DEFAULT_WINDOW_SYS
1476 /* pre-standard compilers didn't support #error; wait til run-time */
1477 Fprintf(stderr,
1478 "Configuration error: DEFAULT_WINDOW_SYS is not defined.\n");
1479 exit(EXIT_FAILURE);
1480 /*NOTREACHED*/
1482 /* put in a dummy value so that do_options() will compile and makedefs
1483 will build, otherwise the message above won't ever get delivered */
1484 #define DEFAULT_WINDOW_SYS "<undefined>"
1485 #else /*DEFAULT_WINDOW_SYS*/
1487 if (!window_opts[0].id) {
1488 Fprintf(stderr, "Configuration error: no windowing systems "
1489 "(TTY_GRAPHICS, &c) enabled.\n");
1490 exit(EXIT_FAILURE);
1494 int i;
1496 for (i = 0; window_opts[i].id; ++i)
1497 if (!strcmp(window_opts[i].id, DEFAULT_WINDOW_SYS))
1498 break;
1499 if (!window_opts[i]
1500 .id) { /* went through whole list without a match */
1501 Fprintf(stderr, "Configuration error: DEFAULT_WINDOW_SYS (%s)\n",
1502 DEFAULT_WINDOW_SYS);
1503 Fprintf(stderr,
1504 " does not match any enabled windowing system (%s%s).\n",
1505 window_opts[0].id, window_opts[1].id ? ", &c" : "");
1506 exit(EXIT_FAILURE);
1509 #endif /*DEFAULT_WINDOW_SYS*/
1512 void
1513 do_options()
1515 static const char indent[] = " ";
1516 const char *str, *sep;
1517 char *word, buf[BUFSZ];
1518 int i, length, winsyscnt;
1520 windowing_sanity();
1522 filename[0] = '\0';
1523 #ifdef FILE_PREFIX
1524 Strcat(filename, file_prefix);
1525 #endif
1526 Sprintf(eos(filename), DATA_TEMPLATE, OPTIONS_FILE);
1527 if (!(ofp = fopen(filename, WRTMODE))) {
1528 perror(filename);
1529 exit(EXIT_FAILURE);
1532 build_savebones_compat_string();
1533 Fprintf(ofp,
1534 #ifdef BETA
1535 "\n NetHack version %d.%d.%d [beta]\n",
1536 #else
1537 "\n NetHack version %d.%d.%d\n",
1538 #endif
1539 VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL);
1541 Fprintf(ofp, "\nOptions compiled into this edition:\n");
1542 length = COLNO + 1; /* force 1st item onto new line */
1543 for (i = 0; i < SIZE(build_opts); i++) {
1544 str = strcpy(buf, build_opts[i]);
1545 while (*str) {
1546 word = index(str, ' ');
1547 if (word)
1548 *word = '\0';
1549 if (length + strlen(str) > COLNO - 5)
1550 Fprintf(ofp, "\n%s", indent), length = strlen(indent);
1551 else
1552 Fprintf(ofp, " "), length++;
1553 Fprintf(ofp, "%s", str), length += strlen(str);
1554 str += strlen(str) + (word ? 1 : 0);
1556 Fprintf(ofp, (i < SIZE(build_opts) - 1) ? "," : "."), length++;
1559 winsyscnt = SIZE(window_opts) - 1;
1560 Fprintf(ofp, "\n\nSupported windowing system%s:\n",
1561 (winsyscnt > 1) ? "s" : "");
1562 length = COLNO + 1; /* force 1st item onto new line */
1563 for (i = 0; i < winsyscnt; i++) {
1564 str = window_opts[i].name;
1565 if (length + strlen(str) > COLNO - 5)
1566 Fprintf(ofp, "\n%s", indent), length = strlen(indent);
1567 else
1568 Fprintf(ofp, " "), length++;
1569 Fprintf(ofp, "%s", str), length += strlen(str);
1570 sep = (winsyscnt == 1)
1571 ? "."
1572 : (winsyscnt == 2)
1573 ? ((i == 0) ? " and" : "")
1574 : (i < winsyscnt - 2)
1575 ? ","
1576 : ((i == winsyscnt - 2) ? ", and" : "");
1577 Fprintf(ofp, "%s", sep), length += strlen(sep);
1579 if (winsyscnt > 1)
1580 Fprintf(ofp, "\n%swith a default of %s.", indent, DEFAULT_WINDOW_SYS);
1581 Fprintf(ofp, "\n\n");
1583 Fclose(ofp);
1584 return;
1587 /* routine to decide whether to discard something from data.base */
1588 static boolean
1589 d_filter(line)
1590 char *line;
1592 if (*line == '#')
1593 return TRUE; /* ignore comment lines */
1594 return FALSE;
1599 New format (v3.1) of 'data' file which allows much faster lookups [pr]
1600 "do not edit" first record is a comment line
1601 01234567 hexadecimal formatted offset to text area
1602 name-a first name of interest
1603 123,4 offset to name's text, and number of lines for it
1604 name-b next name of interest
1605 name-c multiple names which share same description also
1606 456,7 share a single offset,count line
1607 . sentinel to mark end of names
1608 789,0 dummy record containing offset, count of EOF
1609 text-a 4 lines of descriptive text for name-a
1610 text-a at file position 0x01234567L + 123L
1611 text-a
1612 text-a
1613 text-b/text-c 7 lines of text for names-b and -c
1614 text-b/text-c at fseek(0x01234567L + 456L)
1619 void
1620 do_data()
1622 char infile[60], tempfile[60];
1623 boolean ok;
1624 long txt_offset;
1625 int entry_cnt, line_cnt;
1626 char *line;
1628 Sprintf(tempfile, DATA_TEMPLATE, "database.tmp");
1629 filename[0] = '\0';
1630 #ifdef FILE_PREFIX
1631 Strcat(filename, file_prefix);
1632 #endif
1633 Sprintf(eos(filename), DATA_TEMPLATE, DATA_FILE);
1634 Sprintf(infile, DATA_IN_TEMPLATE, DATA_FILE);
1635 #ifdef SHORT_FILENAMES
1636 Strcat(infile, ".bas");
1637 #else
1638 Strcat(infile, ".base");
1639 #endif
1640 if (!(ifp = fopen(infile, RDTMODE))) { /* data.base */
1641 perror(infile);
1642 exit(EXIT_FAILURE);
1644 if (!(ofp = fopen(filename, WRTMODE))) { /* data */
1645 perror(filename);
1646 Fclose(ifp);
1647 exit(EXIT_FAILURE);
1649 if (!(tfp = fopen(tempfile, WRTMODE))) { /* database.tmp */
1650 perror(tempfile);
1651 Fclose(ifp);
1652 Fclose(ofp);
1653 Unlink(filename);
1654 exit(EXIT_FAILURE);
1657 /* output a dummy header record; we'll rewind and overwrite it later */
1658 Fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, 0L);
1660 entry_cnt = line_cnt = 0;
1661 /* read through the input file and split it into two sections */
1662 while ((line = fgetline(ifp)) != 0) {
1663 if (d_filter(line)) {
1664 free(line);
1665 continue;
1667 if (*line > ' ') { /* got an entry name */
1668 /* first finish previous entry */
1669 if (line_cnt)
1670 Fprintf(ofp, "%d\n", line_cnt), line_cnt = 0;
1671 /* output the entry name */
1672 (void) fputs(line, ofp);
1673 entry_cnt++; /* update number of entries */
1674 } else if (entry_cnt) { /* got some descriptive text */
1675 /* update previous entry with current text offset */
1676 if (!line_cnt)
1677 Fprintf(ofp, "%ld,", ftell(tfp));
1678 /* save the text line in the scratch file */
1679 (void) fputs(line, tfp);
1680 line_cnt++; /* update line counter */
1682 free(line);
1684 /* output an end marker and then record the current position */
1685 if (line_cnt)
1686 Fprintf(ofp, "%d\n", line_cnt);
1687 Fprintf(ofp, ".\n%ld,%d\n", ftell(tfp), 0);
1688 txt_offset = ftell(ofp);
1689 Fclose(ifp); /* all done with original input file */
1691 /* reprocess the scratch file; 1st format an error msg, just in case */
1692 line = malloc(256);
1693 Sprintf(line, "rewind of \"%s\"", tempfile);
1694 if (rewind(tfp) != 0)
1695 goto dead_data;
1696 free(line);
1697 /* copy all lines of text from the scratch file into the output file */
1698 while ((line = fgetline(tfp)) != 0) {
1699 (void) fputs(line, ofp);
1700 free(line);
1703 /* finished with scratch file */
1704 Fclose(tfp);
1705 Unlink(tempfile); /* remove it */
1707 /* update the first record of the output file; prepare error msg 1st */
1708 line = malloc(256);
1709 Sprintf(line, "rewind of \"%s\"", filename);
1710 ok = (rewind(ofp) == 0);
1711 if (ok) {
1712 Sprintf(line, "header rewrite of \"%s\"", filename);
1713 ok = (fprintf(ofp, "%s%08lx\n", Dont_Edit_Data,
1714 (unsigned long) txt_offset) >= 0);
1716 if (!ok) {
1717 dead_data:
1718 perror(line); /* report the problem */
1719 free(line);
1720 /* close and kill the aborted output file, then give up */
1721 Fclose(ofp);
1722 Unlink(filename);
1723 exit(EXIT_FAILURE);
1725 free(line);
1727 /* all done */
1728 Fclose(ofp);
1730 return;
1733 /* routine to decide whether to discard something from oracles.txt */
1734 static boolean
1735 h_filter(line)
1736 char *line;
1738 static boolean skip = FALSE;
1739 char *tag;
1741 SpinCursor(3);
1743 if (*line == '#')
1744 return TRUE; /* ignore comment lines */
1746 tag = malloc(strlen(line));
1747 if (sscanf(line, "----- %s", tag) == 1) {
1748 skip = FALSE;
1749 } else if (skip && !strncmp(line, "-----", 5))
1750 skip = FALSE;
1751 free(tag);
1752 return skip;
1755 static const char *special_oracle[] = {
1756 "\"...it is rather disconcerting to be confronted with the",
1757 "following theorem from [Baker, Gill, and Solovay, 1975].", "",
1758 "Theorem 7.18 There exist recursive languages A and B such that",
1759 " (1) P(A) == NP(A), and", " (2) P(B) != NP(B)", "",
1760 "This provides impressive evidence that the techniques that are",
1761 "currently available will not suffice for proving that P != NP or "
1762 " ",
1763 "that P == NP.\" [Garey and Johnson, p. 185.]"
1767 The oracle file consists of a "do not edit" comment, a decimal count N
1768 and set of N+1 hexadecimal fseek offsets, followed by N multiple-line
1769 records, separated by "---" lines. The first oracle is a special case.
1770 The input data contains just those multi-line records, separated by
1771 "-----" lines.
1774 void
1775 do_oracles()
1777 char infile[60], tempfile[60];
1778 boolean in_oracle, ok;
1779 long fpos;
1780 unsigned long txt_offset, offset;
1781 int oracle_cnt;
1782 register int i;
1783 char *line;
1785 Sprintf(tempfile, DATA_TEMPLATE, "oracles.tmp");
1786 filename[0] = '\0';
1787 #ifdef FILE_PREFIX
1788 Strcat(filename, file_prefix);
1789 #endif
1790 Sprintf(eos(filename), DATA_TEMPLATE, ORACLE_FILE);
1791 Sprintf(infile, DATA_IN_TEMPLATE, ORACLE_FILE);
1792 Strcat(infile, ".txt");
1793 if (!(ifp = fopen(infile, RDTMODE))) {
1794 perror(infile);
1795 exit(EXIT_FAILURE);
1797 if (!(ofp = fopen(filename, WRTMODE))) {
1798 perror(filename);
1799 Fclose(ifp);
1800 exit(EXIT_FAILURE);
1802 if (!(tfp = fopen(tempfile, WRTMODE))) { /* oracles.tmp */
1803 perror(tempfile);
1804 Fclose(ifp);
1805 Fclose(ofp);
1806 Unlink(filename);
1807 exit(EXIT_FAILURE);
1810 /* output a dummy header record; we'll rewind and overwrite it later */
1811 Fprintf(ofp, "%s%5d\n", Dont_Edit_Data, 0);
1813 /* handle special oracle; it must come first */
1814 (void) fputs("---\n", tfp);
1815 offset = (unsigned long) ftell(tfp);
1816 Fprintf(ofp, "%05lx\n", offset); /* start pos of special oracle */
1817 for (i = 0; i < SIZE(special_oracle); i++) {
1818 (void) fputs(xcrypt(special_oracle[i]), tfp);
1819 (void) fputc('\n', tfp);
1821 SpinCursor(3);
1823 oracle_cnt = 1;
1824 (void) fputs("---\n", tfp);
1825 offset = (unsigned long) ftell(tfp);
1826 Fprintf(ofp, "%05lx\n", offset); /* start pos of first oracle */
1827 in_oracle = FALSE;
1829 while ((line = fgetline(ifp)) != 0) {
1830 SpinCursor(3);
1832 if (h_filter(line)) {
1833 free(line);
1834 continue;
1836 if (!strncmp(line, "-----", 5)) {
1837 if (!in_oracle) {
1838 free(line);
1839 continue;
1841 in_oracle = FALSE;
1842 oracle_cnt++;
1843 (void) fputs("---\n", tfp);
1844 offset = (unsigned long) ftell(tfp);
1845 Fprintf(ofp, "%05lx\n", offset); /* start pos of this oracle */
1846 } else {
1847 in_oracle = TRUE;
1848 (void) fputs(xcrypt(line), tfp);
1850 free(line);
1853 if (in_oracle) { /* need to terminate last oracle */
1854 oracle_cnt++;
1855 (void) fputs("---\n", tfp);
1856 offset = (unsigned long) ftell(tfp);
1857 Fprintf(ofp, "%05lx\n", offset); /* eof position */
1860 /* record the current position */
1861 txt_offset = (unsigned long) ftell(ofp);
1862 Fclose(ifp); /* all done with original input file */
1864 /* reprocess the scratch file; 1st format an error msg, just in case */
1865 line = malloc(256);
1866 Sprintf(line, "rewind of \"%s\"", tempfile);
1867 if (rewind(tfp) != 0)
1868 goto dead_data;
1869 free(line);
1870 /* copy all lines of text from the scratch file into the output file */
1871 while ((line = fgetline(tfp)) != 0) {
1872 (void) fputs(line, ofp);
1873 free(line);
1876 /* finished with scratch file */
1877 Fclose(tfp);
1878 Unlink(tempfile); /* remove it */
1880 /* update the first record of the output file; prepare error msg 1st */
1881 line = malloc(256);
1882 Sprintf(line, "rewind of \"%s\"", filename);
1883 ok = (rewind(ofp) == 0);
1884 if (ok) {
1885 Sprintf(line, "header rewrite of \"%s\"", filename);
1886 ok = (fprintf(ofp, "%s%5d\n", Dont_Edit_Data, oracle_cnt) >= 0);
1888 if (ok) {
1889 Sprintf(line, "data rewrite of \"%s\"", filename);
1890 for (i = 0; i <= oracle_cnt; i++) {
1891 #ifndef VMS /* alpha/vms v1.0; this fflush seems to confuse ftell */
1892 if (!(ok = (fflush(ofp) == 0)))
1893 break;
1894 #endif
1895 if (!(ok = (fpos = ftell(ofp)) >= 0))
1896 break;
1897 if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0)))
1898 break;
1899 if (!(ok = (fscanf(ofp, "%5lx", &offset) == 1)))
1900 break;
1901 #ifdef MAC
1902 #ifdef __MWERKS__
1904 MetroWerks CodeWarrior Pro 1's (AKA CW12) version of MSL
1905 (ANSI C Libraries) needs this rewind or else the fprintf
1906 stops working. This may also be true for CW11, but has
1907 never been checked.
1909 rewind(ofp);
1910 #endif
1911 #endif
1912 if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0)))
1913 break;
1914 offset += txt_offset;
1915 if (!(ok = (fprintf(ofp, "%05lx\n", offset) >= 0)))
1916 break;
1919 if (!ok) {
1920 dead_data:
1921 perror(line); /* report the problem */
1922 free(line);
1923 /* close and kill the aborted output file, then give up */
1924 Fclose(ofp);
1925 Unlink(filename);
1926 exit(EXIT_FAILURE);
1928 free(line);
1930 /* all done */
1931 Fclose(ofp);
1933 return;
1936 void
1937 do_dungeon()
1939 int rcnt = 0;
1940 char *line;
1942 Sprintf(filename, DATA_IN_TEMPLATE, DGN_I_FILE);
1943 if (!(ifp = fopen(filename, RDTMODE))) {
1944 perror(filename);
1945 exit(EXIT_FAILURE);
1947 filename[0] = '\0';
1948 #ifdef FILE_PREFIX
1949 Strcat(filename, file_prefix);
1950 #endif
1951 Sprintf(eos(filename), DGN_TEMPLATE, DGN_O_FILE);
1952 if (!(ofp = fopen(filename, WRTMODE))) {
1953 perror(filename);
1954 exit(EXIT_FAILURE);
1956 Fprintf(ofp, "%s", Dont_Edit_Data);
1958 tfp = getfp(DATA_TEMPLATE, "grep.tmp", WRTMODE);
1959 grep0(ifp, tfp);
1960 ifp = getfp(DATA_TEMPLATE, "grep.tmp", RDTMODE);
1962 while ((line = fgetline(ifp)) != 0) {
1963 SpinCursor(3);
1965 rcnt++;
1966 if (line[0] == '#') {
1967 free(line);
1968 continue; /* discard comments */
1970 (void) fputs(line, ofp);
1971 free(line);
1973 Fclose(ifp);
1974 Fclose(ofp);
1976 delete_file(DATA_TEMPLATE, "grep.tmp");
1977 return;
1980 static boolean
1981 ranged_attk(ptr) /* returns TRUE if monster can attack at range */
1982 register struct permonst *ptr;
1984 register int i, j;
1985 register int atk_mask = (1 << AT_BREA) | (1 << AT_SPIT) | (1 << AT_GAZE);
1987 for (i = 0; i < NATTK; i++) {
1988 if ((j = ptr->mattk[i].aatyp) >= AT_WEAP || (atk_mask & (1 << j)))
1989 return TRUE;
1992 return FALSE;
1995 /* This routine is designed to return an integer value which represents
1996 * an approximation of monster strength. It uses a similar method of
1997 * determination as "experience()" to arrive at the strength.
1999 static int
2000 mstrength(ptr)
2001 struct permonst *ptr;
2003 int i, tmp2, n, tmp = ptr->mlevel;
2005 if (tmp > 49) /* special fixed hp monster */
2006 tmp = 2 * (tmp - 6) / 4;
2008 /* For creation in groups */
2009 n = (!!(ptr->geno & G_SGROUP));
2010 n += (!!(ptr->geno & G_LGROUP)) << 1;
2012 /* For ranged attacks */
2013 if (ranged_attk(ptr))
2014 n++;
2016 /* For higher ac values */
2017 n += (ptr->ac < 4);
2018 n += (ptr->ac < 0);
2020 /* For very fast monsters */
2021 n += (ptr->mmove >= 18);
2023 /* For each attack and "special" attack */
2024 for (i = 0; i < NATTK; i++) {
2025 tmp2 = ptr->mattk[i].aatyp;
2026 n += (tmp2 > 0);
2027 n += (tmp2 == AT_MAGC);
2028 n += (tmp2 == AT_WEAP && (ptr->mflags2 & M2_STRONG));
2031 /* For each "special" damage type */
2032 for (i = 0; i < NATTK; i++) {
2033 tmp2 = ptr->mattk[i].adtyp;
2034 if ((tmp2 == AD_DRLI) || (tmp2 == AD_STON) || (tmp2 == AD_DRST)
2035 || (tmp2 == AD_DRDX) || (tmp2 == AD_DRCO) || (tmp2 == AD_WERE))
2036 n += 2;
2037 else if (strcmp(ptr->mname, "grid bug"))
2038 n += (tmp2 != AD_PHYS);
2039 n += ((int) (ptr->mattk[i].damd * ptr->mattk[i].damn) > 23);
2042 /* Leprechauns are special cases. They have many hit dice so they can
2043 hit and are hard to kill, but they don't really do much damage. */
2044 if (!strcmp(ptr->mname, "leprechaun"))
2045 n -= 2;
2047 /* Finally, adjust the monster level 0 <= n <= 24 (approx.) */
2048 if (n == 0)
2049 tmp--;
2050 else if (n >= 6)
2051 tmp += (n / 2);
2052 else
2053 tmp += (n / 3 + 1);
2055 return (tmp >= 0) ? tmp : 0;
2058 void
2059 do_monstr()
2061 register struct permonst *ptr;
2062 register int i, j;
2065 * create the source file, "monstr.c"
2067 filename[0] = '\0';
2068 #ifdef FILE_PREFIX
2069 Strcat(filename, file_prefix);
2070 #endif
2071 Sprintf(eos(filename), SOURCE_TEMPLATE, MON_STR_C);
2072 if (!(ofp = fopen(filename, WRTMODE))) {
2073 perror(filename);
2074 exit(EXIT_FAILURE);
2076 Fprintf(ofp, "%s", Dont_Edit_Code);
2077 Fprintf(ofp, "#include \"config.h\"\n");
2078 Fprintf(ofp, "\nconst int monstr[] = {\n");
2079 for (ptr = &mons[0], j = 0; ptr->mlet; ptr++) {
2080 SpinCursor(3);
2082 i = mstrength(ptr);
2083 Fprintf(ofp, "%2d,%c", i, (++j & 15) ? ' ' : '\n');
2085 /* might want to insert a final 0 entry here instead of just newline */
2086 Fprintf(ofp, "%s};\n", (j & 15) ? "\n" : "");
2088 Fprintf(ofp, "\nvoid NDECL(monstr_init);\n");
2089 Fprintf(ofp, "\nvoid\n");
2090 Fprintf(ofp, "monstr_init()\n");
2091 Fprintf(ofp, "{\n");
2092 Fprintf(ofp, " return;\n");
2093 Fprintf(ofp, "}\n");
2094 Fprintf(ofp, "\n/*monstr.c*/\n");
2096 Fclose(ofp);
2097 return;
2100 void
2101 do_permonst()
2103 int i;
2104 char *c, *nam;
2106 filename[0] = '\0';
2107 #ifdef FILE_PREFIX
2108 Strcat(filename, file_prefix);
2109 #endif
2110 Sprintf(eos(filename), INCLUDE_TEMPLATE, MONST_FILE);
2111 if (!(ofp = fopen(filename, WRTMODE))) {
2112 perror(filename);
2113 exit(EXIT_FAILURE);
2115 Fprintf(ofp, "%s", Dont_Edit_Code);
2116 Fprintf(ofp, "#ifndef PM_H\n#define PM_H\n");
2118 if (strcmp(mons[0].mname, "playermon") != 0)
2119 Fprintf(ofp, "\n#define\tPM_PLAYERMON\t(-1)");
2121 for (i = 0; mons[i].mlet; i++) {
2122 SpinCursor(3);
2124 Fprintf(ofp, "\n#define\tPM_");
2125 if (mons[i].mlet == S_HUMAN && !strncmp(mons[i].mname, "were", 4))
2126 Fprintf(ofp, "HUMAN_");
2127 for (nam = c = tmpdup(mons[i].mname); *c; c++)
2128 if (*c >= 'a' && *c <= 'z')
2129 *c -= (char) ('a' - 'A');
2130 else if (*c < 'A' || *c > 'Z')
2131 *c = '_';
2132 Fprintf(ofp, "%s\t%d", nam, i);
2134 Fprintf(ofp, "\n\n#define\tNUMMONS\t%d\n", i);
2135 Fprintf(ofp, "\n#endif /* PM_H */\n");
2136 Fclose(ofp);
2137 return;
2140 /* Start of Quest text file processing. */
2141 #include "qtext.h"
2143 static struct qthdr qt_hdr;
2144 static struct msghdr msg_hdr[N_HDR];
2145 static struct qtmsg *curr_msg;
2147 static int qt_line;
2149 static boolean in_msg;
2150 #define NO_MSG 1 /* strlen of a null line returned by fgets() */
2152 static boolean
2153 qt_comment(s)
2154 char *s;
2156 if (s[0] == '#')
2157 return TRUE;
2158 return (boolean) (!in_msg && strlen(s) == NO_MSG);
2161 static boolean
2162 qt_control(s)
2163 char *s;
2165 return (boolean) (s[0] == '%' && (s[1] == 'C' || s[1] == 'E'));
2168 static int
2169 get_hdr(code)
2170 char *code;
2172 int i;
2174 for (i = 0; i < qt_hdr.n_hdr; i++)
2175 if (!strncmp(code, qt_hdr.id[i], LEN_HDR))
2176 return ++i;
2178 return 0;
2181 static boolean
2182 new_id(code)
2183 char *code;
2185 if (qt_hdr.n_hdr >= N_HDR) {
2186 Fprintf(stderr, OUT_OF_HEADERS, qt_line);
2187 return FALSE;
2190 strncpy(&qt_hdr.id[qt_hdr.n_hdr][0], code, LEN_HDR);
2191 msg_hdr[qt_hdr.n_hdr].n_msg = 0;
2192 qt_hdr.offset[qt_hdr.n_hdr++] = 0L;
2193 return TRUE;
2196 static boolean
2197 known_msg(num, id)
2198 int num, id;
2200 int i;
2202 for (i = 0; i < msg_hdr[num].n_msg; i++)
2203 if (msg_hdr[num].qt_msg[i].msgnum == id)
2204 return TRUE;
2206 return FALSE;
2209 static void
2210 new_msg(s, num, id)
2211 char *s;
2212 int num, id;
2214 struct qtmsg *qt_msg;
2216 if (msg_hdr[num].n_msg >= N_MSG) {
2217 Fprintf(stderr, OUT_OF_MESSAGES, qt_line);
2218 } else {
2219 qt_msg = &(msg_hdr[num].qt_msg[msg_hdr[num].n_msg++]);
2220 qt_msg->msgnum = id;
2221 qt_msg->delivery = s[2];
2222 qt_msg->offset = qt_msg->size = qt_msg->summary_size = 0L;
2224 curr_msg = qt_msg;
2228 /* check %E record for "[summary text]" that nethack can stuff into the
2229 message history buffer when delivering text via window instead of pline */
2230 static char *
2231 valid_qt_summary(s, parsing)
2232 char *s; /* end record: "%E" optionally followed by " [summary]" */
2233 boolean parsing; /* curr_msg is valid iff this is True */
2235 static char summary[BUFSZ];
2236 char *p;
2238 if (*s != '%' || *(s + 1) != 'E')
2239 return (char *) 0;
2240 if ((p = index(s, '[')) == 0)
2241 return (char *) 0;
2242 /* note: opening '[' and closing ']' will be retained in the output;
2243 anything after ']' will be discarded by putting a newline there */
2244 Strcpy(summary, p);
2246 /* have an opening bracket; summary[] holds it and all text that follows
2248 p = eos(summary);
2249 /* find closing bracket */
2250 while (p > summary && *(p - 1) != ']')
2251 --p;
2253 if (p == summary) {
2254 /* we backed up all the way to the start without finding a bracket */
2255 if (parsing) /* malformed summary */
2256 Fprintf(stderr, MAL_SUM, qt_line);
2257 } else if (p == summary + 1) {
2258 ; /* ignore empty [] */
2259 } else { /* got something */
2260 /* p points one spot past ']', usually to '\n';
2261 we need to include the \n as part of the size */
2262 if (parsing) {
2263 /* during the writing pass we won't be able to recheck
2264 delivery, so any useless summary for a pline mode
2265 message has to be carried along to the output file */
2266 if (curr_msg->delivery == 'p')
2267 Fprintf(stderr, DUMB_SUM, qt_line);
2268 /* +1 is for terminating newline */
2269 curr_msg->summary_size = (long) (p - summary) + 1L;
2270 } else {
2271 /* caller is writing rather than just parsing;
2272 force newline after the closing bracket */
2273 Strcpy(p, "\n");
2275 return summary;
2277 return (char *) 0;
2280 static void
2281 do_qt_control(s)
2282 char *s;
2284 char code[BUFSZ];
2285 int num, id = 0;
2287 if (!index(s, '\n'))
2288 Fprintf(stderr, CTRL_TRUNC, qt_line);
2290 switch (s[1]) {
2291 case 'C':
2292 if (in_msg) {
2293 Fprintf(stderr, CREC_IN_MSG, qt_line);
2294 break;
2295 } else {
2296 in_msg = TRUE;
2297 if (sscanf(&s[4], "%s %5d", code, &id) != 2) {
2298 Fprintf(stderr, UNREC_CREC, qt_line);
2299 break;
2301 num = get_hdr(code);
2302 if (!num && !new_id(code))
2303 break;
2304 num = get_hdr(code) - 1;
2305 if (known_msg(num, id))
2306 Fprintf(stderr, DUP_MSG, qt_line);
2307 else
2308 new_msg(s, num, id);
2310 break;
2312 case 'E':
2313 if (!in_msg) {
2314 Fprintf(stderr, END_NOT_IN_MSG, qt_line);
2315 } else {
2316 /* sets curr_msg->summary_size if applicable */
2317 (void) valid_qt_summary(s, TRUE);
2318 in_msg = FALSE;
2320 break;
2322 default:
2323 Fprintf(stderr, UNREC_CREC, qt_line);
2324 break;
2328 static void
2329 do_qt_text(s)
2330 char *s;
2332 if (!in_msg) {
2333 Fprintf(stderr, TEXT_NOT_IN_MSG, qt_line);
2334 } else if (!index(s, '\n')) {
2335 Fprintf(stderr, TEXT_TRUNC, qt_line);
2338 curr_msg->size += strlen(s);
2339 return;
2342 static void
2343 adjust_qt_hdrs()
2345 int i, j;
2346 long count = 0L, hdr_offset = sizeof(int)
2347 + (sizeof(char) * LEN_HDR + sizeof(long))
2348 * qt_hdr.n_hdr;
2350 for (i = 0; i < qt_hdr.n_hdr; i++) {
2351 qt_hdr.offset[i] = hdr_offset;
2352 hdr_offset += sizeof(int) + sizeof(struct qtmsg) * msg_hdr[i].n_msg;
2355 for (i = 0; i < qt_hdr.n_hdr; i++)
2356 for (j = 0; j < msg_hdr[i].n_msg; j++) {
2357 msg_hdr[i].qt_msg[j].offset = hdr_offset + count;
2358 count +=
2359 msg_hdr[i].qt_msg[j].size + msg_hdr[i].qt_msg[j].summary_size;
2361 return;
2364 static void
2365 put_qt_hdrs()
2367 int i;
2370 * The main header record.
2372 if (debug)
2373 Fprintf(stderr, "%ld: header info.\n", ftell(ofp));
2374 (void) fwrite((genericptr_t) & (qt_hdr.n_hdr), sizeof(int), 1, ofp);
2375 (void) fwrite((genericptr_t) & (qt_hdr.id[0][0]), sizeof(char) * LEN_HDR,
2376 qt_hdr.n_hdr, ofp);
2377 (void) fwrite((genericptr_t) & (qt_hdr.offset[0]), sizeof(long),
2378 qt_hdr.n_hdr, ofp);
2379 if (debug) {
2380 for (i = 0; i < qt_hdr.n_hdr; i++)
2381 Fprintf(stderr, "%s @ %ld, ", qt_hdr.id[i], qt_hdr.offset[i]);
2382 Fprintf(stderr, "\n");
2386 * The individual class headers.
2388 for (i = 0; i < qt_hdr.n_hdr; i++) {
2389 if (debug)
2390 Fprintf(stderr, "%ld: %s header info.\n", ftell(ofp),
2391 qt_hdr.id[i]);
2392 (void) fwrite((genericptr_t) & (msg_hdr[i].n_msg), sizeof(int), 1,
2393 ofp);
2394 (void) fwrite((genericptr_t) & (msg_hdr[i].qt_msg[0]),
2395 sizeof(struct qtmsg), msg_hdr[i].n_msg, ofp);
2396 if (debug) {
2397 int j;
2399 for (j = 0; j < msg_hdr[i].n_msg; j++) {
2400 Fprintf(stderr, "msg %d @ %ld (%ld)",
2401 msg_hdr[i].qt_msg[j].msgnum,
2402 msg_hdr[i].qt_msg[j].offset,
2403 msg_hdr[i].qt_msg[j].size);
2404 if (msg_hdr[i].qt_msg[j].summary_size)
2405 Fprintf(stderr, " [%ld]",
2406 msg_hdr[i].qt_msg[j].summary_size);
2407 Fprintf(stderr, "\n");
2413 void
2414 do_questtxt()
2416 char *line;
2418 Sprintf(filename, DATA_IN_TEMPLATE, QTXT_I_FILE);
2419 if (!(ifp = fopen(filename, RDTMODE))) {
2420 perror(filename);
2421 exit(EXIT_FAILURE);
2424 filename[0] = '\0';
2425 #ifdef FILE_PREFIX
2426 Strcat(filename, file_prefix);
2427 #endif
2428 Sprintf(eos(filename), DATA_TEMPLATE, QTXT_O_FILE);
2429 if (!(ofp = fopen(filename, WRBMODE))) {
2430 perror(filename);
2431 Fclose(ifp);
2432 exit(EXIT_FAILURE);
2435 qt_hdr.n_hdr = 0;
2436 qt_line = 0;
2437 in_msg = FALSE;
2439 while ((line = fgetline(ifp)) != 0) {
2440 SpinCursor(3);
2442 qt_line++;
2443 if (qt_control(line))
2444 do_qt_control(line);
2445 else if (qt_comment(line)) {
2446 free(line);
2447 continue;
2448 } else
2449 do_qt_text(line);
2450 free(line);
2453 (void) rewind(ifp);
2454 in_msg = FALSE;
2455 adjust_qt_hdrs();
2456 put_qt_hdrs();
2457 while ((line = fgetline(ifp)) != 0) {
2458 if (qt_control(line)) {
2459 char *summary_p = 0;
2461 in_msg = (line[1] == 'C');
2462 if (!in_msg)
2463 summary_p = valid_qt_summary(line, FALSE);
2464 /* don't write anything unless we've got a summary */
2465 if (!summary_p) {
2466 free(line);
2467 continue;
2469 /* we have summary text; replace raw %E record with it */
2470 Strcpy(line, summary_p); /* (guaranteed to fit) */
2471 } else if (qt_comment(line)) {
2472 free(line);
2473 continue;
2475 if (debug)
2476 Fprintf(stderr, "%ld: %s", ftell(stdout), line);
2477 (void) fputs(xcrypt(line), ofp);
2478 free(line);
2480 Fclose(ifp);
2481 Fclose(ofp);
2482 return;
2485 static char temp[32];
2487 static char *limit(name, pref) /* limit a name to 30 characters length */
2488 char *name;
2489 int pref;
2491 (void) strncpy(temp, name, pref ? 26 : 30);
2492 temp[pref ? 26 : 30] = 0;
2493 return temp;
2496 void
2497 do_objs()
2499 int i, sum = 0;
2500 char *c, *objnam;
2501 int nspell = 0;
2502 int prefix = 0;
2503 char class = '\0';
2504 boolean sumerr = FALSE;
2506 filename[0] = '\0';
2507 #ifdef FILE_PREFIX
2508 Strcat(filename, file_prefix);
2509 #endif
2510 Sprintf(eos(filename), INCLUDE_TEMPLATE, ONAME_FILE);
2511 if (!(ofp = fopen(filename, WRTMODE))) {
2512 perror(filename);
2513 exit(EXIT_FAILURE);
2515 Fprintf(ofp, "%s", Dont_Edit_Code);
2516 Fprintf(ofp, "#ifndef ONAMES_H\n#define ONAMES_H\n\n");
2518 for (i = 0; !i || objects[i].oc_class != ILLOBJ_CLASS; i++) {
2519 SpinCursor(3);
2521 objects[i].oc_name_idx = objects[i].oc_descr_idx = i; /* init */
2522 if (!(objnam = tmpdup(OBJ_NAME(objects[i]))))
2523 continue;
2525 /* make sure probabilities add up to 1000 */
2526 if (objects[i].oc_class != class) {
2527 if (sum && sum != 1000) {
2528 Fprintf(stderr, "prob error for class %d (%d%%)", class, sum);
2529 (void) fflush(stderr);
2530 sumerr = TRUE;
2532 class = objects[i].oc_class;
2533 sum = 0;
2536 for (c = objnam; *c; c++)
2537 if (*c >= 'a' && *c <= 'z')
2538 *c -= (char) ('a' - 'A');
2539 else if (*c < 'A' || *c > 'Z')
2540 *c = '_';
2542 switch (class) {
2543 case WAND_CLASS:
2544 Fprintf(ofp, "#define\tWAN_");
2545 prefix = 1;
2546 break;
2547 case RING_CLASS:
2548 Fprintf(ofp, "#define\tRIN_");
2549 prefix = 1;
2550 break;
2551 case POTION_CLASS:
2552 Fprintf(ofp, "#define\tPOT_");
2553 prefix = 1;
2554 break;
2555 case SPBOOK_CLASS:
2556 Fprintf(ofp, "#define\tSPE_");
2557 prefix = 1;
2558 nspell++;
2559 break;
2560 case SCROLL_CLASS:
2561 Fprintf(ofp, "#define\tSCR_");
2562 prefix = 1;
2563 break;
2564 case AMULET_CLASS:
2565 /* avoid trouble with stupid C preprocessors */
2566 Fprintf(ofp, "#define\t");
2567 if (objects[i].oc_material == PLASTIC) {
2568 Fprintf(ofp, "FAKE_AMULET_OF_YENDOR\t%d\n", i);
2569 prefix = -1;
2570 break;
2572 break;
2573 case GEM_CLASS:
2574 /* avoid trouble with stupid C preprocessors */
2575 if (objects[i].oc_material == GLASS) {
2576 Fprintf(ofp, "/* #define\t%s\t%d */\n", objnam, i);
2577 prefix = -1;
2578 break;
2580 default:
2581 Fprintf(ofp, "#define\t");
2583 if (prefix >= 0)
2584 Fprintf(ofp, "%s\t%d\n", limit(objnam, prefix), i);
2585 prefix = 0;
2587 sum += objects[i].oc_prob;
2590 /* check last set of probabilities */
2591 if (sum && sum != 1000) {
2592 Fprintf(stderr, "prob error for class %d (%d%%)", class, sum);
2593 (void) fflush(stderr);
2594 sumerr = TRUE;
2597 Fprintf(ofp, "#define\tLAST_GEM\t(JADE)\n");
2598 Fprintf(ofp, "#define\tMAXSPELL\t%d\n", nspell + 1);
2599 Fprintf(ofp, "#define\tNUM_OBJECTS\t%d\n", i);
2601 Fprintf(ofp, "\n/* Artifacts (unique objects) */\n\n");
2603 for (i = 1; artifact_names[i]; i++) {
2604 SpinCursor(3);
2606 for (c = objnam = tmpdup(artifact_names[i]); *c; c++)
2607 if (*c >= 'a' && *c <= 'z')
2608 *c -= (char) ('a' - 'A');
2609 else if (*c < 'A' || *c > 'Z')
2610 *c = '_';
2612 if (!strncmp(objnam, "THE_", 4))
2613 objnam += 4;
2614 /* fudge _platinum_ YENDORIAN EXPRESS CARD */
2615 if (!strncmp(objnam, "PLATINUM_", 9))
2616 objnam += 9;
2617 Fprintf(ofp, "#define\tART_%s\t%d\n", limit(objnam, 1), i);
2620 Fprintf(ofp, "#define\tNROFARTIFACTS\t%d\n", i - 1);
2621 Fprintf(ofp, "\n#endif /* ONAMES_H */\n");
2622 Fclose(ofp);
2623 if (sumerr)
2624 exit(EXIT_FAILURE);
2625 return;
2628 /* Read one line from input, up to and including the next newline
2629 * character. Returns a pointer to the heap-allocated string, or a
2630 * null pointer if no characters were read.
2632 static char *
2633 fgetline(fd)
2634 FILE *fd;
2636 static const int inc = 256;
2637 int len = inc;
2638 char *c = malloc(len), *ret;
2640 for (;;) {
2641 ret = fgets(c + len - inc, inc, fd);
2642 if (!ret) {
2643 free(c);
2644 c = NULL;
2645 break;
2646 } else if (index(c, '\n')) {
2647 /* normal case: we have a full line */
2648 break;
2650 len += inc;
2651 c = realloc(c, len);
2653 return c;
2656 static char *
2657 tmpdup(str)
2658 const char *str;
2660 static char buf[128];
2662 if (!str)
2663 return (char *) 0;
2664 (void) strncpy(buf, str, 127);
2665 return buf;
2668 static char *
2669 eos(str)
2670 char *str;
2672 while (*str)
2673 str++;
2674 return str;
2678 * macro used to control vision algorithms:
2679 * VISION_TABLES => generate tables
2682 void
2683 do_vision()
2685 #ifdef VISION_TABLES
2686 int i, j;
2688 /* Everything is clear. xclear may be malloc'ed.
2689 * Block the upper left corner (BLOCK_HEIGHTxBLOCK_WIDTH)
2691 for (i = 0; i < MAX_ROW; i++)
2692 for (j = 0; j < MAX_COL; j++)
2693 if (i < BLOCK_HEIGHT && j < BLOCK_WIDTH)
2694 xclear[i][j] = '\000';
2695 else
2696 xclear[i][j] = '\001';
2697 #endif /* VISION_TABLES */
2699 SpinCursor(3);
2702 * create the include file, "vis_tab.h"
2704 filename[0] = '\0';
2705 #ifdef FILE_PREFIX
2706 Strcat(filename, file_prefix);
2707 #endif
2708 Sprintf(filename, INCLUDE_TEMPLATE, VIS_TAB_H);
2709 if (!(ofp = fopen(filename, WRTMODE))) {
2710 perror(filename);
2711 exit(EXIT_FAILURE);
2713 Fprintf(ofp, "%s", Dont_Edit_Code);
2714 Fprintf(ofp, "#ifdef VISION_TABLES\n");
2715 #ifdef VISION_TABLES
2716 H_close_gen();
2717 H_far_gen();
2718 #endif /* VISION_TABLES */
2719 Fprintf(ofp, "\n#endif /* VISION_TABLES */\n");
2720 Fclose(ofp);
2722 SpinCursor(3);
2725 * create the source file, "vis_tab.c"
2727 filename[0] = '\0';
2728 #ifdef FILE_PREFIX
2729 Strcat(filename, file_prefix);
2730 #endif
2731 Sprintf(filename, SOURCE_TEMPLATE, VIS_TAB_C);
2732 if (!(ofp = fopen(filename, WRTMODE))) {
2733 perror(filename);
2734 Sprintf(filename, INCLUDE_TEMPLATE, VIS_TAB_H);
2735 Unlink(filename);
2736 exit(EXIT_FAILURE);
2738 Fprintf(ofp, "%s", Dont_Edit_Code);
2739 Fprintf(ofp, "#include \"config.h\"\n");
2740 Fprintf(ofp, "#ifdef VISION_TABLES\n");
2741 Fprintf(ofp, "#include \"vis_tab.h\"\n");
2743 SpinCursor(3);
2745 #ifdef VISION_TABLES
2746 C_close_gen();
2747 C_far_gen();
2748 Fprintf(ofp, "\nvoid vis_tab_init() { return; }\n");
2749 #endif /* VISION_TABLES */
2751 SpinCursor(3);
2753 Fprintf(ofp, "\n#endif /* VISION_TABLES */\n");
2754 Fprintf(ofp, "\n/*vis_tab.c*/\n");
2756 Fclose(ofp);
2757 return;
2760 #ifdef VISION_TABLES
2762 /*-------------- vision tables --------------*\
2764 * Generate the close and far tables. This is done by setting up a
2765 * fake dungeon and moving our source to different positions relative
2766 * to a block and finding the first/last visible position. The fake
2767 * dungeon is all clear execpt for the upper left corner (BLOCK_HEIGHT
2768 * by BLOCK_WIDTH) is blocked. Then we move the source around relative
2769 * to the corner of the block. For each new position of the source
2770 * we check positions on rows "kittycorner" from the source. We check
2771 * positions until they are either in sight or out of sight (depends on
2772 * which table we are generating). The picture below shows the setup
2773 * for the generation of the close table. The generation of the far
2774 * table would switch the quadrants of the '@' and the "Check rows
2775 * here".
2778 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2779 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2780 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,, Check rows here ,,,,,,,,,,,,
2781 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2782 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXB,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2783 * ...............................
2784 * ...............................
2785 * .........@.....................
2786 * ...............................
2788 * Table generation figure (close_table). The 'X's are blocked points.
2789 * The 'B' is a special blocked point. The '@' is the source. The ','s
2790 * are the target area. The '.' are just open areas.
2793 * Example usage of close_table[][][].
2795 * The table is as follows:
2797 * dy = |row of '@' - row of 'B'| - 1
2798 * dx = |col of '@' - col of 'B'|
2800 * The first indices are the deltas from the source '@' and the block 'B'.
2801 * You must check for the value inside the abs value bars being zero. If
2802 * so then the block is on the same row and you don't need to do a table
2803 * lookup. The last value:
2805 * dcy = |row of block - row to be checked|
2807 * Is the value of the first visible spot on the check row from the
2808 * block column. So
2810 * first visible col = close_table[dy][dx][dcy] + col of 'B'
2812 \*-------------- vision tables --------------*/
2814 static void
2815 H_close_gen()
2817 Fprintf(ofp, "\n/* Close */\n");
2818 Fprintf(ofp,
2819 "#define CLOSE_MAX_SB_DY %2d\t/* |src row - block row| - 1\t*/\n",
2820 TEST_HEIGHT - 1);
2821 Fprintf(ofp,
2822 "#define CLOSE_MAX_SB_DX %2d\t/* |src col - block col|\t*/\n",
2823 TEST_WIDTH);
2824 Fprintf(ofp,
2825 "#define CLOSE_MAX_BC_DY %2d\t/* |block row - check row|\t*/\n",
2826 TEST_HEIGHT);
2827 Fprintf(ofp, "typedef struct {\n");
2828 Fprintf(ofp,
2829 " unsigned char close[CLOSE_MAX_SB_DX][CLOSE_MAX_BC_DY];\n");
2830 Fprintf(ofp, "} close2d;\n");
2831 Fprintf(ofp, "extern close2d close_table[CLOSE_MAX_SB_DY];\n");
2832 return;
2835 static void
2836 H_far_gen()
2838 Fprintf(ofp, "\n/* Far */\n");
2839 Fprintf(ofp, "#define FAR_MAX_SB_DY %2d\t/* |src row - block row|\t*/\n",
2840 TEST_HEIGHT);
2841 Fprintf(ofp,
2842 "#define FAR_MAX_SB_DX %2d\t/* |src col - block col| - 1\t*/\n",
2843 TEST_WIDTH - 1);
2844 Fprintf(ofp,
2845 "#define FAR_MAX_BC_DY %2d\t/* |block row - check row| - 1\t*/\n",
2846 TEST_HEIGHT - 1);
2847 Fprintf(ofp, "typedef struct {\n");
2848 Fprintf(ofp, " unsigned char far_q[FAR_MAX_SB_DX][FAR_MAX_BC_DY];\n");
2849 Fprintf(ofp, "} far2d;\n");
2850 Fprintf(ofp, "extern far2d far_table[FAR_MAX_SB_DY];\n");
2851 return;
2854 static void
2855 C_close_gen()
2857 int i, dx, dy;
2858 int src_row, src_col; /* source */
2859 int block_row, block_col; /* block */
2860 int this_row;
2861 int no_more;
2862 const char *delim;
2864 block_row = BLOCK_HEIGHT - 1;
2865 block_col = BLOCK_WIDTH - 1;
2867 Fprintf(ofp, "\n#ifndef FAR_TABLE_ONLY\n");
2868 Fprintf(ofp, "\nclose2d close_table[CLOSE_MAX_SB_DY] = {\n");
2869 #ifndef no_vision_progress
2870 Fprintf(stderr, "\nclose:");
2871 #endif
2873 for (dy = 1; dy < TEST_HEIGHT; dy++) {
2874 src_row = block_row + dy;
2875 Fprintf(ofp, "/* DY = %2d (- 1)*/\n {{\n", dy);
2876 #ifndef no_vision_progress
2877 Fprintf(stderr, " %2d", dy), (void) fflush(stderr);
2878 #endif
2879 for (dx = 0; dx < TEST_WIDTH; dx++) {
2880 src_col = block_col - dx;
2881 Fprintf(ofp, " /*%2d*/ {", dx);
2883 no_more = 0;
2884 for (this_row = 0; this_row < TEST_HEIGHT; this_row++) {
2885 delim = (this_row < TEST_HEIGHT - 1) ? "," : "";
2886 if (no_more) {
2887 Fprintf(ofp, "%s%s", CLOSE_OFF_TABLE_STRING, delim);
2888 continue;
2890 SpinCursor(3);
2892 /* Find the first column that we can see. */
2893 for (i = block_col + 1; i < MAX_COL; i++) {
2894 if (clear_path(src_row, src_col, block_row - this_row, i))
2895 break;
2898 if (i == MAX_COL)
2899 no_more = 1;
2900 Fprintf(ofp, "%2d%s", i - block_col, delim);
2902 Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n");
2904 Fprintf(ofp, " }},\n");
2907 Fprintf(ofp, "}; /* close_table[] */\n"); /* closing brace for table */
2908 Fprintf(ofp, "#endif /* !FAR_TABLE_ONLY */\n");
2909 #ifndef no_vision_progress
2910 Fprintf(stderr, "\n");
2911 #endif
2912 return;
2915 static void
2916 C_far_gen()
2918 int i, dx, dy;
2919 int src_row, src_col; /* source */
2920 int block_row, block_col; /* block */
2921 int this_row;
2922 const char *delim;
2924 block_row = BLOCK_HEIGHT - 1;
2925 block_col = BLOCK_WIDTH - 1;
2927 Fprintf(ofp, "\n#ifndef CLOSE_TABLE_ONLY\n");
2928 Fprintf(ofp, "\nfar2d far_table[FAR_MAX_SB_DY] = {\n");
2929 #ifndef no_vision_progress
2930 Fprintf(stderr, "\n_far_:");
2931 #endif
2933 for (dy = 0; dy < TEST_HEIGHT; dy++) {
2934 src_row = block_row - dy;
2935 Fprintf(ofp, "/* DY = %2d */\n {{\n", dy);
2936 #ifndef no_vision_progress
2937 Fprintf(stderr, " %2d", dy), (void) fflush(stderr);
2938 #endif
2939 for (dx = 1; dx < TEST_WIDTH; dx++) {
2940 src_col = block_col + dx;
2941 Fprintf(ofp, " /*%2d(-1)*/ {", dx);
2943 for (this_row = block_row + 1; this_row < block_row + TEST_HEIGHT;
2944 this_row++) {
2945 delim = (this_row < block_row + TEST_HEIGHT - 1) ? "," : "";
2947 SpinCursor(3);
2948 /* Find first col that we can see. */
2949 for (i = 0; i <= block_col; i++) {
2950 if (clear_path(src_row, src_col, this_row, i))
2951 break;
2954 if (block_col - i < 0)
2955 Fprintf(ofp, "%s%s", FAR_OFF_TABLE_STRING, delim);
2956 else
2957 Fprintf(ofp, "%2d%s", block_col - i, delim);
2959 Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n");
2961 Fprintf(ofp, " }},\n");
2964 Fprintf(ofp, "}; /* far_table[] */\n"); /* closing brace for table */
2965 Fprintf(ofp, "#endif /* !CLOSE_TABLE_ONLY */\n");
2966 #ifndef no_vision_progress
2967 Fprintf(stderr, "\n");
2968 #endif
2969 return;
2973 * "Draw" a line from the hero to the given location. Stop if we hit a
2974 * wall.
2976 * Generalized integer Bresenham's algorithm (fast line drawing) for
2977 * all quadrants. From _Procedural Elements for Computer Graphics_, by
2978 * David F. Rogers. McGraw-Hill, 1985.
2980 * I have tried a little bit of optimization by pulling compares out of
2981 * the inner loops.
2983 * NOTE: This had better *not* be called from a position on the
2984 * same row as the hero.
2986 static int
2987 clear_path(you_row, you_col, y2, x2)
2988 int you_row, you_col, y2, x2;
2990 int dx, dy, s1, s2;
2991 register int i, error, x, y, dxs, dys;
2993 x = you_col;
2994 y = you_row;
2995 dx = abs(x2 - you_col);
2996 dy = abs(y2 - you_row);
2997 s1 = sign(x2 - you_col);
2998 s2 = sign(y2 - you_row);
3000 if (s1 == 0) { /* same column */
3001 if (s2 == 1) { /* below (larger y2 value) */
3002 for (i = you_row + 1; i < y2; i++)
3003 if (!xclear[i][you_col])
3004 return 0;
3005 } else { /* above (smaller y2 value) */
3006 for (i = y2 + 1; i < you_row; i++)
3007 if (!xclear[i][you_col])
3008 return 0;
3010 return 1;
3014 * Lines at 0 and 90 degrees have been weeded out.
3016 if (dy > dx) {
3017 error = dx;
3018 dx = dy;
3019 dy = error; /* swap the values */
3020 dxs = dx << 1; /* save the shifted values */
3021 dys = dy << 1;
3022 error = dys - dx; /* NOTE: error is used as a temporary above */
3024 for (i = 0; i < dx; i++) {
3025 if (!xclear[y][x])
3026 return 0; /* plot point */
3028 while (error >= 0) {
3029 x += s1;
3030 error -= dxs;
3032 y += s2;
3033 error += dys;
3035 } else {
3036 dxs = dx << 1; /* save the shifted values */
3037 dys = dy << 1;
3038 error = dys - dx;
3040 for (i = 0; i < dx; i++) {
3041 if (!xclear[y][x])
3042 return 0; /* plot point */
3044 while (error >= 0) {
3045 y += s2;
3046 error -= dxs;
3048 x += s1;
3049 error += dys;
3052 return 1;
3054 #endif /* VISION_TABLES */
3056 #ifdef STRICT_REF_DEF
3057 NEARDATA struct flag flags;
3058 #ifdef ATTRIB_H
3059 struct attribs attrmax, attrmin;
3060 #endif
3061 #endif /* STRICT_REF_DEF */
3063 /*makedefs.c*/