Unify putting a saddle on steed
[aNetHack.git] / util / makedefs.c
blobf8acd34a6faaf90f474895375d00e90e68c6b04d
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 TTY_TILES_ESCCODES
1327 "console escape codes for tile hinting",
1328 #endif
1329 #ifdef COM_COMPL
1330 "command line completion",
1331 #endif
1332 #ifdef LIFE
1333 "Conway's Game of Life",
1334 #endif
1335 #ifdef COMPRESS
1336 "data file compression",
1337 #endif
1338 #ifdef ZLIB_COMP
1339 "ZLIB data file compression",
1340 #endif
1341 #ifdef DLB
1342 "data librarian",
1343 #endif
1344 #ifdef MFLOPPY
1345 "floppy drive support",
1346 #endif
1347 #ifdef INSURANCE
1348 "insurance files for recovering from crashes",
1349 #endif
1350 #ifdef HOLD_LOCKFILE_OPEN
1351 "exclusive lock on level 0 file",
1352 #endif
1353 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
1354 "external program as a message handler",
1355 #endif
1356 #ifdef LOGFILE
1357 "log file",
1358 #endif
1359 #ifdef MAIL
1360 "mail daemon",
1361 #endif
1362 #ifdef GNUDOS
1363 "MSDOS protected mode",
1364 #endif
1365 #ifdef NEWS
1366 "news file",
1367 #endif
1368 #ifdef OVERLAY
1369 #ifdef MOVERLAY
1370 "MOVE overlays",
1371 #else
1372 #ifdef VROOMM
1373 "VROOMM overlays",
1374 #else
1375 "overlays",
1376 #endif
1377 #endif
1378 #endif
1379 #ifdef SELECTSAVED
1380 "restore saved games via menu",
1381 #endif
1382 #ifdef SCORE_ON_BOTL
1383 "score on status line",
1384 #endif
1385 #ifdef CLIPPING
1386 "screen clipping",
1387 #endif
1388 #ifdef NO_TERMS
1389 #ifdef MAC
1390 "screen control via mactty",
1391 #endif
1392 #ifdef SCREEN_BIOS
1393 "screen control via BIOS",
1394 #endif
1395 #ifdef SCREEN_DJGPPFAST
1396 "screen control via DJGPP fast",
1397 #endif
1398 #ifdef SCREEN_VGA
1399 "screen control via VGA graphics",
1400 #endif
1401 #ifdef WIN32CON
1402 "screen control via WIN32 console I/O",
1403 #endif
1404 #endif
1405 #ifdef SHELL
1406 "shell command",
1407 #endif
1408 #ifdef STATUS_VIA_WINDOWPORT
1409 # ifdef STATUS_HILITES
1410 "status via windowport with highlighting",
1411 # else
1412 "status via windowport without highlighting",
1413 # endif
1414 #else
1415 "traditional status display",
1416 #endif
1417 #ifdef SUSPEND
1418 "suspend command",
1419 #endif
1420 #ifdef TERMINFO
1421 "terminal info library",
1422 #else
1423 #if defined(TERMLIB) \
1424 || ((!defined(MICRO) && !defined(WIN32)) && defined(TTY_GRAPHICS))
1425 "terminal capability library",
1426 #endif
1427 #endif
1428 #ifdef TIMED_DELAY
1429 "timed wait for display effects",
1430 #endif
1431 #ifdef USER_SOUNDS
1432 "user sounds",
1433 #endif
1434 #ifdef PREFIXES_IN_USE
1435 "variable playground",
1436 #endif
1437 #ifdef VISION_TABLES
1438 "vision tables",
1439 #endif
1440 #ifdef ZEROCOMP
1441 "zero-compressed save files",
1442 #endif
1443 #ifdef RLECOMP
1444 "run-length compression of map in save files",
1445 #endif
1446 #ifdef SYSCF
1447 "system configuration at run-time",
1448 #endif
1449 save_bones_compat_buf, "and basic NetHack features"
1452 struct win_info {
1453 const char *id, /* DEFAULT_WINDOW_SYS string */
1454 *name; /* description, often same as id */
1456 static struct win_info window_opts[] = {
1457 #ifdef TTY_GRAPHICS
1458 { "tty", "traditional tty-based graphics" },
1459 #endif
1460 #ifdef X11_GRAPHICS
1461 { "X11", "X11" },
1462 #endif
1463 #ifdef QT_GRAPHICS
1464 { "Qt", "Qt" },
1465 #endif
1466 #ifdef GNOME_GRAPHICS
1467 { "Gnome", "Gnome" },
1468 #endif
1469 #ifdef MAC
1470 { "mac", "Mac" },
1471 #endif
1472 #ifdef AMIGA_INTUITION
1473 { "amii", "Amiga Intuition" },
1474 #endif
1475 #ifdef GEM_GRAPHICS
1476 { "Gem", "Gem" },
1477 #endif
1478 #ifdef MSWIN_GRAPHICS
1479 { "mswin", "mswin" },
1480 #endif
1481 #ifdef BEOS_GRAPHICS
1482 { "BeOS", "BeOS InterfaceKit" },
1483 #endif
1484 { 0, 0 }
1487 static void
1488 windowing_sanity()
1490 #ifndef DEFAULT_WINDOW_SYS
1491 /* pre-standard compilers didn't support #error; wait til run-time */
1492 Fprintf(stderr,
1493 "Configuration error: DEFAULT_WINDOW_SYS is not defined.\n");
1494 exit(EXIT_FAILURE);
1495 /*NOTREACHED*/
1497 /* put in a dummy value so that do_options() will compile and makedefs
1498 will build, otherwise the message above won't ever get delivered */
1499 #define DEFAULT_WINDOW_SYS "<undefined>"
1500 #else /*DEFAULT_WINDOW_SYS*/
1502 if (!window_opts[0].id) {
1503 Fprintf(stderr, "Configuration error: no windowing systems "
1504 "(TTY_GRAPHICS, &c) enabled.\n");
1505 exit(EXIT_FAILURE);
1509 int i;
1511 for (i = 0; window_opts[i].id; ++i)
1512 if (!strcmp(window_opts[i].id, DEFAULT_WINDOW_SYS))
1513 break;
1514 if (!window_opts[i]
1515 .id) { /* went through whole list without a match */
1516 Fprintf(stderr, "Configuration error: DEFAULT_WINDOW_SYS (%s)\n",
1517 DEFAULT_WINDOW_SYS);
1518 Fprintf(stderr,
1519 " does not match any enabled windowing system (%s%s).\n",
1520 window_opts[0].id, window_opts[1].id ? ", &c" : "");
1521 exit(EXIT_FAILURE);
1524 #endif /*DEFAULT_WINDOW_SYS*/
1527 void
1528 do_options()
1530 static const char indent[] = " ";
1531 const char *str, *sep;
1532 char *word, buf[BUFSZ];
1533 int i, length, winsyscnt;
1535 windowing_sanity();
1537 filename[0] = '\0';
1538 #ifdef FILE_PREFIX
1539 Strcat(filename, file_prefix);
1540 #endif
1541 Sprintf(eos(filename), DATA_TEMPLATE, OPTIONS_FILE);
1542 if (!(ofp = fopen(filename, WRTMODE))) {
1543 perror(filename);
1544 exit(EXIT_FAILURE);
1547 build_savebones_compat_string();
1548 Fprintf(ofp,
1549 #ifdef BETA
1550 "\n NetHack version %d.%d.%d [beta]\n",
1551 #else
1552 "\n NetHack version %d.%d.%d\n",
1553 #endif
1554 VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL);
1556 Fprintf(ofp, "\nOptions compiled into this edition:\n");
1557 length = COLNO + 1; /* force 1st item onto new line */
1558 for (i = 0; i < SIZE(build_opts); i++) {
1559 str = strcpy(buf, build_opts[i]);
1560 while (*str) {
1561 word = index(str, ' ');
1562 if (word)
1563 *word = '\0';
1564 if (length + strlen(str) > COLNO - 5)
1565 Fprintf(ofp, "\n%s", indent), length = strlen(indent);
1566 else
1567 Fprintf(ofp, " "), length++;
1568 Fprintf(ofp, "%s", str), length += strlen(str);
1569 str += strlen(str) + (word ? 1 : 0);
1571 Fprintf(ofp, (i < SIZE(build_opts) - 1) ? "," : "."), length++;
1574 winsyscnt = SIZE(window_opts) - 1;
1575 Fprintf(ofp, "\n\nSupported windowing system%s:\n",
1576 (winsyscnt > 1) ? "s" : "");
1577 length = COLNO + 1; /* force 1st item onto new line */
1578 for (i = 0; i < winsyscnt; i++) {
1579 str = window_opts[i].name;
1580 if (length + strlen(str) > COLNO - 5)
1581 Fprintf(ofp, "\n%s", indent), length = strlen(indent);
1582 else
1583 Fprintf(ofp, " "), length++;
1584 Fprintf(ofp, "%s", str), length += strlen(str);
1585 sep = (winsyscnt == 1)
1586 ? "."
1587 : (winsyscnt == 2)
1588 ? ((i == 0) ? " and" : "")
1589 : (i < winsyscnt - 2)
1590 ? ","
1591 : ((i == winsyscnt - 2) ? ", and" : "");
1592 Fprintf(ofp, "%s", sep), length += strlen(sep);
1594 if (winsyscnt > 1)
1595 Fprintf(ofp, "\n%swith a default of %s.", indent, DEFAULT_WINDOW_SYS);
1596 Fprintf(ofp, "\n\n");
1598 Fclose(ofp);
1599 return;
1602 /* routine to decide whether to discard something from data.base */
1603 static boolean
1604 d_filter(line)
1605 char *line;
1607 if (*line == '#')
1608 return TRUE; /* ignore comment lines */
1609 return FALSE;
1614 New format (v3.1) of 'data' file which allows much faster lookups [pr]
1615 "do not edit" first record is a comment line
1616 01234567 hexadecimal formatted offset to text area
1617 name-a first name of interest
1618 123,4 offset to name's text, and number of lines for it
1619 name-b next name of interest
1620 name-c multiple names which share same description also
1621 456,7 share a single offset,count line
1622 . sentinel to mark end of names
1623 789,0 dummy record containing offset, count of EOF
1624 text-a 4 lines of descriptive text for name-a
1625 text-a at file position 0x01234567L + 123L
1626 text-a
1627 text-a
1628 text-b/text-c 7 lines of text for names-b and -c
1629 text-b/text-c at fseek(0x01234567L + 456L)
1634 void
1635 do_data()
1637 char infile[60], tempfile[60];
1638 boolean ok;
1639 long txt_offset;
1640 int entry_cnt, line_cnt;
1641 char *line;
1643 Sprintf(tempfile, DATA_TEMPLATE, "database.tmp");
1644 filename[0] = '\0';
1645 #ifdef FILE_PREFIX
1646 Strcat(filename, file_prefix);
1647 #endif
1648 Sprintf(eos(filename), DATA_TEMPLATE, DATA_FILE);
1649 Sprintf(infile, DATA_IN_TEMPLATE, DATA_FILE);
1650 #ifdef SHORT_FILENAMES
1651 Strcat(infile, ".bas");
1652 #else
1653 Strcat(infile, ".base");
1654 #endif
1655 if (!(ifp = fopen(infile, RDTMODE))) { /* data.base */
1656 perror(infile);
1657 exit(EXIT_FAILURE);
1659 if (!(ofp = fopen(filename, WRTMODE))) { /* data */
1660 perror(filename);
1661 Fclose(ifp);
1662 exit(EXIT_FAILURE);
1664 if (!(tfp = fopen(tempfile, WRTMODE))) { /* database.tmp */
1665 perror(tempfile);
1666 Fclose(ifp);
1667 Fclose(ofp);
1668 Unlink(filename);
1669 exit(EXIT_FAILURE);
1672 /* output a dummy header record; we'll rewind and overwrite it later */
1673 Fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, 0L);
1675 entry_cnt = line_cnt = 0;
1676 /* read through the input file and split it into two sections */
1677 while ((line = fgetline(ifp)) != 0) {
1678 if (d_filter(line)) {
1679 free(line);
1680 continue;
1682 if (*line > ' ') { /* got an entry name */
1683 /* first finish previous entry */
1684 if (line_cnt)
1685 Fprintf(ofp, "%d\n", line_cnt), line_cnt = 0;
1686 /* output the entry name */
1687 (void) fputs(line, ofp);
1688 entry_cnt++; /* update number of entries */
1689 } else if (entry_cnt) { /* got some descriptive text */
1690 /* update previous entry with current text offset */
1691 if (!line_cnt)
1692 Fprintf(ofp, "%ld,", ftell(tfp));
1693 /* save the text line in the scratch file */
1694 (void) fputs(line, tfp);
1695 line_cnt++; /* update line counter */
1697 free(line);
1699 /* output an end marker and then record the current position */
1700 if (line_cnt)
1701 Fprintf(ofp, "%d\n", line_cnt);
1702 Fprintf(ofp, ".\n%ld,%d\n", ftell(tfp), 0);
1703 txt_offset = ftell(ofp);
1704 Fclose(ifp); /* all done with original input file */
1706 /* reprocess the scratch file; 1st format an error msg, just in case */
1707 line = malloc(256);
1708 Sprintf(line, "rewind of \"%s\"", tempfile);
1709 if (rewind(tfp) != 0)
1710 goto dead_data;
1711 free(line);
1712 /* copy all lines of text from the scratch file into the output file */
1713 while ((line = fgetline(tfp)) != 0) {
1714 (void) fputs(line, ofp);
1715 free(line);
1718 /* finished with scratch file */
1719 Fclose(tfp);
1720 Unlink(tempfile); /* remove it */
1722 /* update the first record of the output file; prepare error msg 1st */
1723 line = malloc(256);
1724 Sprintf(line, "rewind of \"%s\"", filename);
1725 ok = (rewind(ofp) == 0);
1726 if (ok) {
1727 Sprintf(line, "header rewrite of \"%s\"", filename);
1728 ok = (fprintf(ofp, "%s%08lx\n", Dont_Edit_Data,
1729 (unsigned long) txt_offset) >= 0);
1731 if (!ok) {
1732 dead_data:
1733 perror(line); /* report the problem */
1734 free(line);
1735 /* close and kill the aborted output file, then give up */
1736 Fclose(ofp);
1737 Unlink(filename);
1738 exit(EXIT_FAILURE);
1740 free(line);
1742 /* all done */
1743 Fclose(ofp);
1745 return;
1748 /* routine to decide whether to discard something from oracles.txt */
1749 static boolean
1750 h_filter(line)
1751 char *line;
1753 static boolean skip = FALSE;
1754 char *tag;
1756 SpinCursor(3);
1758 if (*line == '#')
1759 return TRUE; /* ignore comment lines */
1761 tag = malloc(strlen(line));
1762 if (sscanf(line, "----- %s", tag) == 1) {
1763 skip = FALSE;
1764 } else if (skip && !strncmp(line, "-----", 5))
1765 skip = FALSE;
1766 free(tag);
1767 return skip;
1770 static const char *special_oracle[] = {
1771 "\"...it is rather disconcerting to be confronted with the",
1772 "following theorem from [Baker, Gill, and Solovay, 1975].", "",
1773 "Theorem 7.18 There exist recursive languages A and B such that",
1774 " (1) P(A) == NP(A), and", " (2) P(B) != NP(B)", "",
1775 "This provides impressive evidence that the techniques that are",
1776 "currently available will not suffice for proving that P != NP or "
1777 " ",
1778 "that P == NP.\" [Garey and Johnson, p. 185.]"
1782 The oracle file consists of a "do not edit" comment, a decimal count N
1783 and set of N+1 hexadecimal fseek offsets, followed by N multiple-line
1784 records, separated by "---" lines. The first oracle is a special case.
1785 The input data contains just those multi-line records, separated by
1786 "-----" lines.
1789 void
1790 do_oracles()
1792 char infile[60], tempfile[60];
1793 boolean in_oracle, ok;
1794 long fpos;
1795 unsigned long txt_offset, offset;
1796 int oracle_cnt;
1797 register int i;
1798 char *line;
1800 Sprintf(tempfile, DATA_TEMPLATE, "oracles.tmp");
1801 filename[0] = '\0';
1802 #ifdef FILE_PREFIX
1803 Strcat(filename, file_prefix);
1804 #endif
1805 Sprintf(eos(filename), DATA_TEMPLATE, ORACLE_FILE);
1806 Sprintf(infile, DATA_IN_TEMPLATE, ORACLE_FILE);
1807 Strcat(infile, ".txt");
1808 if (!(ifp = fopen(infile, RDTMODE))) {
1809 perror(infile);
1810 exit(EXIT_FAILURE);
1812 if (!(ofp = fopen(filename, WRTMODE))) {
1813 perror(filename);
1814 Fclose(ifp);
1815 exit(EXIT_FAILURE);
1817 if (!(tfp = fopen(tempfile, WRTMODE))) { /* oracles.tmp */
1818 perror(tempfile);
1819 Fclose(ifp);
1820 Fclose(ofp);
1821 Unlink(filename);
1822 exit(EXIT_FAILURE);
1825 /* output a dummy header record; we'll rewind and overwrite it later */
1826 Fprintf(ofp, "%s%5d\n", Dont_Edit_Data, 0);
1828 /* handle special oracle; it must come first */
1829 (void) fputs("---\n", tfp);
1830 offset = (unsigned long) ftell(tfp);
1831 Fprintf(ofp, "%05lx\n", offset); /* start pos of special oracle */
1832 for (i = 0; i < SIZE(special_oracle); i++) {
1833 (void) fputs(xcrypt(special_oracle[i]), tfp);
1834 (void) fputc('\n', tfp);
1836 SpinCursor(3);
1838 oracle_cnt = 1;
1839 (void) fputs("---\n", tfp);
1840 offset = (unsigned long) ftell(tfp);
1841 Fprintf(ofp, "%05lx\n", offset); /* start pos of first oracle */
1842 in_oracle = FALSE;
1844 while ((line = fgetline(ifp)) != 0) {
1845 SpinCursor(3);
1847 if (h_filter(line)) {
1848 free(line);
1849 continue;
1851 if (!strncmp(line, "-----", 5)) {
1852 if (!in_oracle) {
1853 free(line);
1854 continue;
1856 in_oracle = FALSE;
1857 oracle_cnt++;
1858 (void) fputs("---\n", tfp);
1859 offset = (unsigned long) ftell(tfp);
1860 Fprintf(ofp, "%05lx\n", offset); /* start pos of this oracle */
1861 } else {
1862 in_oracle = TRUE;
1863 (void) fputs(xcrypt(line), tfp);
1865 free(line);
1868 if (in_oracle) { /* need to terminate last oracle */
1869 oracle_cnt++;
1870 (void) fputs("---\n", tfp);
1871 offset = (unsigned long) ftell(tfp);
1872 Fprintf(ofp, "%05lx\n", offset); /* eof position */
1875 /* record the current position */
1876 txt_offset = (unsigned long) ftell(ofp);
1877 Fclose(ifp); /* all done with original input file */
1879 /* reprocess the scratch file; 1st format an error msg, just in case */
1880 line = malloc(256);
1881 Sprintf(line, "rewind of \"%s\"", tempfile);
1882 if (rewind(tfp) != 0)
1883 goto dead_data;
1884 free(line);
1885 /* copy all lines of text from the scratch file into the output file */
1886 while ((line = fgetline(tfp)) != 0) {
1887 (void) fputs(line, ofp);
1888 free(line);
1891 /* finished with scratch file */
1892 Fclose(tfp);
1893 Unlink(tempfile); /* remove it */
1895 /* update the first record of the output file; prepare error msg 1st */
1896 line = malloc(256);
1897 Sprintf(line, "rewind of \"%s\"", filename);
1898 ok = (rewind(ofp) == 0);
1899 if (ok) {
1900 Sprintf(line, "header rewrite of \"%s\"", filename);
1901 ok = (fprintf(ofp, "%s%5d\n", Dont_Edit_Data, oracle_cnt) >= 0);
1903 if (ok) {
1904 Sprintf(line, "data rewrite of \"%s\"", filename);
1905 for (i = 0; i <= oracle_cnt; i++) {
1906 #ifndef VMS /* alpha/vms v1.0; this fflush seems to confuse ftell */
1907 if (!(ok = (fflush(ofp) == 0)))
1908 break;
1909 #endif
1910 if (!(ok = (fpos = ftell(ofp)) >= 0))
1911 break;
1912 if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0)))
1913 break;
1914 if (!(ok = (fscanf(ofp, "%5lx", &offset) == 1)))
1915 break;
1916 #ifdef MAC
1917 #ifdef __MWERKS__
1919 MetroWerks CodeWarrior Pro 1's (AKA CW12) version of MSL
1920 (ANSI C Libraries) needs this rewind or else the fprintf
1921 stops working. This may also be true for CW11, but has
1922 never been checked.
1924 rewind(ofp);
1925 #endif
1926 #endif
1927 if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0)))
1928 break;
1929 offset += txt_offset;
1930 if (!(ok = (fprintf(ofp, "%05lx\n", offset) >= 0)))
1931 break;
1934 if (!ok) {
1935 dead_data:
1936 perror(line); /* report the problem */
1937 free(line);
1938 /* close and kill the aborted output file, then give up */
1939 Fclose(ofp);
1940 Unlink(filename);
1941 exit(EXIT_FAILURE);
1943 free(line);
1945 /* all done */
1946 Fclose(ofp);
1948 return;
1951 void
1952 do_dungeon()
1954 int rcnt = 0;
1955 char *line;
1957 Sprintf(filename, DATA_IN_TEMPLATE, DGN_I_FILE);
1958 if (!(ifp = fopen(filename, RDTMODE))) {
1959 perror(filename);
1960 exit(EXIT_FAILURE);
1962 filename[0] = '\0';
1963 #ifdef FILE_PREFIX
1964 Strcat(filename, file_prefix);
1965 #endif
1966 Sprintf(eos(filename), DGN_TEMPLATE, DGN_O_FILE);
1967 if (!(ofp = fopen(filename, WRTMODE))) {
1968 perror(filename);
1969 exit(EXIT_FAILURE);
1971 Fprintf(ofp, "%s", Dont_Edit_Data);
1973 tfp = getfp(DATA_TEMPLATE, "grep.tmp", WRTMODE);
1974 grep0(ifp, tfp);
1975 ifp = getfp(DATA_TEMPLATE, "grep.tmp", RDTMODE);
1977 while ((line = fgetline(ifp)) != 0) {
1978 SpinCursor(3);
1980 rcnt++;
1981 if (line[0] == '#') {
1982 free(line);
1983 continue; /* discard comments */
1985 (void) fputs(line, ofp);
1986 free(line);
1988 Fclose(ifp);
1989 Fclose(ofp);
1991 delete_file(DATA_TEMPLATE, "grep.tmp");
1992 return;
1995 static boolean
1996 ranged_attk(ptr) /* returns TRUE if monster can attack at range */
1997 register struct permonst *ptr;
1999 register int i, j;
2000 register int atk_mask = (1 << AT_BREA) | (1 << AT_SPIT) | (1 << AT_GAZE);
2002 for (i = 0; i < NATTK; i++) {
2003 if ((j = ptr->mattk[i].aatyp) >= AT_WEAP || (atk_mask & (1 << j)))
2004 return TRUE;
2007 return FALSE;
2010 /* This routine is designed to return an integer value which represents
2011 * an approximation of monster strength. It uses a similar method of
2012 * determination as "experience()" to arrive at the strength.
2014 static int
2015 mstrength(ptr)
2016 struct permonst *ptr;
2018 int i, tmp2, n, tmp = ptr->mlevel;
2020 if (tmp > 49) /* special fixed hp monster */
2021 tmp = 2 * (tmp - 6) / 4;
2023 /* For creation in groups */
2024 n = (!!(ptr->geno & G_SGROUP));
2025 n += (!!(ptr->geno & G_LGROUP)) << 1;
2027 /* For ranged attacks */
2028 if (ranged_attk(ptr))
2029 n++;
2031 /* For higher ac values */
2032 n += (ptr->ac < 4);
2033 n += (ptr->ac < 0);
2035 /* For very fast monsters */
2036 n += (ptr->mmove >= 18);
2038 /* For each attack and "special" attack */
2039 for (i = 0; i < NATTK; i++) {
2040 tmp2 = ptr->mattk[i].aatyp;
2041 n += (tmp2 > 0);
2042 n += (tmp2 == AT_MAGC);
2043 n += (tmp2 == AT_WEAP && (ptr->mflags2 & M2_STRONG));
2046 /* For each "special" damage type */
2047 for (i = 0; i < NATTK; i++) {
2048 tmp2 = ptr->mattk[i].adtyp;
2049 if ((tmp2 == AD_DRLI) || (tmp2 == AD_STON) || (tmp2 == AD_DRST)
2050 || (tmp2 == AD_DRDX) || (tmp2 == AD_DRCO) || (tmp2 == AD_WERE))
2051 n += 2;
2052 else if (strcmp(ptr->mname, "grid bug"))
2053 n += (tmp2 != AD_PHYS);
2054 n += ((int) (ptr->mattk[i].damd * ptr->mattk[i].damn) > 23);
2057 /* Leprechauns are special cases. They have many hit dice so they can
2058 hit and are hard to kill, but they don't really do much damage. */
2059 if (!strcmp(ptr->mname, "leprechaun"))
2060 n -= 2;
2062 /* Finally, adjust the monster level 0 <= n <= 24 (approx.) */
2063 if (n == 0)
2064 tmp--;
2065 else if (n >= 6)
2066 tmp += (n / 2);
2067 else
2068 tmp += (n / 3 + 1);
2070 return (tmp >= 0) ? tmp : 0;
2073 void
2074 do_monstr()
2076 register struct permonst *ptr;
2077 register int i, j;
2080 * create the source file, "monstr.c"
2082 filename[0] = '\0';
2083 #ifdef FILE_PREFIX
2084 Strcat(filename, file_prefix);
2085 #endif
2086 Sprintf(eos(filename), SOURCE_TEMPLATE, MON_STR_C);
2087 if (!(ofp = fopen(filename, WRTMODE))) {
2088 perror(filename);
2089 exit(EXIT_FAILURE);
2091 Fprintf(ofp, "%s", Dont_Edit_Code);
2092 Fprintf(ofp, "#include \"config.h\"\n");
2093 Fprintf(ofp, "\nconst int monstr[] = {\n");
2094 for (ptr = &mons[0], j = 0; ptr->mlet; ptr++) {
2095 SpinCursor(3);
2097 i = mstrength(ptr);
2098 Fprintf(ofp, "%2d,%c", i, (++j & 15) ? ' ' : '\n');
2100 /* might want to insert a final 0 entry here instead of just newline */
2101 Fprintf(ofp, "%s};\n", (j & 15) ? "\n" : "");
2103 Fprintf(ofp, "\nvoid NDECL(monstr_init);\n");
2104 Fprintf(ofp, "\nvoid\n");
2105 Fprintf(ofp, "monstr_init()\n");
2106 Fprintf(ofp, "{\n");
2107 Fprintf(ofp, " return;\n");
2108 Fprintf(ofp, "}\n");
2109 Fprintf(ofp, "\n/*monstr.c*/\n");
2111 Fclose(ofp);
2112 return;
2115 void
2116 do_permonst()
2118 int i;
2119 char *c, *nam;
2121 filename[0] = '\0';
2122 #ifdef FILE_PREFIX
2123 Strcat(filename, file_prefix);
2124 #endif
2125 Sprintf(eos(filename), INCLUDE_TEMPLATE, MONST_FILE);
2126 if (!(ofp = fopen(filename, WRTMODE))) {
2127 perror(filename);
2128 exit(EXIT_FAILURE);
2130 Fprintf(ofp, "%s", Dont_Edit_Code);
2131 Fprintf(ofp, "#ifndef PM_H\n#define PM_H\n");
2133 if (strcmp(mons[0].mname, "playermon") != 0)
2134 Fprintf(ofp, "\n#define\tPM_PLAYERMON\t(-1)");
2136 for (i = 0; mons[i].mlet; i++) {
2137 SpinCursor(3);
2139 Fprintf(ofp, "\n#define\tPM_");
2140 if (mons[i].mlet == S_HUMAN && !strncmp(mons[i].mname, "were", 4))
2141 Fprintf(ofp, "HUMAN_");
2142 for (nam = c = tmpdup(mons[i].mname); *c; c++)
2143 if (*c >= 'a' && *c <= 'z')
2144 *c -= (char) ('a' - 'A');
2145 else if (*c < 'A' || *c > 'Z')
2146 *c = '_';
2147 Fprintf(ofp, "%s\t%d", nam, i);
2149 Fprintf(ofp, "\n\n#define\tNUMMONS\t%d\n", i);
2150 Fprintf(ofp, "\n#endif /* PM_H */\n");
2151 Fclose(ofp);
2152 return;
2155 /* Start of Quest text file processing. */
2156 #include "qtext.h"
2158 static struct qthdr qt_hdr;
2159 static struct msghdr msg_hdr[N_HDR];
2160 static struct qtmsg *curr_msg;
2162 static int qt_line;
2164 static boolean in_msg;
2165 #define NO_MSG 1 /* strlen of a null line returned by fgets() */
2167 static boolean
2168 qt_comment(s)
2169 char *s;
2171 if (s[0] == '#')
2172 return TRUE;
2173 return (boolean) (!in_msg && strlen(s) == NO_MSG);
2176 static boolean
2177 qt_control(s)
2178 char *s;
2180 return (boolean) (s[0] == '%' && (s[1] == 'C' || s[1] == 'E'));
2183 static int
2184 get_hdr(code)
2185 char *code;
2187 int i;
2189 for (i = 0; i < qt_hdr.n_hdr; i++)
2190 if (!strncmp(code, qt_hdr.id[i], LEN_HDR))
2191 return ++i;
2193 return 0;
2196 static boolean
2197 new_id(code)
2198 char *code;
2200 if (qt_hdr.n_hdr >= N_HDR) {
2201 Fprintf(stderr, OUT_OF_HEADERS, qt_line);
2202 return FALSE;
2205 strncpy(&qt_hdr.id[qt_hdr.n_hdr][0], code, LEN_HDR);
2206 msg_hdr[qt_hdr.n_hdr].n_msg = 0;
2207 qt_hdr.offset[qt_hdr.n_hdr++] = 0L;
2208 return TRUE;
2211 static boolean
2212 known_msg(num, id)
2213 int num, id;
2215 int i;
2217 for (i = 0; i < msg_hdr[num].n_msg; i++)
2218 if (msg_hdr[num].qt_msg[i].msgnum == id)
2219 return TRUE;
2221 return FALSE;
2224 static void
2225 new_msg(s, num, id)
2226 char *s;
2227 int num, id;
2229 struct qtmsg *qt_msg;
2231 if (msg_hdr[num].n_msg >= N_MSG) {
2232 Fprintf(stderr, OUT_OF_MESSAGES, qt_line);
2233 } else {
2234 qt_msg = &(msg_hdr[num].qt_msg[msg_hdr[num].n_msg++]);
2235 qt_msg->msgnum = id;
2236 qt_msg->delivery = s[2];
2237 qt_msg->offset = qt_msg->size = qt_msg->summary_size = 0L;
2239 curr_msg = qt_msg;
2243 /* check %E record for "[summary text]" that nethack can stuff into the
2244 message history buffer when delivering text via window instead of pline */
2245 static char *
2246 valid_qt_summary(s, parsing)
2247 char *s; /* end record: "%E" optionally followed by " [summary]" */
2248 boolean parsing; /* curr_msg is valid iff this is True */
2250 static char summary[BUFSZ];
2251 char *p;
2253 if (*s != '%' || *(s + 1) != 'E')
2254 return (char *) 0;
2255 if ((p = index(s, '[')) == 0)
2256 return (char *) 0;
2257 /* note: opening '[' and closing ']' will be retained in the output;
2258 anything after ']' will be discarded by putting a newline there */
2259 Strcpy(summary, p);
2261 /* have an opening bracket; summary[] holds it and all text that follows
2263 p = eos(summary);
2264 /* find closing bracket */
2265 while (p > summary && *(p - 1) != ']')
2266 --p;
2268 if (p == summary) {
2269 /* we backed up all the way to the start without finding a bracket */
2270 if (parsing) /* malformed summary */
2271 Fprintf(stderr, MAL_SUM, qt_line);
2272 } else if (p == summary + 1) {
2273 ; /* ignore empty [] */
2274 } else { /* got something */
2275 /* p points one spot past ']', usually to '\n';
2276 we need to include the \n as part of the size */
2277 if (parsing) {
2278 /* during the writing pass we won't be able to recheck
2279 delivery, so any useless summary for a pline mode
2280 message has to be carried along to the output file */
2281 if (curr_msg->delivery == 'p')
2282 Fprintf(stderr, DUMB_SUM, qt_line);
2283 /* +1 is for terminating newline */
2284 curr_msg->summary_size = (long) (p - summary) + 1L;
2285 } else {
2286 /* caller is writing rather than just parsing;
2287 force newline after the closing bracket */
2288 Strcpy(p, "\n");
2290 return summary;
2292 return (char *) 0;
2295 static void
2296 do_qt_control(s)
2297 char *s;
2299 char code[BUFSZ];
2300 int num, id = 0;
2302 if (!index(s, '\n'))
2303 Fprintf(stderr, CTRL_TRUNC, qt_line);
2305 switch (s[1]) {
2306 case 'C':
2307 if (in_msg) {
2308 Fprintf(stderr, CREC_IN_MSG, qt_line);
2309 break;
2310 } else {
2311 in_msg = TRUE;
2312 if (sscanf(&s[4], "%s %5d", code, &id) != 2) {
2313 Fprintf(stderr, UNREC_CREC, qt_line);
2314 break;
2316 num = get_hdr(code);
2317 if (!num && !new_id(code))
2318 break;
2319 num = get_hdr(code) - 1;
2320 if (known_msg(num, id))
2321 Fprintf(stderr, DUP_MSG, qt_line);
2322 else
2323 new_msg(s, num, id);
2325 break;
2327 case 'E':
2328 if (!in_msg) {
2329 Fprintf(stderr, END_NOT_IN_MSG, qt_line);
2330 } else {
2331 /* sets curr_msg->summary_size if applicable */
2332 (void) valid_qt_summary(s, TRUE);
2333 in_msg = FALSE;
2335 break;
2337 default:
2338 Fprintf(stderr, UNREC_CREC, qt_line);
2339 break;
2343 static void
2344 do_qt_text(s)
2345 char *s;
2347 if (!in_msg) {
2348 Fprintf(stderr, TEXT_NOT_IN_MSG, qt_line);
2349 } else if (!index(s, '\n')) {
2350 Fprintf(stderr, TEXT_TRUNC, qt_line);
2353 curr_msg->size += strlen(s);
2354 return;
2357 static void
2358 adjust_qt_hdrs()
2360 int i, j;
2361 long count = 0L, hdr_offset = sizeof(int)
2362 + (sizeof(char) * LEN_HDR + sizeof(long))
2363 * qt_hdr.n_hdr;
2365 for (i = 0; i < qt_hdr.n_hdr; i++) {
2366 qt_hdr.offset[i] = hdr_offset;
2367 hdr_offset += sizeof(int) + sizeof(struct qtmsg) * msg_hdr[i].n_msg;
2370 for (i = 0; i < qt_hdr.n_hdr; i++)
2371 for (j = 0; j < msg_hdr[i].n_msg; j++) {
2372 msg_hdr[i].qt_msg[j].offset = hdr_offset + count;
2373 count +=
2374 msg_hdr[i].qt_msg[j].size + msg_hdr[i].qt_msg[j].summary_size;
2376 return;
2379 static void
2380 put_qt_hdrs()
2382 int i;
2385 * The main header record.
2387 if (debug)
2388 Fprintf(stderr, "%ld: header info.\n", ftell(ofp));
2389 (void) fwrite((genericptr_t) & (qt_hdr.n_hdr), sizeof(int), 1, ofp);
2390 (void) fwrite((genericptr_t) & (qt_hdr.id[0][0]), sizeof(char) * LEN_HDR,
2391 qt_hdr.n_hdr, ofp);
2392 (void) fwrite((genericptr_t) & (qt_hdr.offset[0]), sizeof(long),
2393 qt_hdr.n_hdr, ofp);
2394 if (debug) {
2395 for (i = 0; i < qt_hdr.n_hdr; i++)
2396 Fprintf(stderr, "%s @ %ld, ", qt_hdr.id[i], qt_hdr.offset[i]);
2397 Fprintf(stderr, "\n");
2401 * The individual class headers.
2403 for (i = 0; i < qt_hdr.n_hdr; i++) {
2404 if (debug)
2405 Fprintf(stderr, "%ld: %s header info.\n", ftell(ofp),
2406 qt_hdr.id[i]);
2407 (void) fwrite((genericptr_t) & (msg_hdr[i].n_msg), sizeof(int), 1,
2408 ofp);
2409 (void) fwrite((genericptr_t) & (msg_hdr[i].qt_msg[0]),
2410 sizeof(struct qtmsg), msg_hdr[i].n_msg, ofp);
2411 if (debug) {
2412 int j;
2414 for (j = 0; j < msg_hdr[i].n_msg; j++) {
2415 Fprintf(stderr, "msg %d @ %ld (%ld)",
2416 msg_hdr[i].qt_msg[j].msgnum,
2417 msg_hdr[i].qt_msg[j].offset,
2418 msg_hdr[i].qt_msg[j].size);
2419 if (msg_hdr[i].qt_msg[j].summary_size)
2420 Fprintf(stderr, " [%ld]",
2421 msg_hdr[i].qt_msg[j].summary_size);
2422 Fprintf(stderr, "\n");
2428 void
2429 do_questtxt()
2431 char *line;
2433 Sprintf(filename, DATA_IN_TEMPLATE, QTXT_I_FILE);
2434 if (!(ifp = fopen(filename, RDTMODE))) {
2435 perror(filename);
2436 exit(EXIT_FAILURE);
2439 filename[0] = '\0';
2440 #ifdef FILE_PREFIX
2441 Strcat(filename, file_prefix);
2442 #endif
2443 Sprintf(eos(filename), DATA_TEMPLATE, QTXT_O_FILE);
2444 if (!(ofp = fopen(filename, WRBMODE))) {
2445 perror(filename);
2446 Fclose(ifp);
2447 exit(EXIT_FAILURE);
2450 qt_hdr.n_hdr = 0;
2451 qt_line = 0;
2452 in_msg = FALSE;
2454 while ((line = fgetline(ifp)) != 0) {
2455 SpinCursor(3);
2457 qt_line++;
2458 if (qt_control(line))
2459 do_qt_control(line);
2460 else if (qt_comment(line)) {
2461 free(line);
2462 continue;
2463 } else
2464 do_qt_text(line);
2465 free(line);
2468 (void) rewind(ifp);
2469 in_msg = FALSE;
2470 adjust_qt_hdrs();
2471 put_qt_hdrs();
2472 while ((line = fgetline(ifp)) != 0) {
2473 if (qt_control(line)) {
2474 char *summary_p = 0;
2476 in_msg = (line[1] == 'C');
2477 if (!in_msg)
2478 summary_p = valid_qt_summary(line, FALSE);
2479 /* don't write anything unless we've got a summary */
2480 if (!summary_p) {
2481 free(line);
2482 continue;
2484 /* we have summary text; replace raw %E record with it */
2485 Strcpy(line, summary_p); /* (guaranteed to fit) */
2486 } else if (qt_comment(line)) {
2487 free(line);
2488 continue;
2490 if (debug)
2491 Fprintf(stderr, "%ld: %s", ftell(stdout), line);
2492 (void) fputs(xcrypt(line), ofp);
2493 free(line);
2495 Fclose(ifp);
2496 Fclose(ofp);
2497 return;
2500 static char temp[32];
2502 static char *limit(name, pref) /* limit a name to 30 characters length */
2503 char *name;
2504 int pref;
2506 (void) strncpy(temp, name, pref ? 26 : 30);
2507 temp[pref ? 26 : 30] = 0;
2508 return temp;
2511 void
2512 do_objs()
2514 int i, sum = 0;
2515 char *c, *objnam;
2516 int nspell = 0;
2517 int prefix = 0;
2518 char class = '\0';
2519 boolean sumerr = FALSE;
2521 filename[0] = '\0';
2522 #ifdef FILE_PREFIX
2523 Strcat(filename, file_prefix);
2524 #endif
2525 Sprintf(eos(filename), INCLUDE_TEMPLATE, ONAME_FILE);
2526 if (!(ofp = fopen(filename, WRTMODE))) {
2527 perror(filename);
2528 exit(EXIT_FAILURE);
2530 Fprintf(ofp, "%s", Dont_Edit_Code);
2531 Fprintf(ofp, "#ifndef ONAMES_H\n#define ONAMES_H\n\n");
2533 for (i = 0; !i || objects[i].oc_class != ILLOBJ_CLASS; i++) {
2534 SpinCursor(3);
2536 objects[i].oc_name_idx = objects[i].oc_descr_idx = i; /* init */
2537 if (!(objnam = tmpdup(OBJ_NAME(objects[i]))))
2538 continue;
2540 /* make sure probabilities add up to 1000 */
2541 if (objects[i].oc_class != class) {
2542 if (sum && sum != 1000) {
2543 Fprintf(stderr, "prob error for class %d (%d%%)", class, sum);
2544 (void) fflush(stderr);
2545 sumerr = TRUE;
2547 class = objects[i].oc_class;
2548 sum = 0;
2551 for (c = objnam; *c; c++)
2552 if (*c >= 'a' && *c <= 'z')
2553 *c -= (char) ('a' - 'A');
2554 else if (*c < 'A' || *c > 'Z')
2555 *c = '_';
2557 switch (class) {
2558 case WAND_CLASS:
2559 Fprintf(ofp, "#define\tWAN_");
2560 prefix = 1;
2561 break;
2562 case RING_CLASS:
2563 Fprintf(ofp, "#define\tRIN_");
2564 prefix = 1;
2565 break;
2566 case POTION_CLASS:
2567 Fprintf(ofp, "#define\tPOT_");
2568 prefix = 1;
2569 break;
2570 case SPBOOK_CLASS:
2571 Fprintf(ofp, "#define\tSPE_");
2572 prefix = 1;
2573 nspell++;
2574 break;
2575 case SCROLL_CLASS:
2576 Fprintf(ofp, "#define\tSCR_");
2577 prefix = 1;
2578 break;
2579 case AMULET_CLASS:
2580 /* avoid trouble with stupid C preprocessors */
2581 Fprintf(ofp, "#define\t");
2582 if (objects[i].oc_material == PLASTIC) {
2583 Fprintf(ofp, "FAKE_AMULET_OF_YENDOR\t%d\n", i);
2584 prefix = -1;
2585 break;
2587 break;
2588 case GEM_CLASS:
2589 /* avoid trouble with stupid C preprocessors */
2590 if (objects[i].oc_material == GLASS) {
2591 Fprintf(ofp, "/* #define\t%s\t%d */\n", objnam, i);
2592 prefix = -1;
2593 break;
2595 default:
2596 Fprintf(ofp, "#define\t");
2598 if (prefix >= 0)
2599 Fprintf(ofp, "%s\t%d\n", limit(objnam, prefix), i);
2600 prefix = 0;
2602 sum += objects[i].oc_prob;
2605 /* check last set of probabilities */
2606 if (sum && sum != 1000) {
2607 Fprintf(stderr, "prob error for class %d (%d%%)", class, sum);
2608 (void) fflush(stderr);
2609 sumerr = TRUE;
2612 Fprintf(ofp, "#define\tLAST_GEM\t(JADE)\n");
2613 Fprintf(ofp, "#define\tMAXSPELL\t%d\n", nspell + 1);
2614 Fprintf(ofp, "#define\tNUM_OBJECTS\t%d\n", i);
2616 Fprintf(ofp, "\n/* Artifacts (unique objects) */\n\n");
2618 for (i = 1; artifact_names[i]; i++) {
2619 SpinCursor(3);
2621 for (c = objnam = tmpdup(artifact_names[i]); *c; c++)
2622 if (*c >= 'a' && *c <= 'z')
2623 *c -= (char) ('a' - 'A');
2624 else if (*c < 'A' || *c > 'Z')
2625 *c = '_';
2627 if (!strncmp(objnam, "THE_", 4))
2628 objnam += 4;
2629 /* fudge _platinum_ YENDORIAN EXPRESS CARD */
2630 if (!strncmp(objnam, "PLATINUM_", 9))
2631 objnam += 9;
2632 Fprintf(ofp, "#define\tART_%s\t%d\n", limit(objnam, 1), i);
2635 Fprintf(ofp, "#define\tNROFARTIFACTS\t%d\n", i - 1);
2636 Fprintf(ofp, "\n#endif /* ONAMES_H */\n");
2637 Fclose(ofp);
2638 if (sumerr)
2639 exit(EXIT_FAILURE);
2640 return;
2643 /* Read one line from input, up to and including the next newline
2644 * character. Returns a pointer to the heap-allocated string, or a
2645 * null pointer if no characters were read.
2647 static char *
2648 fgetline(fd)
2649 FILE *fd;
2651 static const int inc = 256;
2652 int len = inc;
2653 char *c = malloc(len), *ret;
2655 for (;;) {
2656 ret = fgets(c + len - inc, inc, fd);
2657 if (!ret) {
2658 free(c);
2659 c = NULL;
2660 break;
2661 } else if (index(c, '\n')) {
2662 /* normal case: we have a full line */
2663 break;
2665 len += inc;
2666 c = realloc(c, len);
2668 return c;
2671 static char *
2672 tmpdup(str)
2673 const char *str;
2675 static char buf[128];
2677 if (!str)
2678 return (char *) 0;
2679 (void) strncpy(buf, str, 127);
2680 return buf;
2683 static char *
2684 eos(str)
2685 char *str;
2687 while (*str)
2688 str++;
2689 return str;
2693 * macro used to control vision algorithms:
2694 * VISION_TABLES => generate tables
2697 void
2698 do_vision()
2700 #ifdef VISION_TABLES
2701 int i, j;
2703 /* Everything is clear. xclear may be malloc'ed.
2704 * Block the upper left corner (BLOCK_HEIGHTxBLOCK_WIDTH)
2706 for (i = 0; i < MAX_ROW; i++)
2707 for (j = 0; j < MAX_COL; j++)
2708 if (i < BLOCK_HEIGHT && j < BLOCK_WIDTH)
2709 xclear[i][j] = '\000';
2710 else
2711 xclear[i][j] = '\001';
2712 #endif /* VISION_TABLES */
2714 SpinCursor(3);
2717 * create the include file, "vis_tab.h"
2719 filename[0] = '\0';
2720 #ifdef FILE_PREFIX
2721 Strcat(filename, file_prefix);
2722 #endif
2723 Sprintf(filename, INCLUDE_TEMPLATE, VIS_TAB_H);
2724 if (!(ofp = fopen(filename, WRTMODE))) {
2725 perror(filename);
2726 exit(EXIT_FAILURE);
2728 Fprintf(ofp, "%s", Dont_Edit_Code);
2729 Fprintf(ofp, "#ifdef VISION_TABLES\n");
2730 #ifdef VISION_TABLES
2731 H_close_gen();
2732 H_far_gen();
2733 #endif /* VISION_TABLES */
2734 Fprintf(ofp, "\n#endif /* VISION_TABLES */\n");
2735 Fclose(ofp);
2737 SpinCursor(3);
2740 * create the source file, "vis_tab.c"
2742 filename[0] = '\0';
2743 #ifdef FILE_PREFIX
2744 Strcat(filename, file_prefix);
2745 #endif
2746 Sprintf(filename, SOURCE_TEMPLATE, VIS_TAB_C);
2747 if (!(ofp = fopen(filename, WRTMODE))) {
2748 perror(filename);
2749 Sprintf(filename, INCLUDE_TEMPLATE, VIS_TAB_H);
2750 Unlink(filename);
2751 exit(EXIT_FAILURE);
2753 Fprintf(ofp, "%s", Dont_Edit_Code);
2754 Fprintf(ofp, "#include \"config.h\"\n");
2755 Fprintf(ofp, "#ifdef VISION_TABLES\n");
2756 Fprintf(ofp, "#include \"vis_tab.h\"\n");
2758 SpinCursor(3);
2760 #ifdef VISION_TABLES
2761 C_close_gen();
2762 C_far_gen();
2763 Fprintf(ofp, "\nvoid vis_tab_init() { return; }\n");
2764 #endif /* VISION_TABLES */
2766 SpinCursor(3);
2768 Fprintf(ofp, "\n#endif /* VISION_TABLES */\n");
2769 Fprintf(ofp, "\n/*vis_tab.c*/\n");
2771 Fclose(ofp);
2772 return;
2775 #ifdef VISION_TABLES
2777 /*-------------- vision tables --------------*\
2779 * Generate the close and far tables. This is done by setting up a
2780 * fake dungeon and moving our source to different positions relative
2781 * to a block and finding the first/last visible position. The fake
2782 * dungeon is all clear execpt for the upper left corner (BLOCK_HEIGHT
2783 * by BLOCK_WIDTH) is blocked. Then we move the source around relative
2784 * to the corner of the block. For each new position of the source
2785 * we check positions on rows "kittycorner" from the source. We check
2786 * positions until they are either in sight or out of sight (depends on
2787 * which table we are generating). The picture below shows the setup
2788 * for the generation of the close table. The generation of the far
2789 * table would switch the quadrants of the '@' and the "Check rows
2790 * here".
2793 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2794 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2795 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,, Check rows here ,,,,,,,,,,,,
2796 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2797 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXB,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2798 * ...............................
2799 * ...............................
2800 * .........@.....................
2801 * ...............................
2803 * Table generation figure (close_table). The 'X's are blocked points.
2804 * The 'B' is a special blocked point. The '@' is the source. The ','s
2805 * are the target area. The '.' are just open areas.
2808 * Example usage of close_table[][][].
2810 * The table is as follows:
2812 * dy = |row of '@' - row of 'B'| - 1
2813 * dx = |col of '@' - col of 'B'|
2815 * The first indices are the deltas from the source '@' and the block 'B'.
2816 * You must check for the value inside the abs value bars being zero. If
2817 * so then the block is on the same row and you don't need to do a table
2818 * lookup. The last value:
2820 * dcy = |row of block - row to be checked|
2822 * Is the value of the first visible spot on the check row from the
2823 * block column. So
2825 * first visible col = close_table[dy][dx][dcy] + col of 'B'
2827 \*-------------- vision tables --------------*/
2829 static void
2830 H_close_gen()
2832 Fprintf(ofp, "\n/* Close */\n");
2833 Fprintf(ofp,
2834 "#define CLOSE_MAX_SB_DY %2d\t/* |src row - block row| - 1\t*/\n",
2835 TEST_HEIGHT - 1);
2836 Fprintf(ofp,
2837 "#define CLOSE_MAX_SB_DX %2d\t/* |src col - block col|\t*/\n",
2838 TEST_WIDTH);
2839 Fprintf(ofp,
2840 "#define CLOSE_MAX_BC_DY %2d\t/* |block row - check row|\t*/\n",
2841 TEST_HEIGHT);
2842 Fprintf(ofp, "typedef struct {\n");
2843 Fprintf(ofp,
2844 " unsigned char close[CLOSE_MAX_SB_DX][CLOSE_MAX_BC_DY];\n");
2845 Fprintf(ofp, "} close2d;\n");
2846 Fprintf(ofp, "extern close2d close_table[CLOSE_MAX_SB_DY];\n");
2847 return;
2850 static void
2851 H_far_gen()
2853 Fprintf(ofp, "\n/* Far */\n");
2854 Fprintf(ofp, "#define FAR_MAX_SB_DY %2d\t/* |src row - block row|\t*/\n",
2855 TEST_HEIGHT);
2856 Fprintf(ofp,
2857 "#define FAR_MAX_SB_DX %2d\t/* |src col - block col| - 1\t*/\n",
2858 TEST_WIDTH - 1);
2859 Fprintf(ofp,
2860 "#define FAR_MAX_BC_DY %2d\t/* |block row - check row| - 1\t*/\n",
2861 TEST_HEIGHT - 1);
2862 Fprintf(ofp, "typedef struct {\n");
2863 Fprintf(ofp, " unsigned char far_q[FAR_MAX_SB_DX][FAR_MAX_BC_DY];\n");
2864 Fprintf(ofp, "} far2d;\n");
2865 Fprintf(ofp, "extern far2d far_table[FAR_MAX_SB_DY];\n");
2866 return;
2869 static void
2870 C_close_gen()
2872 int i, dx, dy;
2873 int src_row, src_col; /* source */
2874 int block_row, block_col; /* block */
2875 int this_row;
2876 int no_more;
2877 const char *delim;
2879 block_row = BLOCK_HEIGHT - 1;
2880 block_col = BLOCK_WIDTH - 1;
2882 Fprintf(ofp, "\n#ifndef FAR_TABLE_ONLY\n");
2883 Fprintf(ofp, "\nclose2d close_table[CLOSE_MAX_SB_DY] = {\n");
2884 #ifndef no_vision_progress
2885 Fprintf(stderr, "\nclose:");
2886 #endif
2888 for (dy = 1; dy < TEST_HEIGHT; dy++) {
2889 src_row = block_row + dy;
2890 Fprintf(ofp, "/* DY = %2d (- 1)*/\n {{\n", dy);
2891 #ifndef no_vision_progress
2892 Fprintf(stderr, " %2d", dy), (void) fflush(stderr);
2893 #endif
2894 for (dx = 0; dx < TEST_WIDTH; dx++) {
2895 src_col = block_col - dx;
2896 Fprintf(ofp, " /*%2d*/ {", dx);
2898 no_more = 0;
2899 for (this_row = 0; this_row < TEST_HEIGHT; this_row++) {
2900 delim = (this_row < TEST_HEIGHT - 1) ? "," : "";
2901 if (no_more) {
2902 Fprintf(ofp, "%s%s", CLOSE_OFF_TABLE_STRING, delim);
2903 continue;
2905 SpinCursor(3);
2907 /* Find the first column that we can see. */
2908 for (i = block_col + 1; i < MAX_COL; i++) {
2909 if (clear_path(src_row, src_col, block_row - this_row, i))
2910 break;
2913 if (i == MAX_COL)
2914 no_more = 1;
2915 Fprintf(ofp, "%2d%s", i - block_col, delim);
2917 Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n");
2919 Fprintf(ofp, " }},\n");
2922 Fprintf(ofp, "}; /* close_table[] */\n"); /* closing brace for table */
2923 Fprintf(ofp, "#endif /* !FAR_TABLE_ONLY */\n");
2924 #ifndef no_vision_progress
2925 Fprintf(stderr, "\n");
2926 #endif
2927 return;
2930 static void
2931 C_far_gen()
2933 int i, dx, dy;
2934 int src_row, src_col; /* source */
2935 int block_row, block_col; /* block */
2936 int this_row;
2937 const char *delim;
2939 block_row = BLOCK_HEIGHT - 1;
2940 block_col = BLOCK_WIDTH - 1;
2942 Fprintf(ofp, "\n#ifndef CLOSE_TABLE_ONLY\n");
2943 Fprintf(ofp, "\nfar2d far_table[FAR_MAX_SB_DY] = {\n");
2944 #ifndef no_vision_progress
2945 Fprintf(stderr, "\n_far_:");
2946 #endif
2948 for (dy = 0; dy < TEST_HEIGHT; dy++) {
2949 src_row = block_row - dy;
2950 Fprintf(ofp, "/* DY = %2d */\n {{\n", dy);
2951 #ifndef no_vision_progress
2952 Fprintf(stderr, " %2d", dy), (void) fflush(stderr);
2953 #endif
2954 for (dx = 1; dx < TEST_WIDTH; dx++) {
2955 src_col = block_col + dx;
2956 Fprintf(ofp, " /*%2d(-1)*/ {", dx);
2958 for (this_row = block_row + 1; this_row < block_row + TEST_HEIGHT;
2959 this_row++) {
2960 delim = (this_row < block_row + TEST_HEIGHT - 1) ? "," : "";
2962 SpinCursor(3);
2963 /* Find first col that we can see. */
2964 for (i = 0; i <= block_col; i++) {
2965 if (clear_path(src_row, src_col, this_row, i))
2966 break;
2969 if (block_col - i < 0)
2970 Fprintf(ofp, "%s%s", FAR_OFF_TABLE_STRING, delim);
2971 else
2972 Fprintf(ofp, "%2d%s", block_col - i, delim);
2974 Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n");
2976 Fprintf(ofp, " }},\n");
2979 Fprintf(ofp, "}; /* far_table[] */\n"); /* closing brace for table */
2980 Fprintf(ofp, "#endif /* !CLOSE_TABLE_ONLY */\n");
2981 #ifndef no_vision_progress
2982 Fprintf(stderr, "\n");
2983 #endif
2984 return;
2988 * "Draw" a line from the hero to the given location. Stop if we hit a
2989 * wall.
2991 * Generalized integer Bresenham's algorithm (fast line drawing) for
2992 * all quadrants. From _Procedural Elements for Computer Graphics_, by
2993 * David F. Rogers. McGraw-Hill, 1985.
2995 * I have tried a little bit of optimization by pulling compares out of
2996 * the inner loops.
2998 * NOTE: This had better *not* be called from a position on the
2999 * same row as the hero.
3001 static int
3002 clear_path(you_row, you_col, y2, x2)
3003 int you_row, you_col, y2, x2;
3005 int dx, dy, s1, s2;
3006 register int i, error, x, y, dxs, dys;
3008 x = you_col;
3009 y = you_row;
3010 dx = abs(x2 - you_col);
3011 dy = abs(y2 - you_row);
3012 s1 = sign(x2 - you_col);
3013 s2 = sign(y2 - you_row);
3015 if (s1 == 0) { /* same column */
3016 if (s2 == 1) { /* below (larger y2 value) */
3017 for (i = you_row + 1; i < y2; i++)
3018 if (!xclear[i][you_col])
3019 return 0;
3020 } else { /* above (smaller y2 value) */
3021 for (i = y2 + 1; i < you_row; i++)
3022 if (!xclear[i][you_col])
3023 return 0;
3025 return 1;
3029 * Lines at 0 and 90 degrees have been weeded out.
3031 if (dy > dx) {
3032 error = dx;
3033 dx = dy;
3034 dy = error; /* swap the values */
3035 dxs = dx << 1; /* save the shifted values */
3036 dys = dy << 1;
3037 error = dys - dx; /* NOTE: error is used as a temporary above */
3039 for (i = 0; i < dx; i++) {
3040 if (!xclear[y][x])
3041 return 0; /* plot point */
3043 while (error >= 0) {
3044 x += s1;
3045 error -= dxs;
3047 y += s2;
3048 error += dys;
3050 } else {
3051 dxs = dx << 1; /* save the shifted values */
3052 dys = dy << 1;
3053 error = dys - dx;
3055 for (i = 0; i < dx; i++) {
3056 if (!xclear[y][x])
3057 return 0; /* plot point */
3059 while (error >= 0) {
3060 y += s2;
3061 error -= dxs;
3063 x += s1;
3064 error += dys;
3067 return 1;
3069 #endif /* VISION_TABLES */
3071 #ifdef STRICT_REF_DEF
3072 NEARDATA struct flag flags;
3073 #ifdef ATTRIB_H
3074 struct attribs attrmax, attrmin;
3075 #endif
3076 #endif /* STRICT_REF_DEF */
3078 /*makedefs.c*/