1 /* NetHack 3.6 makedefs.c $NHDT-Date: 1459208813 2016/03/28 23:46:53 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.110 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* Copyright (c) M. Stephenson, 1990, 1991. */
4 /* Copyright (c) Dean Luick, 1990. */
5 /* NetHack may be freely redistributed. See license for details. */
7 #define MAKEDEFS_C /* use to conditionally include file sections */
11 #undef free /* makedefs doesn't use the alloc and free in src/alloc.c */
25 /* version information */
26 #ifdef SHORT_FILENAMES
29 #include "patchlevel.h"
34 #if defined(__SC__) || defined(__MRC__) /* MPW compilers */
36 #include <CursorCtl.h>
38 #else /* MAC without MPWTOOL */
39 #define MACsansMPWTOOL
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 */
54 #if defined(UNIX) && !defined(LINT) && !defined(GCC_WARN)
55 static const char SCCS_Id
[] = "@(#)makedefs.c\t3.6\t2016/02/12";
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"
63 #define OPTIONS_FILE "options"
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 */
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"
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 */
90 #define DATA_TEMPLATE ":Dungeon:%s"
92 #define DATA_TEMPLATE ":lib:%s"
93 #endif /* __SC__ || __MRC__ */
94 #define DATA_IN_TEMPLATE ":dat:%s"
95 #else /* neither AMIGA nor MAC */
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 */
114 "/* This source file is generated by 'makedefs'. Do not edit. */\n",
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))
133 static char xclear
[MAX_ROW
][MAX_COL
];
135 /*-end of vision defs-*/
137 static char filename
[600];
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
= "";
146 #ifdef MACsansMPWTOOL
147 int FDECL(main
, (void));
149 int FDECL(main
, (int, char **));
151 void FDECL(do_makedefs
, (char *));
154 void NDECL(do_dungeon
);
156 void NDECL(do_options
);
157 void NDECL(do_monstr
);
158 void NDECL(do_permonst
);
159 void NDECL(do_questtxt
);
160 void NDECL(do_rumors
);
161 void NDECL(do_oracles
);
162 void NDECL(do_vision
);
164 extern void NDECL(monst_init
); /* monst.c */
165 extern void NDECL(objects_init
); /* objects.c */
167 static void NDECL(link_sanity_check
);
168 static char *FDECL(name_file
, (const char *, const char *));
169 static void FDECL(delete_file
, (const char *template, const char *));
170 static FILE *FDECL(getfp
, (const char *, const char *, const char *));
171 static void FDECL(do_ext_makedefs
, (int, char **));
173 static void NDECL(make_version
);
174 static char *FDECL(version_string
, (char *, const char *));
175 static char *FDECL(version_id_string
, (char *, const char *));
176 static char *FDECL(bannerc_string
, (char *, const char *));
177 static char *FDECL(xcrypt
, (const char *));
178 static unsigned long FDECL(read_rumors_file
,
179 (const char *, int *, long *, unsigned long));
180 static void FDECL(do_rnd_access_file
, (const char *));
181 static boolean
FDECL(d_filter
, (char *));
182 static boolean
FDECL(h_filter
, (char *));
183 static boolean
FDECL(ranged_attk
, (struct permonst
*));
184 static int FDECL(mstrength
, (struct permonst
*));
185 static void NDECL(build_savebones_compat_string
);
186 static void NDECL(windowing_sanity
);
188 static boolean
FDECL(qt_comment
, (char *));
189 static boolean
FDECL(qt_control
, (char *));
190 static int FDECL(get_hdr
, (char *));
191 static boolean
FDECL(new_id
, (char *));
192 static boolean
FDECL(known_msg
, (int, int));
193 static void FDECL(new_msg
, (char *, int, int));
194 static char *FDECL(valid_qt_summary
, (char *, BOOLEAN_P
));
195 static void FDECL(do_qt_control
, (char *));
196 static void FDECL(do_qt_text
, (char *));
197 static void NDECL(adjust_qt_hdrs
);
198 static void NDECL(put_qt_hdrs
);
201 static void NDECL(H_close_gen
);
202 static void NDECL(H_far_gen
);
203 static void NDECL(C_close_gen
);
204 static void NDECL(C_far_gen
);
205 static int FDECL(clear_path
, (int, int, int, int));
208 static char *FDECL(fgetline
, (FILE*));
209 static char *FDECL(tmpdup
, (const char *));
210 static char *FDECL(limit
, (char *, int));
211 static char *FDECL(eos
, (char *));
213 /* input, output, tmp */
214 static FILE *ifp
, *ofp
, *tfp
;
216 #if defined(__BORLANDC__) && !defined(_WIN32)
217 extern unsigned _stklen
= STKSIZ
;
220 #ifdef MACsansMPWTOOL
224 const char *def_options
= "odemvpqrshz";
228 printf("Enter options to run: [%s] ", def_options
);
230 fgets(buf
, 100, stdin
);
233 Strcpy(buf
, def_options
);
235 buf
[len
- 1] = 0; /* remove return */
237 if (buf
[0] == '-' && buf
[1] == '-') {
239 split up buf into words
240 do_ext_makedefs(fakeargc
, fakeargv
);
242 printf("extended makedefs not implemented for Mac OS9\n");
263 && !(argv
[1][0] == '-' && argv
[1][1] == '-')) {
264 Fprintf(stderr
, "Bad arg count (%d).\n", argc
- 1);
265 (void) fflush(stderr
);
270 if (argc
>= 2 && argv
[1][0] != '-') {
271 file_prefix
= argv
[1];
277 if (argv
[1][0] == '-' && argv
[1][1] == '-') {
278 do_ext_makedefs(argc
, argv
);
280 do_makedefs(&argv
[1][1]);
292 /* Note: these initializers don't do anything except guarantee that
293 we're linked properly.
303 boolean more_than_one
;
307 /* construct the current version number */
310 more_than_one
= strlen(options
) > 1;
313 Fprintf(stderr
, "makedefs -%c\n", *options
);
351 do_rnd_access_file(EPITAPHFILE
);
352 do_rnd_access_file(ENGRAVEFILE
);
353 do_rnd_access_file(BOGUSMONFILE
);
365 Fprintf(stderr
, "Unknown option '%c'.\n", *options
);
366 (void) fflush(stderr
);
372 Fprintf(stderr
, "Completed.\n"); /* feedback */
375 static char namebuf
[1000];
378 name_file(template, tag
)
379 const char *template;
382 Sprintf(namebuf
, template, tag
);
387 delete_file(template, tag
)
388 const char *template;
391 char *name
= name_file(template, tag
);
397 getfp(template, tag
, mode
)
398 const char *template;
402 char *name
= name_file(template, tag
);
403 FILE *rv
= fopen(name
, mode
);
406 Fprintf(stderr
, "Can't open '%s'.\n", name
);
412 static boolean debug
= FALSE
;
414 static FILE *inputfp
;
415 static FILE *outputfp
;
419 int is_defined
; /* 0 undef; 1 defined */
421 /* struct grep_var grep_vars[] and TODO_* constants in include file: */
424 static void NDECL(do_grep_showvars
);
425 static struct grep_var
*FDECL(grepsearch
, (const char *));
426 static int FDECL(grep_check_id
, (const char *));
427 static void FDECL(grep_show_wstack
, (const char *));
428 static char *FDECL(do_grep_control
, (char *));
429 static void NDECL(do_grep
);
430 static void FDECL(grep0
, (FILE *, FILE *));
432 static int grep_trace
= 0;
434 #define IS_OPTION(str) if (!strcmp(&argv[0][2], str))
441 Fprintf(stderr, "missing option\n"); \
442 exit(EXIT_FAILURE); \
446 do_ext_makedefs(int argc
, char **argv
)
453 argv
++; /* skip program name */
456 if (argv
[0][0] != '-')
458 if (argv
[0][1] != '-') {
459 Fprintf(stderr
, "Can't mix - and -- options.\n");
463 /* short version string for packaging - note no \n */
467 argv
++; /* not CONSUME */
470 strcpy(delim
, argv
[0]);
471 Fprintf(stdout
, "%s", version_string(buf
, delim
));
480 do_makedefs(argv
[0]);
485 if (!strcmp(argv
[0], "-")) {
488 inputfp
= fopen(argv
[0], RDTMODE
);
490 Fprintf(stderr
, "Can't open '%s'.\n", argv
[0]);
496 IS_OPTION("output") {
498 if (!strcmp(argv
[0], "-")) {
501 outputfp
= fopen(argv
[0], WRTMODE
);
503 Fprintf(stderr
, "Can't open '%s'.\n", argv
[0]);
511 Fprintf(stderr
, "Can't do grep and something else.\n");
517 IS_OPTION("grep-showvars") {
521 IS_OPTION("grep-trace") {
525 IS_OPTION("grep-define") {
529 p
= grepsearch(argv
[0]);
533 Fprintf(stderr
, "Unknown symbol '%s'\n", argv
[0]);
538 IS_OPTION("grep-undef") {
542 p
= grepsearch(argv
[0]);
546 Fprintf(stderr
, "Unknown symbol '%s'\n", argv
[0]);
555 Fprintf(stderr
, "Unknown option '%s'.\n", argv
[0]);
559 Fprintf(stderr
, "unexpected argument '%s'.\n", argv
[0]);
565 Fprintf(stderr
, "Confused about what to do?\n");
568 Fprintf(stderr
, "Nothing to do?\n");
582 * Any line NOT starting with a caret is either suppressed or passed
583 * through unchanged depending on the current conditional state.
585 * The default conditional state is printing on.
587 * Conditionals may be nested.
589 * makedefs will exit with a EXIT_FAILURE if any errors are detected;
590 * as many errors as possible are detected before giving up.
592 * Unknown identifiers are treated as TRUE and also as an error to
593 * allow processing to continue past the unknown identifier (note
594 * that "#undef" is different than unknown).
596 * Any line starting with a caret is a control line; as in C, zero or
597 * more spaces may be embedded in the line almost anywhere; the caret
598 * MUST be in column 1.
599 * (XXX for the moment, no white space is allowed after the caret because
600 * existing lines in the docs look like that.)
603 * ^^ a line starting with a (single) literal caret
604 * ^# a comment - the line is ignored
605 * ^?ID if defined(ID)
606 * ^!ID if !defined(ID)
610 #define GREP_MAGIC '^'
611 #define GREP_STACK_SIZE 100
613 static int grep_rewrite
= 0; /* need to (possibly) rewrite lines */
615 static int grep_writing
= 1; /* need to copy lines to output */
616 static int grep_errors
= 0;
617 static int grep_sp
= 0;
618 #define ST_LD(old, opp) (((old) ? 1 : 0) | ((opp) ? 2 : 0))
619 #define ST_OLD(v) (((v) & 1) != 0)
620 #define ST_OPP(v) (((v) & 2) != 0)
622 static int grep_stack
[GREP_STACK_SIZE
] = { ST_LD(1, 0) };
623 static int grep_lineno
= 0;
630 for (x
= 0; x
< SIZE(grep_vars
) - 1; x
++) {
631 printf("%d\t%s\n", grep_vars
[x
].is_defined
, grep_vars
[x
].name
);
635 static struct grep_var
*
639 /* XXX make into binary search */
642 while (x
< SIZE(grep_vars
) - 1) {
643 if (!strcmp(grep_vars
[x
].name
, name
))
644 return &grep_vars
[x
];
656 while (*id
&& isspace((uchar
) *id
))
659 Fprintf(stderr
, "missing identifier in line %d", grep_lineno
);
666 Fprintf(outputfp
, "ID %d %s\n", rv
->is_defined
, id
);
668 return rv
->is_defined
;
672 Fprintf(outputfp
, "ID U %s\n", id
);
674 Fprintf(stderr
, "unknown identifier '%s' in line %d.\n", id
, grep_lineno
);
676 return 2; /* So new features can be checked before makedefs
681 grep_show_wstack(tag
)
689 Fprintf(outputfp
, "%s w=%d sp=%d\t", tag
, grep_writing
, grep_sp
);
690 for (x
= grep_sp
; x
>= 0 && x
> grep_sp
- 6; x
--) {
691 Fprintf(outputfp
, "[%d]=%d ", x
, grep_stack
[x
]);
693 Fprintf(outputfp
, "\n");
703 if (isspace((uchar
) buf
[0]))
704 return &buf
[-1]; /* XXX see docs above */
706 while (buf
[0] && isspace((uchar
) buf
[0]))
710 case '#': /* comment */
712 case '.': /* end of if level */
714 Fprintf(stderr
, "unmatched ^. (endif) at line %d.\n",
718 grep_writing
= ST_OLD(grep_stack
[grep_sp
--]);
719 grep_show_wstack("pop");
722 case '!': /* if not ID */
725 case '?': /* if ID */
726 if (grep_sp
== GREP_STACK_SIZE
- 2) {
727 Fprintf(stderr
, "stack overflow at line %d.", grep_lineno
);
731 isif
= grep_check_id(&buf
[1]) ? isif
: !isif
;
732 grep_stack
[++grep_sp
] = ST_LD(grep_writing
, !isif
);
735 grep_stack
[++grep_sp
] = ST_LD(0, 0);
736 /* grep_writing = 0; */
738 grep_show_wstack("push");
741 if (ST_ELSE
& grep_stack
[grep_sp
]) {
742 Fprintf(stderr
, "multiple : for same conditional at line %d.\n",
746 grep_writing
= ST_OPP(grep_stack
[grep_sp
]);
747 grep_stack
[grep_sp
] |= ST_ELSE
;
750 case '(': /* start of expression */
752 case GREP_MAGIC
: /* ^^ -> ^ */
757 if (isprint((uchar
) buf
[0])) {
761 sprintf(str
, "0x%02x", buf
[0]);
763 Fprintf(stderr
, "unknown control ^%s at line %d.\n", str
,
776 /* no language features use this yet */
781 static void grep0(FILE *, FILE *);
787 Fprintf(stderr
, "--grep requires --input\n");
790 Fprintf(stderr
, "--grep requires --output\n");
792 if (!inputfp
|| !outputfp
) {
796 grep0(inputfp
, outputfp
);
800 grep0(inputfp0
, outputfp0
)
804 char buf
[16384]; /* looong, just in case */
806 while (!feof(inputfp0
) && !ferror(inputfp0
)) {
810 if (fgets(buf
, sizeof(buf
), inputfp0
) == 0)
812 if ((tmp
= strchr(buf
, '\n')))
816 Fprintf(outputfp0
, "%04d %c >%s\n", grep_lineno
,
817 grep_writing
? ' ' : '#', buf
);
820 if (buf
[0] == GREP_MAGIC
) {
821 buf1
= do_grep_control(&buf
[1]);
829 do_grep_rewrite(buf1
);
832 Fprintf(outputfp0
, "%s\n", buf1
);
834 if (ferror(inputfp0
)) {
835 Fprintf(stderr
, "read error!\n");
838 if (ferror(outputfp0
)) {
839 Fprintf(stderr
, "write error!\n");
845 Fprintf(stderr
, "%d unterminated conditional level%s\n", grep_sp
,
846 grep_sp
== 1 ? "" : "s");
850 Fprintf(stderr
, "%d error%s detected.\n", grep_errors
,
851 grep_errors
== 1 ? "" : "s");
856 /* trivial text encryption routine which can't be broken with `tr' */
860 { /* duplicated in src/hacklib.c */
861 static char buf
[BUFSZ
];
862 register const char *p
;
864 register int bitmask
;
866 for (bitmask
= 1, p
= str
, q
= buf
; *p
; q
++) {
870 if ((bitmask
<<= 1) >= 32)
877 #define PAD_RUMORS_TO 60
878 /* common code for do_rumors(). Return 0 on error. */
880 read_rumors_file(file_ext
, rumor_count
, rumor_size
, old_rumor_offset
)
881 const char *file_ext
;
884 unsigned long old_rumor_offset
;
888 unsigned long rumor_offset
;
890 Sprintf(infile
, DATA_IN_TEMPLATE
, RUMOR_FILE
);
891 Strcat(infile
, file_ext
);
892 if (!(ifp
= fopen(infile
, RDTMODE
))) {
897 /* copy the rumors */
898 while ((line
= fgetline(ifp
)) != 0) {
900 /* rumor selection is accomplished by seeking to a random
901 position in the file, advancing to newline, and taking
902 the next line; therefore, rumors which follow long-line
903 rumors are most likely to be chosen and rumors which
904 follow short-line rumors are least likely to be chosen;
905 we ameliorate the latter by padding the shortest lines,
906 increasing the chance of the random seek landing in them */
907 int len
= (int) strlen(line
);
909 if (len
<= PAD_RUMORS_TO
) {
910 char *base
= index(line
, '\n');
911 /* this is only safe because fgetline() overallocates */
912 while (len
++ < PAD_RUMORS_TO
) {
921 /*[if we forced binary output, this would be sufficient]*/
922 *rumor_size
+= strlen(line
); /* includes newline */
924 (void) fputs(xcrypt(line
), tfp
);
927 /* record the current position; next rumors section will start here */
928 rumor_offset
= (unsigned long) ftell(tfp
);
929 Fclose(ifp
); /* all done with rumors.file_ext */
931 /* the calculated value for *_rumor_count assumes that
932 a single-byte line terminator is in use; for platforms
933 which use two byte CR+LF, we need to override that value
934 [it's much simpler to do so unconditionally, rendering
935 the loop's accumulation above obsolete] */
936 *rumor_size
= (long) (rumor_offset
- old_rumor_offset
);
941 do_rnd_access_file(fname
)
946 Sprintf(filename
, DATA_IN_TEMPLATE
, fname
);
947 Strcat(filename
, ".txt");
948 if (!(ifp
= fopen(filename
, RDTMODE
))) {
954 Strcat(filename
, file_prefix
);
956 Sprintf(eos(filename
), DATA_TEMPLATE
, fname
);
957 if (!(ofp
= fopen(filename
, WRTMODE
))) {
961 Fprintf(ofp
, "%s", Dont_Edit_Data
);
963 tfp
= getfp(DATA_TEMPLATE
, "grep.tmp", WRTMODE
);
965 ifp
= getfp(DATA_TEMPLATE
, "grep.tmp", RDTMODE
);
967 while ((line
= fgetline(ifp
)) != 0) {
968 if (line
[0] != '#' && line
[0] != '\n')
969 (void) fputs(xcrypt(line
), ofp
);
975 delete_file(DATA_TEMPLATE
, "grep.tmp");
983 static const char rumors_header
[] =
984 "%s%04d,%06ld,%06lx;%04d,%06ld,%06lx;0,0,%06lx\n";
986 int true_rumor_count
, false_rumor_count
;
987 long true_rumor_size
, false_rumor_size
;
988 unsigned long true_rumor_offset
, false_rumor_offset
, eof_offset
;
990 Sprintf(tempfile
, DATA_TEMPLATE
, "rumors.tmp");
993 Strcat(filename
, file_prefix
);
995 Sprintf(eos(filename
), DATA_TEMPLATE
, RUMOR_FILE
);
996 if (!(ofp
= fopen(filename
, WRTMODE
))) {
1000 if (!(tfp
= fopen(tempfile
, WRTMODE
))) {
1006 true_rumor_count
= false_rumor_count
= 0;
1007 true_rumor_size
= false_rumor_size
= 0L;
1008 true_rumor_offset
= false_rumor_offset
= eof_offset
= 0L;
1010 /* output a dummy header record; we'll replace it in final output */
1011 Fprintf(tfp
, rumors_header
, Dont_Edit_Data
, true_rumor_count
,
1012 true_rumor_size
, true_rumor_offset
, false_rumor_count
,
1013 false_rumor_size
, false_rumor_offset
, eof_offset
);
1014 /* record the current position; true rumors will start here */
1015 true_rumor_offset
= ftell(tfp
);
1017 false_rumor_offset
= read_rumors_file(
1018 ".tru", &true_rumor_count
, &true_rumor_size
, true_rumor_offset
);
1019 if (!false_rumor_offset
)
1020 goto rumors_failure
;
1022 eof_offset
= read_rumors_file(".fal", &false_rumor_count
,
1023 &false_rumor_size
, false_rumor_offset
);
1025 goto rumors_failure
;
1027 /* get ready to transfer the contents of temp file to output file */
1029 Sprintf(line
, "rewind of \"%s\"", tempfile
);
1030 if (rewind(tfp
) != 0) {
1033 goto rumors_failure
;
1037 /* output the header record */
1038 Fprintf(ofp
, rumors_header
, Dont_Edit_Data
, true_rumor_count
,
1039 true_rumor_size
, true_rumor_offset
, false_rumor_count
,
1040 false_rumor_size
, false_rumor_offset
, eof_offset
);
1041 /* skip the temp file's dummy header */
1042 if (!(line
= fgetline(tfp
))) { /* "Don't Edit" */
1044 goto rumors_failure
;
1047 if (!(line
= fgetline(tfp
))) { /* count,size,offset */
1049 goto rumors_failure
;
1052 /* copy the rest of the temp file into the final output file */
1053 while ((line
= fgetline(tfp
)) != 0) {
1054 (void) fputs(line
, ofp
);
1057 /* all done; delete temp file */
1065 Unlink(filename
); /* kill empty or incomplete output file */
1067 Unlink(tempfile
); /* and temporary file */
1072 * Use this to explicitly mask out features during version checks.
1074 * ZEROCOMP, RLECOMP, and ZLIB_COMP describe compression features
1075 * that the port/plaform which wrote the savefile was capable of
1076 * dealing with. Don't reject a savefile just because the port
1077 * reading the savefile doesn't match on all/some of them.
1078 * The actual compression features used to produce the savefile are
1079 * recorded in the savefile_info structure immediately following the
1080 * version_info, and that is what needs to be checked against the
1081 * feature set of the port that is reading the savefile back in.
1082 * That check is done in src/restore.c now.
1085 #define IGNORED_FEATURES \
1086 (0L | (1L << 19) /* SCORE_ON_BOTL */ \
1087 | (1L << 27) /* ZEROCOMP */ \
1088 | (1L << 28) /* RLECOMP */ \
1097 * integer version number
1099 version
.incarnation
= ((unsigned long) VERSION_MAJOR
<< 24)
1100 | ((unsigned long) VERSION_MINOR
<< 16)
1101 | ((unsigned long) PATCHLEVEL
<< 8)
1102 | ((unsigned long) EDITLEVEL
);
1104 * encoded feature list
1105 * Note: if any of these magic numbers are changed or reassigned,
1106 * EDITLEVEL in patchlevel.h should be incremented at the same time.
1107 * The actual values have no special meaning, and the category
1108 * groupings are just for convenience.
1110 version
.feature_set
= (unsigned long) (0L
1111 /* levels and/or topology (0..4) */
1112 /* monsters (5..9) */
1116 /* objects (10..14) */
1117 /* flag bits and/or other global variables (15..26) */
1124 #ifdef SCORE_ON_BOTL
1127 /* data format (27..31)
1128 * External compression methods such as COMPRESS and ZLIB_COMP
1129 * do not affect the contents and are thus excluded from here */
1138 * Value used for object & monster sanity check.
1139 * (NROFARTIFACTS<<24) | (NUM_OBJECTS<<12) | (NUMMONS<<0)
1141 for (i
= 1; artifact_names
[i
]; i
++)
1143 version
.entity_count
= (unsigned long) (i
- 1);
1144 for (i
= 1; objects
[i
].oc_class
!= ILLOBJ_CLASS
; i
++)
1146 version
.entity_count
= (version
.entity_count
<< 12) | (unsigned long) i
;
1147 for (i
= 0; mons
[i
].mlet
; i
++)
1149 version
.entity_count
= (version
.entity_count
<< 12) | (unsigned long) i
;
1151 * Value used for compiler (word size/field alignment/padding) check.
1153 version
.struct_sizes1
=
1154 (((unsigned long) sizeof(struct context_info
) << 24)
1155 | ((unsigned long) sizeof(struct obj
) << 17)
1156 | ((unsigned long) sizeof(struct monst
) << 10)
1157 | ((unsigned long) sizeof(struct you
)));
1158 version
.struct_sizes2
= (((unsigned long) sizeof(struct flag
) << 10) |
1159 /* free bits in here */
1161 ((unsigned long) sizeof(struct sysflag
)));
1163 ((unsigned long) 0L));
1168 /* REPRODUCIBLE_BUILD will change this to TRUE */
1169 static boolean date_via_env
= FALSE
;
1172 version_string(outbuf
, delim
)
1176 Sprintf(outbuf
, "%d%s%d%s%d", VERSION_MAJOR
, delim
, VERSION_MINOR
, delim
,
1179 Sprintf(eos(outbuf
), "-%d", EDITLEVEL
);
1185 version_id_string(outbuf
, build_date
)
1187 const char *build_date
;
1189 char subbuf
[64], versbuf
[64];
1194 Strcpy(&subbuf
[1], PORT_SUB_ID
);
1197 Strcat(subbuf
, " Beta");
1200 Sprintf(outbuf
, "%s NetHack%s Version %s - last %s %s.", PORT_ID
,
1201 subbuf
, version_string(versbuf
, "."),
1202 date_via_env
? "revision" : "build", build_date
);
1207 bannerc_string(outbuf
, build_date
)
1209 const char *build_date
;
1211 char subbuf
[64], versbuf
[64];
1216 Strcpy(&subbuf
[1], PORT_SUB_ID
);
1219 Strcat(subbuf
, " Beta");
1222 Sprintf(outbuf
, " Version %s %s%s, %s %s.",
1223 version_string(versbuf
, "."), PORT_ID
, subbuf
,
1224 date_via_env
? "revised" : "built", &build_date
[4]);
1226 Sprintf(outbuf
, "%s NetHack%s %s Copyright 1985-%s (built %s)",
1227 PORT_ID
, subbuf
, version_string(versbuf
,"."), RELEASE_YEAR
,
1239 time_t clocktim
= 0;
1241 char *c
, cbuf
[60], buf
[BUFSZ
];
1244 /* before creating date.h, make sure that xxx_GRAPHICS and
1245 DEFAULT_WINDOW_SYS have been set up in a viable fashion */
1250 Strcat(filename
, file_prefix
);
1252 Sprintf(eos(filename
), INCLUDE_TEMPLATE
, DATE_FILE
);
1253 if (!(ofp
= fopen(filename
, WRTMODE
))) {
1257 /* NB: We've moved on from SCCS, but this way this line
1258 * won't get clobbered when downstream projects import
1259 * this file into something more modern. */
1260 Fprintf(ofp
, "%s", Dont_Edit_Code
);
1262 (void) time(&clocktim
);
1263 #ifdef REPRODUCIBLE_BUILD
1266 * Use date+time of latest source file revision (set up in
1267 * our environment rather than derived by scanning sources)
1268 * instead of current date+time, so that later rebuilds of
1269 * the same sources specifying the same configuration will
1270 * produce the same result.
1272 * Changing the configuration should be done by modifying
1273 * config.h or <port>conf.h and setting SOURCE_DATE_EPOCH
1274 * based on whichever changed most recently, not by using
1275 * make CFLAGS='-Dthis -Dthat'
1276 * to make alterations on the fly.
1278 * Limited validation is performed to prevent dates in the
1279 * future (beyond a leeway of 24 hours) or distant past.
1281 * Assumes the value of time_t is in seconds, which is
1282 * fundamental for Unix and mandated by POSIX. For any ports
1283 * where that isn't true, leaving REPRODUCIBLE_BUILD disabled
1284 * is probably preferrable to hacking this code....
1286 static struct tm nh360
; /* static init should yield UTC timezone */
1287 unsigned long sd_num
, sd_earliest
, sd_latest
;
1288 const char *sd_str
= getenv("SOURCE_DATE_EPOCH");
1291 sd_num
= strtoul(sd_str
, (char **) 0, 10);
1293 * Note: this does not need to be updated for future
1294 * releases. It serves as a sanity check for potentially
1295 * mis-set environment, not a hard baseline for when the
1296 * current version could have first been built.
1298 /* oldest date we'll accept: 7-Dec-2015 (release of 3.6.0) */
1300 nh360
.tm_mon
= 12 - 1;
1301 nh360
.tm_year
= 2015 - 1900;
1302 sd_earliest
= (unsigned long) mktime(&nh360
);
1303 /* 'youngest' date we'll accept: 24 hours in the future */
1304 sd_latest
= (unsigned long) clocktim
+ 24L * 60L * 60L;
1306 if (sd_num
>= sd_earliest
&& sd_num
<= sd_latest
) {
1307 /* use SOURCE_DATE_EPOCH value */
1308 clocktim
= (time_t) sd_num
;
1309 date_via_env
= TRUE
;
1311 Fprintf(stderr
, "? Invalid value for SOURCE_DATE_EPOCH (%lu)",
1313 if (sd_num
> 0L && sd_num
< sd_earliest
)
1314 Fprintf(stderr
, ", older than %lu", sd_earliest
);
1315 else if (sd_num
> sd_latest
)
1316 Fprintf(stderr
, ", newer than %lu", sd_latest
);
1317 Fprintf(stderr
, ".\n");
1318 Fprintf(stderr
, ": Reverting to current date+time (%lu).\n",
1319 (unsigned long) clocktim
);
1320 (void) fflush(stderr
);
1323 /* REPRODUCIBLE_BUILD enabled but SOURCE_DATE_EPOCH is missing */
1324 Fprintf(stderr
, "? No value for SOURCE_DATE_EPOCH.\n");
1325 Fprintf(stderr
, ": Using current date+time (%lu).\n",
1326 (unsigned long) clocktim
);
1327 (void) fflush(stderr
);
1329 Strcpy(cbuf
, asctime(gmtime(&clocktim
)));
1332 /* ordinary build: use current date+time */
1333 Strcpy(cbuf
, ctime(&clocktim
));
1336 if ((c
= index(cbuf
, '\n')) != 0)
1337 *c
= '\0'; /* strip off the '\n' */
1344 Fprintf(ofp
, "#define SOURCE_DATE_EPOCH (%lu%s) /* via getenv() */\n",
1345 (unsigned long) clocktim
, ul_sfx
);
1346 Fprintf(ofp
, "#define BUILD_DATE \"%s\"\n", cbuf
);
1348 Fprintf(ofp
, "#define BUILD_TIME SOURCE_DATE_EPOCH\n");
1350 Fprintf(ofp
, "#define BUILD_TIME (%lu%s)\n",
1351 (unsigned long) clocktim
, ul_sfx
);
1353 Fprintf(ofp
, "#define VERSION_NUMBER 0x%08lx%s\n", version
.incarnation
,
1355 Fprintf(ofp
, "#define VERSION_FEATURES 0x%08lx%s\n", version
.feature_set
,
1357 #ifdef IGNORED_FEATURES
1358 Fprintf(ofp
, "#define IGNORED_FEATURES 0x%08lx%s\n",
1359 (unsigned long) IGNORED_FEATURES
, ul_sfx
);
1361 Fprintf(ofp
, "#define VERSION_SANITY1 0x%08lx%s\n", version
.entity_count
,
1363 Fprintf(ofp
, "#define VERSION_SANITY2 0x%08lx%s\n", version
.struct_sizes1
,
1365 Fprintf(ofp
, "#define VERSION_SANITY3 0x%08lx%s\n", version
.struct_sizes2
,
1368 Fprintf(ofp
, "#define VERSION_STRING \"%s\"\n", version_string(buf
, "."));
1369 Fprintf(ofp
, "#define VERSION_ID \\\n \"%s\"\n",
1370 version_id_string(buf
, cbuf
));
1371 Fprintf(ofp
, "#define COPYRIGHT_BANNER_C \\\n \"%s\"\n",
1372 bannerc_string(buf
, cbuf
));
1376 struct tm
*tm
= localtime((time_t *) &clocktim
);
1378 Fprintf(ofp
, "#define AMIGA_VERSION_STRING ");
1379 Fprintf(ofp
, "\"\\0$VER: NetHack %d.%d.%d (%d.%d.%d)\"\n",
1380 VERSION_MAJOR
, VERSION_MINOR
, PATCHLEVEL
,
1381 tm
->tm_mday
, tm
->tm_mon
+ 1, tm
->tm_year
+ 1900);
1388 static char save_bones_compat_buf
[BUFSZ
];
1391 build_savebones_compat_string()
1393 #ifdef VERSION_COMPATIBILITY
1394 unsigned long uver
= VERSION_COMPATIBILITY
;
1396 Strcpy(save_bones_compat_buf
,
1397 "save and bones files accepted from version");
1398 #ifdef VERSION_COMPATIBILITY
1399 Sprintf(eos(save_bones_compat_buf
), "s %lu.%lu.%lu through %d.%d.%d",
1400 ((uver
& 0xFF000000L
) >> 24), ((uver
& 0x00FF0000L
) >> 16),
1401 ((uver
& 0x0000FF00L
) >> 8), VERSION_MAJOR
, VERSION_MINOR
,
1404 Sprintf(eos(save_bones_compat_buf
), " %d.%d.%d only", VERSION_MAJOR
,
1405 VERSION_MINOR
, PATCHLEVEL
);
1409 static const char *build_opts
[] = {
1411 "Amiga WorkBench support",
1414 "ANSI default terminal",
1419 #ifdef TTY_TILES_ESCCODES
1420 "console escape codes for tile hinting",
1423 "command line completion",
1426 "Conway's Game of Life",
1429 "data file compression",
1432 "ZLIB data file compression",
1438 "floppy drive support",
1441 "insurance files for recovering from crashes",
1443 #ifdef HOLD_LOCKFILE_OPEN
1444 "exclusive lock on level 0 file",
1446 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
1447 "external program as a message handler",
1456 "MSDOS protected mode",
1473 "restore saved games via menu",
1475 #ifdef SCORE_ON_BOTL
1476 "score on status line",
1483 "screen control via mactty",
1486 "screen control via BIOS",
1488 #ifdef SCREEN_DJGPPFAST
1489 "screen control via DJGPP fast",
1492 "screen control via VGA graphics",
1495 "screen control via WIN32 console I/O",
1501 #ifdef STATUS_VIA_WINDOWPORT
1502 # ifdef STATUS_HILITES
1503 "status via windowport with highlighting",
1505 "status via windowport without highlighting",
1508 "traditional status display",
1514 "terminal info library",
1516 #if defined(TERMLIB) \
1517 || ((!defined(MICRO) && !defined(WIN32)) && defined(TTY_GRAPHICS))
1518 "terminal capability library",
1522 "timed wait for display effects",
1527 #ifdef PREFIXES_IN_USE
1528 "variable playground",
1530 #ifdef VISION_TABLES
1534 "zero-compressed save files",
1537 "run-length compression of map in save files",
1540 "system configuration at run-time",
1542 save_bones_compat_buf
, "and basic NetHack features"
1546 const char *id
, /* DEFAULT_WINDOW_SYS string */
1547 *name
; /* description, often same as id */
1549 static struct win_info window_opts
[] = {
1551 { "tty", "traditional tty-based graphics" },
1559 #ifdef GNOME_GRAPHICS
1560 { "Gnome", "Gnome" },
1565 #ifdef AMIGA_INTUITION
1566 { "amii", "Amiga Intuition" },
1571 #ifdef MSWIN_GRAPHICS
1572 { "mswin", "mswin" },
1574 #ifdef BEOS_GRAPHICS
1575 { "BeOS", "BeOS InterfaceKit" },
1583 #ifndef DEFAULT_WINDOW_SYS
1584 /* pre-standard compilers didn't support #error; wait til run-time */
1586 "Configuration error: DEFAULT_WINDOW_SYS is not defined.\n");
1590 /* put in a dummy value so that do_options() will compile and makedefs
1591 will build, otherwise the message above won't ever get delivered */
1592 #define DEFAULT_WINDOW_SYS "<undefined>"
1593 #else /*DEFAULT_WINDOW_SYS*/
1595 if (!window_opts
[0].id
) {
1596 Fprintf(stderr
, "Configuration error: no windowing systems "
1597 "(TTY_GRAPHICS, &c) enabled.\n");
1604 for (i
= 0; window_opts
[i
].id
; ++i
)
1605 if (!strcmp(window_opts
[i
].id
, DEFAULT_WINDOW_SYS
))
1608 .id
) { /* went through whole list without a match */
1609 Fprintf(stderr
, "Configuration error: DEFAULT_WINDOW_SYS (%s)\n",
1610 DEFAULT_WINDOW_SYS
);
1612 " does not match any enabled windowing system (%s%s).\n",
1613 window_opts
[0].id
, window_opts
[1].id
? ", &c" : "");
1617 #endif /*DEFAULT_WINDOW_SYS*/
1623 static const char indent
[] = " ";
1624 const char *str
, *sep
;
1625 char *word
, buf
[BUFSZ
];
1626 int i
, length
, winsyscnt
;
1632 Strcat(filename
, file_prefix
);
1634 Sprintf(eos(filename
), DATA_TEMPLATE
, OPTIONS_FILE
);
1635 if (!(ofp
= fopen(filename
, WRTMODE
))) {
1640 build_savebones_compat_string();
1643 "\n NetHack version %d.%d.%d [beta]\n",
1645 "\n NetHack version %d.%d.%d\n",
1647 VERSION_MAJOR
, VERSION_MINOR
, PATCHLEVEL
);
1649 Fprintf(ofp
, "\nOptions compiled into this edition:\n");
1650 length
= COLNO
+ 1; /* force 1st item onto new line */
1651 for (i
= 0; i
< SIZE(build_opts
); i
++) {
1652 str
= strcpy(buf
, build_opts
[i
]);
1654 word
= index(str
, ' ');
1657 if (length
+ strlen(str
) > COLNO
- 5)
1658 Fprintf(ofp
, "\n%s", indent
), length
= strlen(indent
);
1660 Fprintf(ofp
, " "), length
++;
1661 Fprintf(ofp
, "%s", str
), length
+= strlen(str
);
1662 str
+= strlen(str
) + (word
? 1 : 0);
1664 Fprintf(ofp
, (i
< SIZE(build_opts
) - 1) ? "," : "."), length
++;
1667 winsyscnt
= SIZE(window_opts
) - 1;
1668 Fprintf(ofp
, "\n\nSupported windowing system%s:\n",
1669 (winsyscnt
> 1) ? "s" : "");
1670 length
= COLNO
+ 1; /* force 1st item onto new line */
1671 for (i
= 0; i
< winsyscnt
; i
++) {
1672 str
= window_opts
[i
].name
;
1673 if (length
+ strlen(str
) > COLNO
- 5)
1674 Fprintf(ofp
, "\n%s", indent
), length
= strlen(indent
);
1676 Fprintf(ofp
, " "), length
++;
1677 Fprintf(ofp
, "%s", str
), length
+= strlen(str
);
1678 sep
= (winsyscnt
== 1)
1681 ? ((i
== 0) ? " and" : "")
1682 : (i
< winsyscnt
- 2)
1684 : ((i
== winsyscnt
- 2) ? ", and" : "");
1685 Fprintf(ofp
, "%s", sep
), length
+= strlen(sep
);
1688 Fprintf(ofp
, "\n%swith a default of %s.", indent
, DEFAULT_WINDOW_SYS
);
1689 Fprintf(ofp
, "\n\n");
1695 /* routine to decide whether to discard something from data.base */
1701 return TRUE
; /* ignore comment lines */
1707 New format (v3.1) of 'data' file which allows much faster lookups [pr]
1708 "do not edit" first record is a comment line
1709 01234567 hexadecimal formatted offset to text area
1710 name-a first name of interest
1711 123,4 offset to name's text, and number of lines for it
1712 name-b next name of interest
1713 name-c multiple names which share same description also
1714 456,7 share a single offset,count line
1715 . sentinel to mark end of names
1716 789,0 dummy record containing offset, count of EOF
1717 text-a 4 lines of descriptive text for name-a
1718 text-a at file position 0x01234567L + 123L
1721 text-b/text-c 7 lines of text for names-b and -c
1722 text-b/text-c at fseek(0x01234567L + 456L)
1730 char infile
[60], tempfile
[60];
1733 int entry_cnt
, line_cnt
;
1736 Sprintf(tempfile
, DATA_TEMPLATE
, "database.tmp");
1739 Strcat(filename
, file_prefix
);
1741 Sprintf(eos(filename
), DATA_TEMPLATE
, DATA_FILE
);
1742 Sprintf(infile
, DATA_IN_TEMPLATE
, DATA_FILE
);
1743 #ifdef SHORT_FILENAMES
1744 Strcat(infile
, ".bas");
1746 Strcat(infile
, ".base");
1748 if (!(ifp
= fopen(infile
, RDTMODE
))) { /* data.base */
1752 if (!(ofp
= fopen(filename
, WRTMODE
))) { /* data */
1757 if (!(tfp
= fopen(tempfile
, WRTMODE
))) { /* database.tmp */
1765 /* output a dummy header record; we'll rewind and overwrite it later */
1766 Fprintf(ofp
, "%s%08lx\n", Dont_Edit_Data
, 0L);
1768 entry_cnt
= line_cnt
= 0;
1769 /* read through the input file and split it into two sections */
1770 while ((line
= fgetline(ifp
)) != 0) {
1771 if (d_filter(line
)) {
1775 if (*line
> ' ') { /* got an entry name */
1776 /* first finish previous entry */
1778 Fprintf(ofp
, "%d\n", line_cnt
), line_cnt
= 0;
1779 /* output the entry name */
1780 (void) fputs(line
, ofp
);
1781 entry_cnt
++; /* update number of entries */
1782 } else if (entry_cnt
) { /* got some descriptive text */
1783 /* update previous entry with current text offset */
1785 Fprintf(ofp
, "%ld,", ftell(tfp
));
1786 /* save the text line in the scratch file */
1787 (void) fputs(line
, tfp
);
1788 line_cnt
++; /* update line counter */
1792 /* output an end marker and then record the current position */
1794 Fprintf(ofp
, "%d\n", line_cnt
);
1795 Fprintf(ofp
, ".\n%ld,%d\n", ftell(tfp
), 0);
1796 txt_offset
= ftell(ofp
);
1797 Fclose(ifp
); /* all done with original input file */
1799 /* reprocess the scratch file; 1st format an error msg, just in case */
1801 Sprintf(line
, "rewind of \"%s\"", tempfile
);
1802 if (rewind(tfp
) != 0)
1805 /* copy all lines of text from the scratch file into the output file */
1806 while ((line
= fgetline(tfp
)) != 0) {
1807 (void) fputs(line
, ofp
);
1811 /* finished with scratch file */
1813 Unlink(tempfile
); /* remove it */
1815 /* update the first record of the output file; prepare error msg 1st */
1817 Sprintf(line
, "rewind of \"%s\"", filename
);
1818 ok
= (rewind(ofp
) == 0);
1820 Sprintf(line
, "header rewrite of \"%s\"", filename
);
1821 ok
= (fprintf(ofp
, "%s%08lx\n", Dont_Edit_Data
,
1822 (unsigned long) txt_offset
) >= 0);
1826 perror(line
); /* report the problem */
1828 /* close and kill the aborted output file, then give up */
1841 /* routine to decide whether to discard something from oracles.txt */
1846 static boolean skip
= FALSE
;
1852 return TRUE
; /* ignore comment lines */
1854 tag
= malloc(strlen(line
));
1855 if (sscanf(line
, "----- %s", tag
) == 1) {
1857 } else if (skip
&& !strncmp(line
, "-----", 5))
1863 static const char *special_oracle
[] = {
1864 "\"...it is rather disconcerting to be confronted with the",
1865 "following theorem from [Baker, Gill, and Solovay, 1975].", "",
1866 "Theorem 7.18 There exist recursive languages A and B such that",
1867 " (1) P(A) == NP(A), and", " (2) P(B) != NP(B)", "",
1868 "This provides impressive evidence that the techniques that are",
1869 "currently available will not suffice for proving that P != NP or "
1871 "that P == NP.\" [Garey and Johnson, p. 185.]"
1875 The oracle file consists of a "do not edit" comment, a decimal count N
1876 and set of N+1 hexadecimal fseek offsets, followed by N multiple-line
1877 records, separated by "---" lines. The first oracle is a special case.
1878 The input data contains just those multi-line records, separated by
1885 char infile
[60], tempfile
[60];
1886 boolean in_oracle
, ok
;
1888 unsigned long txt_offset
, offset
;
1893 Sprintf(tempfile
, DATA_TEMPLATE
, "oracles.tmp");
1896 Strcat(filename
, file_prefix
);
1898 Sprintf(eos(filename
), DATA_TEMPLATE
, ORACLE_FILE
);
1899 Sprintf(infile
, DATA_IN_TEMPLATE
, ORACLE_FILE
);
1900 Strcat(infile
, ".txt");
1901 if (!(ifp
= fopen(infile
, RDTMODE
))) {
1905 if (!(ofp
= fopen(filename
, WRTMODE
))) {
1910 if (!(tfp
= fopen(tempfile
, WRTMODE
))) { /* oracles.tmp */
1918 /* output a dummy header record; we'll rewind and overwrite it later */
1919 Fprintf(ofp
, "%s%5d\n", Dont_Edit_Data
, 0);
1921 /* handle special oracle; it must come first */
1922 (void) fputs("---\n", tfp
);
1923 offset
= (unsigned long) ftell(tfp
);
1924 Fprintf(ofp
, "%05lx\n", offset
); /* start pos of special oracle */
1925 for (i
= 0; i
< SIZE(special_oracle
); i
++) {
1926 (void) fputs(xcrypt(special_oracle
[i
]), tfp
);
1927 (void) fputc('\n', tfp
);
1932 (void) fputs("---\n", tfp
);
1933 offset
= (unsigned long) ftell(tfp
);
1934 Fprintf(ofp
, "%05lx\n", offset
); /* start pos of first oracle */
1937 while ((line
= fgetline(ifp
)) != 0) {
1940 if (h_filter(line
)) {
1944 if (!strncmp(line
, "-----", 5)) {
1951 (void) fputs("---\n", tfp
);
1952 offset
= (unsigned long) ftell(tfp
);
1953 Fprintf(ofp
, "%05lx\n", offset
); /* start pos of this oracle */
1956 (void) fputs(xcrypt(line
), tfp
);
1961 if (in_oracle
) { /* need to terminate last oracle */
1963 (void) fputs("---\n", tfp
);
1964 offset
= (unsigned long) ftell(tfp
);
1965 Fprintf(ofp
, "%05lx\n", offset
); /* eof position */
1968 /* record the current position */
1969 txt_offset
= (unsigned long) ftell(ofp
);
1970 Fclose(ifp
); /* all done with original input file */
1972 /* reprocess the scratch file; 1st format an error msg, just in case */
1974 Sprintf(line
, "rewind of \"%s\"", tempfile
);
1975 if (rewind(tfp
) != 0)
1978 /* copy all lines of text from the scratch file into the output file */
1979 while ((line
= fgetline(tfp
)) != 0) {
1980 (void) fputs(line
, ofp
);
1984 /* finished with scratch file */
1986 Unlink(tempfile
); /* remove it */
1988 /* update the first record of the output file; prepare error msg 1st */
1990 Sprintf(line
, "rewind of \"%s\"", filename
);
1991 ok
= (rewind(ofp
) == 0);
1993 Sprintf(line
, "header rewrite of \"%s\"", filename
);
1994 ok
= (fprintf(ofp
, "%s%5d\n", Dont_Edit_Data
, oracle_cnt
) >= 0);
1997 Sprintf(line
, "data rewrite of \"%s\"", filename
);
1998 for (i
= 0; i
<= oracle_cnt
; i
++) {
1999 #ifndef VMS /* alpha/vms v1.0; this fflush seems to confuse ftell */
2000 if (!(ok
= (fflush(ofp
) == 0)))
2003 if (!(ok
= (fpos
= ftell(ofp
)) >= 0))
2005 if (!(ok
= (fseek(ofp
, fpos
, SEEK_SET
) >= 0)))
2007 if (!(ok
= (fscanf(ofp
, "%5lx", &offset
) == 1)))
2012 MetroWerks CodeWarrior Pro 1's (AKA CW12) version of MSL
2013 (ANSI C Libraries) needs this rewind or else the fprintf
2014 stops working. This may also be true for CW11, but has
2020 if (!(ok
= (fseek(ofp
, fpos
, SEEK_SET
) >= 0)))
2022 offset
+= txt_offset
;
2023 if (!(ok
= (fprintf(ofp
, "%05lx\n", offset
) >= 0)))
2029 perror(line
); /* report the problem */
2031 /* close and kill the aborted output file, then give up */
2049 Sprintf(filename
, DATA_IN_TEMPLATE
, DGN_I_FILE
);
2050 if (!(ifp
= fopen(filename
, RDTMODE
))) {
2056 Strcat(filename
, file_prefix
);
2058 Sprintf(eos(filename
), DGN_TEMPLATE
, DGN_O_FILE
);
2059 if (!(ofp
= fopen(filename
, WRTMODE
))) {
2063 Fprintf(ofp
, "%s", Dont_Edit_Data
);
2065 tfp
= getfp(DATA_TEMPLATE
, "grep.tmp", WRTMODE
);
2067 ifp
= getfp(DATA_TEMPLATE
, "grep.tmp", RDTMODE
);
2069 while ((line
= fgetline(ifp
)) != 0) {
2072 if (line
[0] == '#') {
2074 continue; /* discard comments */
2076 (void) fputs(line
, ofp
);
2082 delete_file(DATA_TEMPLATE
, "grep.tmp");
2087 ranged_attk(ptr
) /* returns TRUE if monster can attack at range */
2088 register struct permonst
*ptr
;
2091 register int atk_mask
= (1 << AT_BREA
) | (1 << AT_SPIT
) | (1 << AT_GAZE
);
2093 for (i
= 0; i
< NATTK
; i
++) {
2094 if ((j
= ptr
->mattk
[i
].aatyp
) >= AT_WEAP
|| (atk_mask
& (1 << j
)))
2101 /* This routine is designed to return an integer value which represents
2102 * an approximation of monster strength. It uses a similar method of
2103 * determination as "experience()" to arrive at the strength.
2107 struct permonst
*ptr
;
2109 int i
, tmp2
, n
, tmp
= ptr
->mlevel
;
2111 if (tmp
> 49) /* special fixed hp monster */
2112 tmp
= 2 * (tmp
- 6) / 4;
2114 /* For creation in groups */
2115 n
= (!!(ptr
->geno
& G_SGROUP
));
2116 n
+= (!!(ptr
->geno
& G_LGROUP
)) << 1;
2118 /* For ranged attacks */
2119 if (ranged_attk(ptr
))
2122 /* For higher ac values */
2126 /* For very fast monsters */
2127 n
+= (ptr
->mmove
>= 18);
2129 /* For each attack and "special" attack */
2130 for (i
= 0; i
< NATTK
; i
++) {
2131 tmp2
= ptr
->mattk
[i
].aatyp
;
2133 n
+= (tmp2
== AT_MAGC
);
2134 n
+= (tmp2
== AT_WEAP
&& (ptr
->mflags2
& M2_STRONG
));
2137 /* For each "special" damage type */
2138 for (i
= 0; i
< NATTK
; i
++) {
2139 tmp2
= ptr
->mattk
[i
].adtyp
;
2140 if ((tmp2
== AD_DRLI
) || (tmp2
== AD_STON
) || (tmp2
== AD_DRST
)
2141 || (tmp2
== AD_DRDX
) || (tmp2
== AD_DRCO
) || (tmp2
== AD_WERE
))
2143 else if (strcmp(ptr
->mname
, "grid bug"))
2144 n
+= (tmp2
!= AD_PHYS
);
2145 n
+= ((int) (ptr
->mattk
[i
].damd
* ptr
->mattk
[i
].damn
) > 23);
2148 /* Leprechauns are special cases. They have many hit dice so they can
2149 hit and are hard to kill, but they don't really do much damage. */
2150 if (!strcmp(ptr
->mname
, "leprechaun"))
2153 /* Finally, adjust the monster level 0 <= n <= 24 (approx.) */
2161 return (tmp
>= 0) ? tmp
: 0;
2167 register struct permonst
*ptr
;
2171 * create the source file, "monstr.c"
2175 Strcat(filename
, file_prefix
);
2177 Sprintf(eos(filename
), SOURCE_TEMPLATE
, MON_STR_C
);
2178 if (!(ofp
= fopen(filename
, WRTMODE
))) {
2182 Fprintf(ofp
, "%s", Dont_Edit_Code
);
2183 Fprintf(ofp
, "#include \"config.h\"\n");
2184 Fprintf(ofp
, "\nconst int monstr[] = {\n");
2185 for (ptr
= &mons
[0], j
= 0; ptr
->mlet
; ptr
++) {
2189 Fprintf(ofp
, "%2d,%c", i
, (++j
& 15) ? ' ' : '\n');
2191 /* might want to insert a final 0 entry here instead of just newline */
2192 Fprintf(ofp
, "%s};\n", (j
& 15) ? "\n" : "");
2194 Fprintf(ofp
, "\nvoid NDECL(monstr_init);\n");
2195 Fprintf(ofp
, "\nvoid\n");
2196 Fprintf(ofp
, "monstr_init()\n");
2197 Fprintf(ofp
, "{\n");
2198 Fprintf(ofp
, " return;\n");
2199 Fprintf(ofp
, "}\n");
2200 Fprintf(ofp
, "\n/*monstr.c*/\n");
2214 Strcat(filename
, file_prefix
);
2216 Sprintf(eos(filename
), INCLUDE_TEMPLATE
, MONST_FILE
);
2217 if (!(ofp
= fopen(filename
, WRTMODE
))) {
2221 Fprintf(ofp
, "%s", Dont_Edit_Code
);
2222 Fprintf(ofp
, "#ifndef PM_H\n#define PM_H\n");
2224 if (strcmp(mons
[0].mname
, "playermon") != 0)
2225 Fprintf(ofp
, "\n#define\tPM_PLAYERMON\t(-1)");
2227 for (i
= 0; mons
[i
].mlet
; i
++) {
2230 Fprintf(ofp
, "\n#define\tPM_");
2231 if (mons
[i
].mlet
== S_HUMAN
&& !strncmp(mons
[i
].mname
, "were", 4))
2232 Fprintf(ofp
, "HUMAN_");
2233 for (nam
= c
= tmpdup(mons
[i
].mname
); *c
; c
++)
2234 if (*c
>= 'a' && *c
<= 'z')
2235 *c
-= (char) ('a' - 'A');
2236 else if (*c
< 'A' || *c
> 'Z')
2238 Fprintf(ofp
, "%s\t%d", nam
, i
);
2240 Fprintf(ofp
, "\n\n#define\tNUMMONS\t%d\n", i
);
2241 Fprintf(ofp
, "\n#endif /* PM_H */\n");
2246 /* Start of Quest text file processing. */
2249 static struct qthdr qt_hdr
;
2250 static struct msghdr msg_hdr
[N_HDR
];
2251 static struct qtmsg
*curr_msg
;
2255 static boolean in_msg
;
2256 #define NO_MSG 1 /* strlen of a null line returned by fgets() */
2264 return (boolean
) (!in_msg
&& strlen(s
) == NO_MSG
);
2271 return (boolean
) (s
[0] == '%' && (s
[1] == 'C' || s
[1] == 'E'));
2280 for (i
= 0; i
< qt_hdr
.n_hdr
; i
++)
2281 if (!strncmp(code
, qt_hdr
.id
[i
], LEN_HDR
))
2291 if (qt_hdr
.n_hdr
>= N_HDR
) {
2292 Fprintf(stderr
, OUT_OF_HEADERS
, qt_line
);
2296 strncpy(&qt_hdr
.id
[qt_hdr
.n_hdr
][0], code
, LEN_HDR
);
2297 msg_hdr
[qt_hdr
.n_hdr
].n_msg
= 0;
2298 qt_hdr
.offset
[qt_hdr
.n_hdr
++] = 0L;
2308 for (i
= 0; i
< msg_hdr
[num
].n_msg
; i
++)
2309 if (msg_hdr
[num
].qt_msg
[i
].msgnum
== id
)
2320 struct qtmsg
*qt_msg
;
2322 if (msg_hdr
[num
].n_msg
>= N_MSG
) {
2323 Fprintf(stderr
, OUT_OF_MESSAGES
, qt_line
);
2325 qt_msg
= &(msg_hdr
[num
].qt_msg
[msg_hdr
[num
].n_msg
++]);
2326 qt_msg
->msgnum
= id
;
2327 qt_msg
->delivery
= s
[2];
2328 qt_msg
->offset
= qt_msg
->size
= qt_msg
->summary_size
= 0L;
2334 /* check %E record for "[summary text]" that nethack can stuff into the
2335 message history buffer when delivering text via window instead of pline */
2337 valid_qt_summary(s
, parsing
)
2338 char *s
; /* end record: "%E" optionally followed by " [summary]" */
2339 boolean parsing
; /* curr_msg is valid iff this is True */
2341 static char summary
[BUFSZ
];
2344 if (*s
!= '%' || *(s
+ 1) != 'E')
2346 if ((p
= index(s
, '[')) == 0)
2348 /* note: opening '[' and closing ']' will be retained in the output;
2349 anything after ']' will be discarded by putting a newline there */
2352 /* have an opening bracket; summary[] holds it and all text that follows
2355 /* find closing bracket */
2356 while (p
> summary
&& *(p
- 1) != ']')
2360 /* we backed up all the way to the start without finding a bracket */
2361 if (parsing
) /* malformed summary */
2362 Fprintf(stderr
, MAL_SUM
, qt_line
);
2363 } else if (p
== summary
+ 1) {
2364 ; /* ignore empty [] */
2365 } else { /* got something */
2366 /* p points one spot past ']', usually to '\n';
2367 we need to include the \n as part of the size */
2369 /* during the writing pass we won't be able to recheck
2370 delivery, so any useless summary for a pline mode
2371 message has to be carried along to the output file */
2372 if (curr_msg
->delivery
== 'p')
2373 Fprintf(stderr
, DUMB_SUM
, qt_line
);
2374 /* +1 is for terminating newline */
2375 curr_msg
->summary_size
= (long) (p
- summary
) + 1L;
2377 /* caller is writing rather than just parsing;
2378 force newline after the closing bracket */
2393 if (!index(s
, '\n'))
2394 Fprintf(stderr
, CTRL_TRUNC
, qt_line
);
2399 Fprintf(stderr
, CREC_IN_MSG
, qt_line
);
2403 if (sscanf(&s
[4], "%s %5d", code
, &id
) != 2) {
2404 Fprintf(stderr
, UNREC_CREC
, qt_line
);
2407 num
= get_hdr(code
);
2408 if (!num
&& !new_id(code
))
2410 num
= get_hdr(code
) - 1;
2411 if (known_msg(num
, id
))
2412 Fprintf(stderr
, DUP_MSG
, qt_line
);
2414 new_msg(s
, num
, id
);
2420 Fprintf(stderr
, END_NOT_IN_MSG
, qt_line
);
2422 /* sets curr_msg->summary_size if applicable */
2423 (void) valid_qt_summary(s
, TRUE
);
2429 Fprintf(stderr
, UNREC_CREC
, qt_line
);
2439 Fprintf(stderr
, TEXT_NOT_IN_MSG
, qt_line
);
2440 } else if (!index(s
, '\n')) {
2441 Fprintf(stderr
, TEXT_TRUNC
, qt_line
);
2444 curr_msg
->size
+= strlen(s
);
2452 long count
= 0L, hdr_offset
= sizeof(int)
2453 + (sizeof(char) * LEN_HDR
+ sizeof(long))
2456 for (i
= 0; i
< qt_hdr
.n_hdr
; i
++) {
2457 qt_hdr
.offset
[i
] = hdr_offset
;
2458 hdr_offset
+= sizeof(int) + sizeof(struct qtmsg
) * msg_hdr
[i
].n_msg
;
2461 for (i
= 0; i
< qt_hdr
.n_hdr
; i
++)
2462 for (j
= 0; j
< msg_hdr
[i
].n_msg
; j
++) {
2463 msg_hdr
[i
].qt_msg
[j
].offset
= hdr_offset
+ count
;
2465 msg_hdr
[i
].qt_msg
[j
].size
+ msg_hdr
[i
].qt_msg
[j
].summary_size
;
2476 * The main header record.
2479 Fprintf(stderr
, "%ld: header info.\n", ftell(ofp
));
2480 (void) fwrite((genericptr_t
) & (qt_hdr
.n_hdr
), sizeof(int), 1, ofp
);
2481 (void) fwrite((genericptr_t
) & (qt_hdr
.id
[0][0]), sizeof(char) * LEN_HDR
,
2483 (void) fwrite((genericptr_t
) & (qt_hdr
.offset
[0]), sizeof(long),
2486 for (i
= 0; i
< qt_hdr
.n_hdr
; i
++)
2487 Fprintf(stderr
, "%s @ %ld, ", qt_hdr
.id
[i
], qt_hdr
.offset
[i
]);
2488 Fprintf(stderr
, "\n");
2492 * The individual class headers.
2494 for (i
= 0; i
< qt_hdr
.n_hdr
; i
++) {
2496 Fprintf(stderr
, "%ld: %s header info.\n", ftell(ofp
),
2498 (void) fwrite((genericptr_t
) & (msg_hdr
[i
].n_msg
), sizeof(int), 1,
2500 (void) fwrite((genericptr_t
) & (msg_hdr
[i
].qt_msg
[0]),
2501 sizeof(struct qtmsg
), msg_hdr
[i
].n_msg
, ofp
);
2505 for (j
= 0; j
< msg_hdr
[i
].n_msg
; j
++) {
2506 Fprintf(stderr
, "msg %d @ %ld (%ld)",
2507 msg_hdr
[i
].qt_msg
[j
].msgnum
,
2508 msg_hdr
[i
].qt_msg
[j
].offset
,
2509 msg_hdr
[i
].qt_msg
[j
].size
);
2510 if (msg_hdr
[i
].qt_msg
[j
].summary_size
)
2511 Fprintf(stderr
, " [%ld]",
2512 msg_hdr
[i
].qt_msg
[j
].summary_size
);
2513 Fprintf(stderr
, "\n");
2524 Sprintf(filename
, DATA_IN_TEMPLATE
, QTXT_I_FILE
);
2525 if (!(ifp
= fopen(filename
, RDTMODE
))) {
2532 Strcat(filename
, file_prefix
);
2534 Sprintf(eos(filename
), DATA_TEMPLATE
, QTXT_O_FILE
);
2535 if (!(ofp
= fopen(filename
, WRBMODE
))) {
2545 while ((line
= fgetline(ifp
)) != 0) {
2549 if (qt_control(line
))
2550 do_qt_control(line
);
2551 else if (qt_comment(line
)) {
2563 while ((line
= fgetline(ifp
)) != 0) {
2564 if (qt_control(line
)) {
2565 char *summary_p
= 0;
2567 in_msg
= (line
[1] == 'C');
2569 summary_p
= valid_qt_summary(line
, FALSE
);
2570 /* don't write anything unless we've got a summary */
2575 /* we have summary text; replace raw %E record with it */
2576 Strcpy(line
, summary_p
); /* (guaranteed to fit) */
2577 } else if (qt_comment(line
)) {
2582 Fprintf(stderr
, "%ld: %s", ftell(stdout
), line
);
2583 (void) fputs(xcrypt(line
), ofp
);
2591 static char temp
[32];
2593 static char *limit(name
, pref
) /* limit a name to 30 characters length */
2597 (void) strncpy(temp
, name
, pref
? 26 : 30);
2598 temp
[pref
? 26 : 30] = 0;
2610 boolean sumerr
= FALSE
;
2614 Strcat(filename
, file_prefix
);
2616 Sprintf(eos(filename
), INCLUDE_TEMPLATE
, ONAME_FILE
);
2617 if (!(ofp
= fopen(filename
, WRTMODE
))) {
2621 Fprintf(ofp
, "%s", Dont_Edit_Code
);
2622 Fprintf(ofp
, "#ifndef ONAMES_H\n#define ONAMES_H\n\n");
2624 for (i
= 0; !i
|| objects
[i
].oc_class
!= ILLOBJ_CLASS
; i
++) {
2627 objects
[i
].oc_name_idx
= objects
[i
].oc_descr_idx
= i
; /* init */
2628 if (!(objnam
= tmpdup(OBJ_NAME(objects
[i
]))))
2631 /* make sure probabilities add up to 1000 */
2632 if (objects
[i
].oc_class
!= class) {
2633 if (sum
&& sum
!= 1000) {
2634 Fprintf(stderr
, "prob error for class %d (%d%%)", class, sum
);
2635 (void) fflush(stderr
);
2638 class = objects
[i
].oc_class
;
2642 for (c
= objnam
; *c
; c
++)
2643 if (*c
>= 'a' && *c
<= 'z')
2644 *c
-= (char) ('a' - 'A');
2645 else if (*c
< 'A' || *c
> 'Z')
2650 Fprintf(ofp
, "#define\tWAN_");
2654 Fprintf(ofp
, "#define\tRIN_");
2658 Fprintf(ofp
, "#define\tPOT_");
2662 Fprintf(ofp
, "#define\tSPE_");
2667 Fprintf(ofp
, "#define\tSCR_");
2671 /* avoid trouble with stupid C preprocessors */
2672 Fprintf(ofp
, "#define\t");
2673 if (objects
[i
].oc_material
== PLASTIC
) {
2674 Fprintf(ofp
, "FAKE_AMULET_OF_YENDOR\t%d\n", i
);
2680 /* avoid trouble with stupid C preprocessors */
2681 if (objects
[i
].oc_material
== GLASS
) {
2682 Fprintf(ofp
, "/* #define\t%s\t%d */\n", objnam
, i
);
2687 Fprintf(ofp
, "#define\t");
2690 Fprintf(ofp
, "%s\t%d\n", limit(objnam
, prefix
), i
);
2693 sum
+= objects
[i
].oc_prob
;
2696 /* check last set of probabilities */
2697 if (sum
&& sum
!= 1000) {
2698 Fprintf(stderr
, "prob error for class %d (%d%%)", class, sum
);
2699 (void) fflush(stderr
);
2703 Fprintf(ofp
, "#define\tLAST_GEM\t(JADE)\n");
2704 Fprintf(ofp
, "#define\tMAXSPELL\t%d\n", nspell
+ 1);
2705 Fprintf(ofp
, "#define\tNUM_OBJECTS\t%d\n", i
);
2707 Fprintf(ofp
, "\n/* Artifacts (unique objects) */\n\n");
2709 for (i
= 1; artifact_names
[i
]; i
++) {
2712 for (c
= objnam
= tmpdup(artifact_names
[i
]); *c
; c
++)
2713 if (*c
>= 'a' && *c
<= 'z')
2714 *c
-= (char) ('a' - 'A');
2715 else if (*c
< 'A' || *c
> 'Z')
2718 if (!strncmp(objnam
, "THE_", 4))
2720 /* fudge _platinum_ YENDORIAN EXPRESS CARD */
2721 if (!strncmp(objnam
, "PLATINUM_", 9))
2723 Fprintf(ofp
, "#define\tART_%s\t%d\n", limit(objnam
, 1), i
);
2726 Fprintf(ofp
, "#define\tNROFARTIFACTS\t%d\n", i
- 1);
2727 Fprintf(ofp
, "\n#endif /* ONAMES_H */\n");
2734 /* Read one line from input, up to and including the next newline
2735 * character. Returns a pointer to the heap-allocated string, or a
2736 * null pointer if no characters were read.
2742 static const int inc
= 256;
2744 char *c
= malloc(len
), *ret
;
2747 ret
= fgets(c
+ len
- inc
, inc
, fd
);
2752 } else if (index(c
, '\n')) {
2753 /* normal case: we have a full line */
2757 c
= realloc(c
, len
);
2766 static char buf
[128];
2770 (void) strncpy(buf
, str
, 127);
2784 * macro used to control vision algorithms:
2785 * VISION_TABLES => generate tables
2791 #ifdef VISION_TABLES
2794 /* Everything is clear. xclear may be malloc'ed.
2795 * Block the upper left corner (BLOCK_HEIGHTxBLOCK_WIDTH)
2797 for (i
= 0; i
< MAX_ROW
; i
++)
2798 for (j
= 0; j
< MAX_COL
; j
++)
2799 if (i
< BLOCK_HEIGHT
&& j
< BLOCK_WIDTH
)
2800 xclear
[i
][j
] = '\000';
2802 xclear
[i
][j
] = '\001';
2803 #endif /* VISION_TABLES */
2808 * create the include file, "vis_tab.h"
2812 Strcat(filename
, file_prefix
);
2814 Sprintf(filename
, INCLUDE_TEMPLATE
, VIS_TAB_H
);
2815 if (!(ofp
= fopen(filename
, WRTMODE
))) {
2819 Fprintf(ofp
, "%s", Dont_Edit_Code
);
2820 Fprintf(ofp
, "#ifdef VISION_TABLES\n");
2821 #ifdef VISION_TABLES
2824 #endif /* VISION_TABLES */
2825 Fprintf(ofp
, "\n#endif /* VISION_TABLES */\n");
2831 * create the source file, "vis_tab.c"
2835 Strcat(filename
, file_prefix
);
2837 Sprintf(filename
, SOURCE_TEMPLATE
, VIS_TAB_C
);
2838 if (!(ofp
= fopen(filename
, WRTMODE
))) {
2840 Sprintf(filename
, INCLUDE_TEMPLATE
, VIS_TAB_H
);
2844 Fprintf(ofp
, "%s", Dont_Edit_Code
);
2845 Fprintf(ofp
, "#include \"config.h\"\n");
2846 Fprintf(ofp
, "#ifdef VISION_TABLES\n");
2847 Fprintf(ofp
, "#include \"vis_tab.h\"\n");
2851 #ifdef VISION_TABLES
2854 Fprintf(ofp
, "\nvoid vis_tab_init() { return; }\n");
2855 #endif /* VISION_TABLES */
2859 Fprintf(ofp
, "\n#endif /* VISION_TABLES */\n");
2860 Fprintf(ofp
, "\n/*vis_tab.c*/\n");
2866 #ifdef VISION_TABLES
2868 /*-------------- vision tables --------------*\
2870 * Generate the close and far tables. This is done by setting up a
2871 * fake dungeon and moving our source to different positions relative
2872 * to a block and finding the first/last visible position. The fake
2873 * dungeon is all clear execpt for the upper left corner (BLOCK_HEIGHT
2874 * by BLOCK_WIDTH) is blocked. Then we move the source around relative
2875 * to the corner of the block. For each new position of the source
2876 * we check positions on rows "kittycorner" from the source. We check
2877 * positions until they are either in sight or out of sight (depends on
2878 * which table we are generating). The picture below shows the setup
2879 * for the generation of the close table. The generation of the far
2880 * table would switch the quadrants of the '@' and the "Check rows
2884 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2885 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2886 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,, Check rows here ,,,,,,,,,,,,
2887 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2888 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXB,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2889 * ...............................
2890 * ...............................
2891 * .........@.....................
2892 * ...............................
2894 * Table generation figure (close_table). The 'X's are blocked points.
2895 * The 'B' is a special blocked point. The '@' is the source. The ','s
2896 * are the target area. The '.' are just open areas.
2899 * Example usage of close_table[][][].
2901 * The table is as follows:
2903 * dy = |row of '@' - row of 'B'| - 1
2904 * dx = |col of '@' - col of 'B'|
2906 * The first indices are the deltas from the source '@' and the block 'B'.
2907 * You must check for the value inside the abs value bars being zero. If
2908 * so then the block is on the same row and you don't need to do a table
2909 * lookup. The last value:
2911 * dcy = |row of block - row to be checked|
2913 * Is the value of the first visible spot on the check row from the
2916 * first visible col = close_table[dy][dx][dcy] + col of 'B'
2918 \*-------------- vision tables --------------*/
2923 Fprintf(ofp
, "\n/* Close */\n");
2925 "#define CLOSE_MAX_SB_DY %2d\t/* |src row - block row| - 1\t*/\n",
2928 "#define CLOSE_MAX_SB_DX %2d\t/* |src col - block col|\t*/\n",
2931 "#define CLOSE_MAX_BC_DY %2d\t/* |block row - check row|\t*/\n",
2933 Fprintf(ofp
, "typedef struct {\n");
2935 " unsigned char close[CLOSE_MAX_SB_DX][CLOSE_MAX_BC_DY];\n");
2936 Fprintf(ofp
, "} close2d;\n");
2937 Fprintf(ofp
, "extern close2d close_table[CLOSE_MAX_SB_DY];\n");
2944 Fprintf(ofp
, "\n/* Far */\n");
2945 Fprintf(ofp
, "#define FAR_MAX_SB_DY %2d\t/* |src row - block row|\t*/\n",
2948 "#define FAR_MAX_SB_DX %2d\t/* |src col - block col| - 1\t*/\n",
2951 "#define FAR_MAX_BC_DY %2d\t/* |block row - check row| - 1\t*/\n",
2953 Fprintf(ofp
, "typedef struct {\n");
2954 Fprintf(ofp
, " unsigned char far_q[FAR_MAX_SB_DX][FAR_MAX_BC_DY];\n");
2955 Fprintf(ofp
, "} far2d;\n");
2956 Fprintf(ofp
, "extern far2d far_table[FAR_MAX_SB_DY];\n");
2964 int src_row
, src_col
; /* source */
2965 int block_row
, block_col
; /* block */
2970 block_row
= BLOCK_HEIGHT
- 1;
2971 block_col
= BLOCK_WIDTH
- 1;
2973 Fprintf(ofp
, "\n#ifndef FAR_TABLE_ONLY\n");
2974 Fprintf(ofp
, "\nclose2d close_table[CLOSE_MAX_SB_DY] = {\n");
2975 #ifndef no_vision_progress
2976 Fprintf(stderr
, "\nclose:");
2979 for (dy
= 1; dy
< TEST_HEIGHT
; dy
++) {
2980 src_row
= block_row
+ dy
;
2981 Fprintf(ofp
, "/* DY = %2d (- 1)*/\n {{\n", dy
);
2982 #ifndef no_vision_progress
2983 Fprintf(stderr
, " %2d", dy
), (void) fflush(stderr
);
2985 for (dx
= 0; dx
< TEST_WIDTH
; dx
++) {
2986 src_col
= block_col
- dx
;
2987 Fprintf(ofp
, " /*%2d*/ {", dx
);
2990 for (this_row
= 0; this_row
< TEST_HEIGHT
; this_row
++) {
2991 delim
= (this_row
< TEST_HEIGHT
- 1) ? "," : "";
2993 Fprintf(ofp
, "%s%s", CLOSE_OFF_TABLE_STRING
, delim
);
2998 /* Find the first column that we can see. */
2999 for (i
= block_col
+ 1; i
< MAX_COL
; i
++) {
3000 if (clear_path(src_row
, src_col
, block_row
- this_row
, i
))
3006 Fprintf(ofp
, "%2d%s", i
- block_col
, delim
);
3008 Fprintf(ofp
, "}%s", (dx
< TEST_WIDTH
- 1) ? ",\n" : "\n");
3010 Fprintf(ofp
, " }},\n");
3013 Fprintf(ofp
, "}; /* close_table[] */\n"); /* closing brace for table */
3014 Fprintf(ofp
, "#endif /* !FAR_TABLE_ONLY */\n");
3015 #ifndef no_vision_progress
3016 Fprintf(stderr
, "\n");
3025 int src_row
, src_col
; /* source */
3026 int block_row
, block_col
; /* block */
3030 block_row
= BLOCK_HEIGHT
- 1;
3031 block_col
= BLOCK_WIDTH
- 1;
3033 Fprintf(ofp
, "\n#ifndef CLOSE_TABLE_ONLY\n");
3034 Fprintf(ofp
, "\nfar2d far_table[FAR_MAX_SB_DY] = {\n");
3035 #ifndef no_vision_progress
3036 Fprintf(stderr
, "\n_far_:");
3039 for (dy
= 0; dy
< TEST_HEIGHT
; dy
++) {
3040 src_row
= block_row
- dy
;
3041 Fprintf(ofp
, "/* DY = %2d */\n {{\n", dy
);
3042 #ifndef no_vision_progress
3043 Fprintf(stderr
, " %2d", dy
), (void) fflush(stderr
);
3045 for (dx
= 1; dx
< TEST_WIDTH
; dx
++) {
3046 src_col
= block_col
+ dx
;
3047 Fprintf(ofp
, " /*%2d(-1)*/ {", dx
);
3049 for (this_row
= block_row
+ 1; this_row
< block_row
+ TEST_HEIGHT
;
3051 delim
= (this_row
< block_row
+ TEST_HEIGHT
- 1) ? "," : "";
3054 /* Find first col that we can see. */
3055 for (i
= 0; i
<= block_col
; i
++) {
3056 if (clear_path(src_row
, src_col
, this_row
, i
))
3060 if (block_col
- i
< 0)
3061 Fprintf(ofp
, "%s%s", FAR_OFF_TABLE_STRING
, delim
);
3063 Fprintf(ofp
, "%2d%s", block_col
- i
, delim
);
3065 Fprintf(ofp
, "}%s", (dx
< TEST_WIDTH
- 1) ? ",\n" : "\n");
3067 Fprintf(ofp
, " }},\n");
3070 Fprintf(ofp
, "}; /* far_table[] */\n"); /* closing brace for table */
3071 Fprintf(ofp
, "#endif /* !CLOSE_TABLE_ONLY */\n");
3072 #ifndef no_vision_progress
3073 Fprintf(stderr
, "\n");
3079 * "Draw" a line from the hero to the given location. Stop if we hit a
3082 * Generalized integer Bresenham's algorithm (fast line drawing) for
3083 * all quadrants. From _Procedural Elements for Computer Graphics_, by
3084 * David F. Rogers. McGraw-Hill, 1985.
3086 * I have tried a little bit of optimization by pulling compares out of
3089 * NOTE: This had better *not* be called from a position on the
3090 * same row as the hero.
3093 clear_path(you_row
, you_col
, y2
, x2
)
3094 int you_row
, you_col
, y2
, x2
;
3097 register int i
, error
, x
, y
, dxs
, dys
;
3101 dx
= abs(x2
- you_col
);
3102 dy
= abs(y2
- you_row
);
3103 s1
= sign(x2
- you_col
);
3104 s2
= sign(y2
- you_row
);
3106 if (s1
== 0) { /* same column */
3107 if (s2
== 1) { /* below (larger y2 value) */
3108 for (i
= you_row
+ 1; i
< y2
; i
++)
3109 if (!xclear
[i
][you_col
])
3111 } else { /* above (smaller y2 value) */
3112 for (i
= y2
+ 1; i
< you_row
; i
++)
3113 if (!xclear
[i
][you_col
])
3120 * Lines at 0 and 90 degrees have been weeded out.
3125 dy
= error
; /* swap the values */
3126 dxs
= dx
<< 1; /* save the shifted values */
3128 error
= dys
- dx
; /* NOTE: error is used as a temporary above */
3130 for (i
= 0; i
< dx
; i
++) {
3132 return 0; /* plot point */
3134 while (error
>= 0) {
3142 dxs
= dx
<< 1; /* save the shifted values */
3146 for (i
= 0; i
< dx
; i
++) {
3148 return 0; /* plot point */
3150 while (error
>= 0) {
3160 #endif /* VISION_TABLES */
3162 #ifdef STRICT_REF_DEF
3163 NEARDATA
struct flag flags
;
3165 struct attribs attrmax
, attrmin
;
3167 #endif /* STRICT_REF_DEF */