3224 lines
88 KiB
C
3224 lines
88 KiB
C
/* NetHack 3.6 makedefs.c $NHDT-Date: 1539804926 2018/10/17 19:35:26 $ $NHDT-Branch: keni-makedefsm $:$NHDT-Revision: 1.126 $ */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/*-Copyright (c) Kenneth Lorber, Kensington, Maryland, 2015. */
|
|
/* Copyright (c) M. Stephenson, 1990, 1991. */
|
|
/* Copyright (c) Dean Luick, 1990. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#define MAKEDEFS_C /* use to conditionally include file sections */
|
|
|
|
#include "config.h"
|
|
#ifdef MONITOR_HEAP
|
|
#undef free /* makedefs doesn't use the alloc and free in src/alloc.c */
|
|
#endif
|
|
#include "permonst.h"
|
|
#include "objclass.h"
|
|
#include "monsym.h"
|
|
#include "artilist.h"
|
|
#include "dungeon.h"
|
|
#include "obj.h"
|
|
#include "monst.h"
|
|
#include "you.h"
|
|
#include "context.h"
|
|
#include "flag.h"
|
|
#include "dlb.h"
|
|
|
|
/* version information */
|
|
#ifdef SHORT_FILENAMES
|
|
#include "patchlev.h"
|
|
#else
|
|
#include "patchlevel.h"
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
#ifdef MAC
|
|
#if defined(__SC__) || defined(__MRC__) /* MPW compilers */
|
|
#define MPWTOOL
|
|
#include <CursorCtl.h>
|
|
#include <string.h>
|
|
#else /* MAC without MPWTOOL */
|
|
#define MACsansMPWTOOL
|
|
#endif
|
|
#endif /* MAC */
|
|
|
|
#ifndef MPWTOOL
|
|
#define SpinCursor(x)
|
|
#endif
|
|
|
|
#define Fprintf (void) fprintf
|
|
#define Fclose (void) fclose
|
|
#define Unlink (void) unlink
|
|
#if !defined(AMIGA) || defined(AZTEC_C)
|
|
#define rewind(fp) fseek((fp), 0L, SEEK_SET) /* guarantee a return value */
|
|
#endif
|
|
|
|
#if defined(UNIX) && !defined(LINT) && !defined(GCC_WARN)
|
|
static const char SCCS_Id[] UNUSED = "@(#)makedefs.c\t3.6\t2018/03/02";
|
|
#endif
|
|
|
|
/* names of files to be generated */
|
|
#define DATE_FILE "date.h"
|
|
#define MONST_FILE "pm.h"
|
|
#define ONAME_FILE "onames.h"
|
|
#ifndef OPTIONS_FILE
|
|
#define OPTIONS_FILE "options"
|
|
#endif
|
|
#define ORACLE_FILE "oracles"
|
|
#define DATA_FILE "data"
|
|
#define RUMOR_FILE "rumors"
|
|
#define DGN_I_FILE "dungeon.def"
|
|
#define DGN_O_FILE "dungeon.pdf"
|
|
#define MON_STR_C "monstr.c"
|
|
#define QTXT_I_FILE "quest.txt"
|
|
#define QTXT_O_FILE "quest.dat"
|
|
#define VIS_TAB_H "vis_tab.h"
|
|
#define VIS_TAB_C "vis_tab.c"
|
|
#define GITINFO_FILE "gitinfo.txt"
|
|
/* locations for those files */
|
|
#ifdef AMIGA
|
|
#define FILE_PREFIX
|
|
#define INCLUDE_TEMPLATE "NH:include/t.%s"
|
|
#define SOURCE_TEMPLATE "NH:src/%s"
|
|
#define DGN_TEMPLATE "NH:dat/%s" /* where dungeon.pdf file goes */
|
|
#define DATA_TEMPLATE "NH:slib/%s"
|
|
#define DATA_IN_TEMPLATE "NH:dat/%s"
|
|
#else /* not AMIGA */
|
|
#if defined(MAC) && !defined(__MACH__)
|
|
/* MacOS 9 or earlier */
|
|
#define INCLUDE_TEMPLATE ":include:%s"
|
|
#define SOURCE_TEMPLATE ":src:%s"
|
|
#define DGN_TEMPLATE ":dat:%s" /* where dungeon.pdf file goes */
|
|
#if __SC__ || __MRC__
|
|
#define DATA_TEMPLATE ":Dungeon:%s"
|
|
#else
|
|
#define DATA_TEMPLATE ":lib:%s"
|
|
#endif /* __SC__ || __MRC__ */
|
|
#define DATA_IN_TEMPLATE ":dat:%s"
|
|
#else /* neither AMIGA nor MAC */
|
|
#ifdef OS2
|
|
#define INCLUDE_TEMPLATE "..\\include\\%s"
|
|
#define SOURCE_TEMPLATE "..\\src\\%s"
|
|
#define DGN_TEMPLATE "..\\dat\\%s" /* where dungeon.pdf file goes */
|
|
#define DATA_TEMPLATE "..\\dat\\%s"
|
|
#define DATA_IN_TEMPLATE "..\\dat\\%s"
|
|
#else /* not AMIGA, MAC, or OS2 */
|
|
#define INCLUDE_TEMPLATE "../include/%s"
|
|
#define SOURCE_TEMPLATE "../src/%s"
|
|
#define DGN_TEMPLATE "../dat/%s" /* where dungeon.pdf file goes */
|
|
#define DATA_TEMPLATE "../dat/%s"
|
|
#define DATA_IN_TEMPLATE "../dat/%s"
|
|
#endif /* else !OS2 */
|
|
#endif /* else !MAC */
|
|
#endif /* else !AMIGA */
|
|
|
|
static const char
|
|
*Dont_Edit_Code =
|
|
"/* This source file is generated by 'makedefs'. Do not edit. */\n",
|
|
*Dont_Edit_Data =
|
|
"#\tThis data file is generated by 'makedefs'. Do not edit. \n";
|
|
|
|
static struct version_info version;
|
|
|
|
/* definitions used for vision tables */
|
|
#define TEST_WIDTH COLNO
|
|
#define TEST_HEIGHT ROWNO
|
|
#define BLOCK_WIDTH (TEST_WIDTH + 10)
|
|
#define BLOCK_HEIGHT TEST_HEIGHT /* don't need extra spaces */
|
|
#define MAX_ROW (BLOCK_HEIGHT + TEST_HEIGHT)
|
|
#define MAX_COL (BLOCK_WIDTH + TEST_WIDTH)
|
|
/* Use this as an out-of-bound value in the close table. */
|
|
#define CLOSE_OFF_TABLE_STRING "99" /* for the close table */
|
|
#define FAR_OFF_TABLE_STRING "0xff" /* for the far table */
|
|
|
|
#define sign(z) ((z) < 0 ? -1 : ((z) ? 1 : 0))
|
|
#ifdef VISION_TABLES
|
|
static char xclear[MAX_ROW][MAX_COL];
|
|
#endif
|
|
/*-end of vision defs-*/
|
|
|
|
#define MAXFNAMELEN 600
|
|
|
|
static char filename[MAXFNAMELEN];
|
|
|
|
#ifdef FILE_PREFIX
|
|
/* if defined, a first argument not starting with - is
|
|
* taken as a text string to be prepended to any
|
|
* output filename generated */
|
|
char *file_prefix = "";
|
|
#endif
|
|
|
|
#ifdef MACsansMPWTOOL
|
|
int FDECL(main, (void));
|
|
#else
|
|
int FDECL(main, (int, char **));
|
|
#endif
|
|
void FDECL(do_makedefs, (char *));
|
|
void NDECL(do_objs);
|
|
void NDECL(do_data);
|
|
void NDECL(do_dungeon);
|
|
void NDECL(do_date);
|
|
void NDECL(do_options);
|
|
void NDECL(do_monstr);
|
|
void NDECL(do_permonst);
|
|
void NDECL(do_questtxt);
|
|
void NDECL(do_rumors);
|
|
void NDECL(do_oracles);
|
|
void NDECL(do_vision);
|
|
|
|
extern void NDECL(monst_globals_init); /* monst.c */
|
|
extern void NDECL(objects_globals_init); /* objects.c */
|
|
|
|
static char *FDECL(name_file, (const char *, const char *));
|
|
static void FDECL(delete_file, (const char *template, const char *));
|
|
static FILE *FDECL(getfp, (const char *, const char *, const char *));
|
|
static void FDECL(do_ext_makedefs, (int, char **));
|
|
|
|
static void NDECL(make_version);
|
|
static char *FDECL(version_string, (char *, const char *));
|
|
static char *FDECL(version_id_string, (char *, const char *));
|
|
static char *FDECL(bannerc_string, (char *, const char *));
|
|
static char *FDECL(xcrypt, (const char *));
|
|
static unsigned long FDECL(read_rumors_file,
|
|
(const char *, int *, long *, unsigned long));
|
|
static boolean FDECL(get_gitinfo, (char *, char *));
|
|
static void FDECL(do_rnd_access_file, (const char *));
|
|
static boolean FDECL(d_filter, (char *));
|
|
static boolean FDECL(h_filter, (char *));
|
|
static void NDECL(build_savebones_compat_string);
|
|
static void NDECL(windowing_sanity);
|
|
|
|
static boolean FDECL(qt_comment, (char *));
|
|
static boolean FDECL(qt_control, (char *));
|
|
static int FDECL(get_hdr, (char *));
|
|
static boolean FDECL(new_id, (char *));
|
|
static boolean FDECL(known_msg, (int, int));
|
|
static void FDECL(new_msg, (char *, int, int));
|
|
static char *FDECL(valid_qt_summary, (char *, BOOLEAN_P));
|
|
static void FDECL(do_qt_control, (char *));
|
|
static void FDECL(do_qt_text, (char *));
|
|
static void NDECL(adjust_qt_hdrs);
|
|
static void NDECL(put_qt_hdrs);
|
|
|
|
#ifdef VISION_TABLES
|
|
static void NDECL(H_close_gen);
|
|
static void NDECL(H_far_gen);
|
|
static void NDECL(C_close_gen);
|
|
static void NDECL(C_far_gen);
|
|
static int FDECL(clear_path, (int, int, int, int));
|
|
#endif
|
|
|
|
static char *FDECL(fgetline, (FILE*));
|
|
static char *FDECL(tmpdup, (const char *));
|
|
static char *FDECL(limit, (char *, int));
|
|
static char *FDECL(eos, (char *));
|
|
static int FDECL(case_insensitive_comp, (const char *, const char *));
|
|
|
|
/* input, output, tmp */
|
|
static FILE *ifp, *ofp, *tfp;
|
|
|
|
#if defined(__BORLANDC__) && !defined(_WIN32)
|
|
extern unsigned _stklen = STKSIZ;
|
|
#endif
|
|
|
|
#ifdef MACsansMPWTOOL
|
|
int
|
|
main(void)
|
|
{
|
|
const char *def_options = "odemvpqrshz";
|
|
char buf[100];
|
|
int len;
|
|
|
|
printf("Enter options to run: [%s] ", def_options);
|
|
fflush(stdout);
|
|
fgets(buf, 100, stdin);
|
|
len = strlen(buf);
|
|
if (len <= 1)
|
|
Strcpy(buf, def_options);
|
|
else
|
|
buf[len - 1] = 0; /* remove return */
|
|
|
|
if (buf[0] == '-' && buf[1] == '-') {
|
|
#if 0
|
|
split up buf into words
|
|
do_ext_makedefs(fakeargc, fakeargv);
|
|
#else
|
|
printf("extended makedefs not implemented for Mac OS9\n");
|
|
exit(EXIT_FAILURE);
|
|
#endif
|
|
}
|
|
|
|
do_makedefs(buf);
|
|
exit(EXIT_SUCCESS);
|
|
return 0;
|
|
}
|
|
|
|
#else /* ! MAC */
|
|
|
|
int
|
|
main(argc, argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
if ((argc == 1) ||
|
|
((argc != 2)
|
|
#ifdef FILE_PREFIX
|
|
&& (argc != 3)
|
|
#endif
|
|
&& !(argv[1][0] == '-' && argv[1][1] == '-'))) {
|
|
Fprintf(stderr, "Bad arg count (%d).\n", argc - 1);
|
|
(void) fflush(stderr);
|
|
return 1;
|
|
}
|
|
|
|
#ifdef FILE_PREFIX
|
|
if (argc >= 2 && argv[1][0] != '-') {
|
|
file_prefix = argv[1];
|
|
argc--;
|
|
argv++;
|
|
}
|
|
#endif
|
|
|
|
if (argv[1][0] == '-' && argv[1][1] == '-') {
|
|
do_ext_makedefs(argc, argv);
|
|
} else {
|
|
do_makedefs(&argv[1][1]);
|
|
}
|
|
exit(EXIT_SUCCESS);
|
|
/*NOTREACHED*/
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
void
|
|
do_makedefs(options)
|
|
char *options;
|
|
{
|
|
boolean more_than_one;
|
|
|
|
objects_globals_init();
|
|
monst_globals_init();
|
|
|
|
/* construct the current version number */
|
|
make_version();
|
|
|
|
more_than_one = strlen(options) > 1;
|
|
while (*options) {
|
|
if (more_than_one)
|
|
Fprintf(stderr, "makedefs -%c\n", *options);
|
|
|
|
switch (*options) {
|
|
case 'o':
|
|
case 'O':
|
|
do_objs();
|
|
break;
|
|
case 'd':
|
|
case 'D':
|
|
do_data();
|
|
break;
|
|
case 'e':
|
|
case 'E':
|
|
do_dungeon();
|
|
break;
|
|
case 'm':
|
|
case 'M':
|
|
do_monstr();
|
|
break;
|
|
case 'v':
|
|
case 'V':
|
|
do_date();
|
|
do_options();
|
|
break;
|
|
case 'p':
|
|
case 'P':
|
|
do_permonst();
|
|
break;
|
|
case 'q':
|
|
case 'Q':
|
|
do_questtxt();
|
|
break;
|
|
case 'r':
|
|
case 'R':
|
|
do_rumors();
|
|
break;
|
|
case 's':
|
|
case 'S':
|
|
do_rnd_access_file(EPITAPHFILE);
|
|
do_rnd_access_file(ENGRAVEFILE);
|
|
do_rnd_access_file(BOGUSMONFILE);
|
|
break;
|
|
case 'h':
|
|
case 'H':
|
|
do_oracles();
|
|
break;
|
|
case 'z':
|
|
case 'Z':
|
|
do_vision();
|
|
break;
|
|
|
|
default:
|
|
Fprintf(stderr, "Unknown option '%c'.\n", *options);
|
|
(void) fflush(stderr);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
options++;
|
|
}
|
|
if (more_than_one)
|
|
Fprintf(stderr, "Completed.\n"); /* feedback */
|
|
}
|
|
|
|
static char namebuf[1000];
|
|
|
|
static char *
|
|
name_file(template, tag)
|
|
const char *template;
|
|
const char *tag;
|
|
{
|
|
Sprintf(namebuf, template, tag);
|
|
return namebuf;
|
|
}
|
|
|
|
static void
|
|
delete_file(template, tag)
|
|
const char *template;
|
|
const char *tag;
|
|
{
|
|
char *name = name_file(template, tag);
|
|
|
|
Unlink(name);
|
|
}
|
|
|
|
static FILE *
|
|
getfp(template, tag, mode)
|
|
const char *template;
|
|
const char *tag;
|
|
const char *mode;
|
|
{
|
|
char *name = name_file(template, tag);
|
|
FILE *rv = fopen(name, mode);
|
|
|
|
if (!rv) {
|
|
Fprintf(stderr, "Can't open '%s'.\n", name);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
static boolean debug = FALSE;
|
|
|
|
static FILE *inputfp;
|
|
static FILE *outputfp;
|
|
|
|
struct grep_var {
|
|
const char *name;
|
|
int is_defined; /* 0 undef; 1 defined */
|
|
};
|
|
/* struct grep_var grep_vars[] and TODO_* constants in include file: */
|
|
#include "mdgrep.h"
|
|
|
|
static void NDECL(do_grep_showvars);
|
|
static struct grep_var *FDECL(grepsearch, (const char *));
|
|
static int FDECL(grep_check_id, (const char *));
|
|
static void FDECL(grep_show_wstack, (const char *));
|
|
static char *FDECL(do_grep_control, (char *));
|
|
static void NDECL(do_grep);
|
|
static void FDECL(grep0, (FILE *, FILE *));
|
|
|
|
static int grep_trace = 0;
|
|
|
|
#define IS_OPTION(str) if (!strcmp(&argv[0][2], str))
|
|
#define CONTINUE \
|
|
argv++, argc--; \
|
|
continue
|
|
#define CONSUME \
|
|
argv++, argc--; \
|
|
if (argc == 0) { \
|
|
Fprintf(stderr, "missing option\n"); \
|
|
exit(EXIT_FAILURE); \
|
|
}
|
|
|
|
static void
|
|
do_ext_makedefs(int argc, char **argv)
|
|
{
|
|
int todo = 0;
|
|
|
|
argc--;
|
|
argv++; /* skip program name */
|
|
|
|
while (argc) {
|
|
if (argv[0][0] != '-')
|
|
break;
|
|
if (argv[0][1] != '-') {
|
|
Fprintf(stderr, "Can't mix - and -- options.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
IS_OPTION("svs") {
|
|
/* short version string for packaging - note no \n */
|
|
char buf[100];
|
|
char delim[10];
|
|
|
|
argv++; /* not CONSUME */
|
|
delim[0] = '\0';
|
|
if (argv[0])
|
|
strcpy(delim, argv[0]);
|
|
Fprintf(stdout, "%s", version_string(buf, delim));
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
IS_OPTION("debug") {
|
|
debug = TRUE;
|
|
CONTINUE;
|
|
}
|
|
IS_OPTION("make") {
|
|
CONSUME;
|
|
do_makedefs(argv[0]);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
IS_OPTION("input") {
|
|
CONSUME;
|
|
if (!strcmp(argv[0], "-")) {
|
|
inputfp = stdin;
|
|
} else {
|
|
inputfp = fopen(argv[0], RDTMODE);
|
|
if (!inputfp) {
|
|
Fprintf(stderr, "Can't open '%s'.\n", argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
CONTINUE;
|
|
}
|
|
IS_OPTION("output") {
|
|
CONSUME;
|
|
if (!strcmp(argv[0], "-")) {
|
|
outputfp = stdout;
|
|
} else {
|
|
outputfp = fopen(argv[0], WRTMODE);
|
|
if (!outputfp) {
|
|
Fprintf(stderr, "Can't open '%s'.\n", argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
CONTINUE;
|
|
}
|
|
IS_OPTION("grep") {
|
|
if (todo) {
|
|
Fprintf(stderr, "Can't do grep and something else.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
todo = TODO_GREP;
|
|
CONTINUE;
|
|
}
|
|
IS_OPTION("grep-showvars") {
|
|
do_grep_showvars();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
IS_OPTION("grep-trace") {
|
|
grep_trace = 1;
|
|
CONTINUE;
|
|
}
|
|
IS_OPTION("grep-define") {
|
|
struct grep_var *p;
|
|
|
|
CONSUME;
|
|
p = grepsearch(argv[0]);
|
|
if (p) {
|
|
p->is_defined = 1;
|
|
} else {
|
|
Fprintf(stderr, "Unknown symbol '%s'\n", argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
CONTINUE;
|
|
}
|
|
IS_OPTION("grep-undef") {
|
|
struct grep_var *p;
|
|
|
|
CONSUME;
|
|
p = grepsearch(argv[0]);
|
|
if (p) {
|
|
p->is_defined = 0;
|
|
} else {
|
|
Fprintf(stderr, "Unknown symbol '%s'\n", argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
CONTINUE;
|
|
}
|
|
#ifdef notyet
|
|
IS_OPTION("help") {
|
|
}
|
|
#endif
|
|
Fprintf(stderr, "Unknown option '%s'.\n", argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (argc) {
|
|
Fprintf(stderr, "unexpected argument '%s'.\n", argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
switch (todo) {
|
|
default:
|
|
Fprintf(stderr, "Confused about what to do?\n");
|
|
exit(EXIT_FAILURE);
|
|
case 0:
|
|
Fprintf(stderr, "Nothing to do?\n");
|
|
exit(EXIT_FAILURE);
|
|
case TODO_GREP:
|
|
do_grep();
|
|
break;
|
|
}
|
|
}
|
|
|
|
#undef IS_OPTION
|
|
#undef CONTINUE
|
|
#undef CONSUME
|
|
|
|
/*
|
|
* Filtering syntax:
|
|
* Any line NOT starting with a caret is either suppressed or passed
|
|
* through unchanged depending on the current conditional state.
|
|
*
|
|
* The default conditional state is printing on.
|
|
*
|
|
* Conditionals may be nested.
|
|
*
|
|
* makedefs will exit with a EXIT_FAILURE if any errors are detected;
|
|
* as many errors as possible are detected before giving up.
|
|
*
|
|
* Unknown identifiers are treated as TRUE and also as an error to
|
|
* allow processing to continue past the unknown identifier (note
|
|
* that "#undef" is different than unknown).
|
|
*
|
|
* Any line starting with a caret is a control line; as in C, zero or
|
|
* more spaces may be embedded in the line almost anywhere; the caret
|
|
* MUST be in column 1.
|
|
* (XXX for the moment, no white space is allowed after the caret because
|
|
* existing lines in the docs look like that.)
|
|
*
|
|
* Control lines:
|
|
* ^^ a line starting with a (single) literal caret
|
|
* ^# a comment - the line is ignored
|
|
* ^?ID if defined(ID)
|
|
* ^!ID if !defined(ID)
|
|
* ^: else
|
|
* ^. endif
|
|
*/
|
|
#define GREP_MAGIC '^'
|
|
#define GREP_STACK_SIZE 100
|
|
#ifdef notyet
|
|
static int grep_rewrite = 0; /* need to (possibly) rewrite lines */
|
|
#endif
|
|
static int grep_writing = 1; /* need to copy lines to output */
|
|
static int grep_errors = 0;
|
|
static int grep_sp = 0;
|
|
#define ST_LD(old, opp) (((old) ? 1 : 0) | ((opp) ? 2 : 0))
|
|
#define ST_OLD(v) (((v) & 1) != 0)
|
|
#define ST_OPP(v) (((v) & 2) != 0)
|
|
#define ST_ELSE 4
|
|
static int grep_stack[GREP_STACK_SIZE] = { ST_LD(1, 0) };
|
|
static int grep_lineno = 0;
|
|
|
|
static void
|
|
do_grep_showvars()
|
|
{
|
|
int x;
|
|
|
|
for (x = 0; x < SIZE(grep_vars) - 1; x++) {
|
|
printf("%d\t%s\n", grep_vars[x].is_defined, grep_vars[x].name);
|
|
}
|
|
}
|
|
|
|
static struct grep_var *
|
|
grepsearch(name)
|
|
const char *name;
|
|
{
|
|
/* XXX make into binary search */
|
|
int x = 0;
|
|
|
|
while (x < SIZE(grep_vars) - 1) {
|
|
if (!strcmp(grep_vars[x].name, name))
|
|
return &grep_vars[x];
|
|
x++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
grep_check_id(id)
|
|
const char *id;
|
|
{
|
|
struct grep_var *rv;
|
|
|
|
while (*id && isspace((uchar) *id))
|
|
id++;
|
|
if (!*id) {
|
|
Fprintf(stderr, "missing identifier in line %d", grep_lineno);
|
|
grep_errors++;
|
|
return 0;
|
|
}
|
|
rv = grepsearch(id);
|
|
if (rv) {
|
|
if (grep_trace) {
|
|
Fprintf(outputfp, "ID %d %s\n", rv->is_defined, id);
|
|
}
|
|
return rv->is_defined;
|
|
}
|
|
|
|
if (grep_trace) {
|
|
Fprintf(outputfp, "ID U %s\n", id);
|
|
}
|
|
Fprintf(stderr, "unknown identifier '%s' in line %d.\n", id, grep_lineno);
|
|
grep_errors++;
|
|
return 2; /* So new features can be checked before makedefs
|
|
* is rebuilt. */
|
|
}
|
|
|
|
static void
|
|
grep_show_wstack(tag)
|
|
const char *tag;
|
|
{
|
|
int x;
|
|
|
|
if (!grep_trace)
|
|
return;
|
|
|
|
Fprintf(outputfp, "%s w=%d sp=%d\t", tag, grep_writing, grep_sp);
|
|
for (x = grep_sp; x >= 0 && x > grep_sp - 6; x--) {
|
|
Fprintf(outputfp, "[%d]=%d ", x, grep_stack[x]);
|
|
}
|
|
Fprintf(outputfp, "\n");
|
|
}
|
|
|
|
static char *
|
|
do_grep_control(buf)
|
|
char *buf;
|
|
{
|
|
int isif = 1;
|
|
char *buf0 = buf;
|
|
#if 1
|
|
if (isspace((uchar) buf[0]))
|
|
return &buf[-1]; /* XXX see docs above */
|
|
#else
|
|
while (buf[0] && isspace((uchar) buf[0]))
|
|
buf++;
|
|
#endif
|
|
switch (buf[0]) {
|
|
case '#': /* comment */
|
|
break;
|
|
case '.': /* end of if level */
|
|
if (grep_sp == 0) {
|
|
Fprintf(stderr, "unmatched ^. (endif) at line %d.\n",
|
|
grep_lineno);
|
|
grep_errors++;
|
|
} else {
|
|
grep_writing = ST_OLD(grep_stack[grep_sp--]);
|
|
grep_show_wstack("pop");
|
|
}
|
|
break;
|
|
case '!': /* if not ID */
|
|
isif = 0;
|
|
/* FALLTHROUGH */
|
|
case '?': /* if ID */
|
|
if (grep_sp == GREP_STACK_SIZE - 2) {
|
|
Fprintf(stderr, "stack overflow at line %d.", grep_lineno);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (grep_writing) {
|
|
isif = grep_check_id(&buf[1]) ? isif : !isif;
|
|
grep_stack[++grep_sp] = ST_LD(grep_writing, !isif);
|
|
grep_writing = isif;
|
|
} else {
|
|
grep_stack[++grep_sp] = ST_LD(0, 0);
|
|
/* grep_writing = 0; */
|
|
}
|
|
grep_show_wstack("push");
|
|
break;
|
|
case ':': /* else */
|
|
if (ST_ELSE & grep_stack[grep_sp]) {
|
|
Fprintf(stderr, "multiple : for same conditional at line %d.\n",
|
|
grep_lineno);
|
|
grep_errors++;
|
|
}
|
|
grep_writing = ST_OPP(grep_stack[grep_sp]);
|
|
grep_stack[grep_sp] |= ST_ELSE;
|
|
break;
|
|
#if defined(notyet)
|
|
case '(': /* start of expression */
|
|
#endif
|
|
case GREP_MAGIC: /* ^^ -> ^ */
|
|
return buf0;
|
|
default: {
|
|
char str[10];
|
|
|
|
if (isprint((uchar) buf[0])) {
|
|
str[0] = buf[0];
|
|
str[1] = '\0';
|
|
} else {
|
|
sprintf(str, "0x%02x", buf[0]);
|
|
}
|
|
Fprintf(stderr, "unknown control ^%s at line %d.\n", str,
|
|
grep_lineno);
|
|
grep_errors++;
|
|
} break;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef notyet
|
|
static void
|
|
do_grep_rewrite(buf)
|
|
char *buf;
|
|
{
|
|
/* no language features use this yet */
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
static void grep0(FILE *, FILE *);
|
|
|
|
static void
|
|
do_grep()
|
|
{
|
|
if (!inputfp) {
|
|
Fprintf(stderr, "--grep requires --input\n");
|
|
}
|
|
if (!outputfp) {
|
|
Fprintf(stderr, "--grep requires --output\n");
|
|
}
|
|
if (!inputfp || !outputfp) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
grep0(inputfp, outputfp);
|
|
}
|
|
|
|
static void
|
|
grep0(inputfp0, outputfp0)
|
|
FILE *inputfp0;
|
|
FILE *outputfp0;
|
|
{
|
|
char buf[16384]; /* looong, just in case */
|
|
|
|
while (!feof(inputfp0) && !ferror(inputfp0)) {
|
|
char *tmp;
|
|
char *buf1;
|
|
|
|
if (fgets(buf, sizeof(buf), inputfp0) == 0)
|
|
break;
|
|
if ((tmp = strchr(buf, '\n')))
|
|
*tmp = '\0';
|
|
grep_lineno++;
|
|
if (grep_trace) {
|
|
Fprintf(outputfp0, "%04d %c >%s\n", grep_lineno,
|
|
grep_writing ? ' ' : '#', buf);
|
|
}
|
|
|
|
if (buf[0] == GREP_MAGIC) {
|
|
buf1 = do_grep_control(&buf[1]);
|
|
if (!buf1)
|
|
continue;
|
|
} else {
|
|
buf1 = buf;
|
|
}
|
|
#ifdef notyet
|
|
if (grep_rewrite)
|
|
do_grep_rewrite(buf1);
|
|
#endif
|
|
if (grep_writing)
|
|
Fprintf(outputfp0, "%s\n", buf1);
|
|
}
|
|
if (ferror(inputfp0)) {
|
|
Fprintf(stderr, "read error!\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (ferror(outputfp0)) {
|
|
Fprintf(stderr, "write error!\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
fclose(inputfp0);
|
|
fclose(outputfp0);
|
|
if (grep_sp) {
|
|
Fprintf(stderr, "%d unterminated conditional level%s\n", grep_sp,
|
|
grep_sp == 1 ? "" : "s");
|
|
grep_errors++;
|
|
}
|
|
if (grep_errors) {
|
|
Fprintf(stderr, "%d error%s detected.\n", grep_errors,
|
|
grep_errors == 1 ? "" : "s");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* trivial text encryption routine which can't be broken with `tr' */
|
|
static char *
|
|
xcrypt(str)
|
|
const char *str;
|
|
{ /* duplicated in src/hacklib.c */
|
|
static char buf[BUFSZ];
|
|
register const char *p;
|
|
register char *q;
|
|
register int bitmask;
|
|
|
|
for (bitmask = 1, p = str, q = buf; *p; q++) {
|
|
*q = *p++;
|
|
if (*q & (32 | 64))
|
|
*q ^= bitmask;
|
|
if ((bitmask <<= 1) >= 32)
|
|
bitmask = 1;
|
|
}
|
|
*q = '\0';
|
|
return buf;
|
|
}
|
|
|
|
#define PAD_RUMORS_TO 60
|
|
/* common code for do_rumors(). Return 0 on error. */
|
|
static unsigned long
|
|
read_rumors_file(file_ext, rumor_count, rumor_size, old_rumor_offset)
|
|
const char *file_ext;
|
|
int *rumor_count;
|
|
long *rumor_size;
|
|
unsigned long old_rumor_offset;
|
|
{
|
|
char infile[MAXFNAMELEN];
|
|
char *line;
|
|
unsigned long rumor_offset;
|
|
|
|
Sprintf(infile, DATA_IN_TEMPLATE, RUMOR_FILE);
|
|
Strcat(infile, file_ext);
|
|
if (!(ifp = fopen(infile, RDTMODE))) {
|
|
perror(infile);
|
|
return 0L;
|
|
}
|
|
|
|
/* copy the rumors */
|
|
while ((line = fgetline(ifp)) != 0) {
|
|
#ifdef PAD_RUMORS_TO
|
|
/* rumor selection is accomplished by seeking to a random
|
|
position in the file, advancing to newline, and taking
|
|
the next line; therefore, rumors which follow long-line
|
|
rumors are most likely to be chosen and rumors which
|
|
follow short-line rumors are least likely to be chosen;
|
|
we ameliorate the latter by padding the shortest lines,
|
|
increasing the chance of the random seek landing in them */
|
|
int len = (int) strlen(line);
|
|
|
|
if (len <= PAD_RUMORS_TO) {
|
|
char *base = index(line, '\n');
|
|
/* this is only safe because fgetline() overallocates */
|
|
while (len++ < PAD_RUMORS_TO) {
|
|
*base++ = '_';
|
|
}
|
|
*base++ = '\n';
|
|
*base = '\0';
|
|
}
|
|
#endif
|
|
(*rumor_count)++;
|
|
#if 0
|
|
/*[if we forced binary output, this would be sufficient]*/
|
|
*rumor_size += strlen(line); /* includes newline */
|
|
#endif
|
|
(void) fputs(xcrypt(line), tfp);
|
|
free(line);
|
|
}
|
|
/* record the current position; next rumors section will start here */
|
|
rumor_offset = (unsigned long) ftell(tfp);
|
|
Fclose(ifp); /* all done with rumors.file_ext */
|
|
|
|
/* the calculated value for *_rumor_count assumes that
|
|
a single-byte line terminator is in use; for platforms
|
|
which use two byte CR+LF, we need to override that value
|
|
[it's much simpler to do so unconditionally, rendering
|
|
the loop's accumulation above obsolete] */
|
|
*rumor_size = (long) (rumor_offset - old_rumor_offset);
|
|
return rumor_offset;
|
|
}
|
|
|
|
void
|
|
do_rnd_access_file(fname)
|
|
const char *fname;
|
|
{
|
|
char *line;
|
|
|
|
Sprintf(filename, DATA_IN_TEMPLATE, fname);
|
|
Strcat(filename, ".txt");
|
|
if (!(ifp = fopen(filename, RDTMODE))) {
|
|
perror(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
filename[0] = '\0';
|
|
#ifdef FILE_PREFIX
|
|
Strcat(filename, file_prefix);
|
|
#endif
|
|
Sprintf(eos(filename), DATA_TEMPLATE, fname);
|
|
if (!(ofp = fopen(filename, WRTMODE))) {
|
|
perror(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
Fprintf(ofp, "%s", Dont_Edit_Data);
|
|
|
|
tfp = getfp(DATA_TEMPLATE, "grep.tmp", WRTMODE);
|
|
grep0(ifp, tfp);
|
|
ifp = getfp(DATA_TEMPLATE, "grep.tmp", RDTMODE);
|
|
|
|
while ((line = fgetline(ifp)) != 0) {
|
|
if (line[0] != '#' && line[0] != '\n')
|
|
(void) fputs(xcrypt(line), ofp);
|
|
free(line);
|
|
}
|
|
Fclose(ifp);
|
|
Fclose(ofp);
|
|
|
|
delete_file(DATA_TEMPLATE, "grep.tmp");
|
|
return;
|
|
}
|
|
|
|
void
|
|
do_rumors()
|
|
{
|
|
char *line;
|
|
static const char rumors_header[] =
|
|
"%s%04d,%06ld,%06lx;%04d,%06ld,%06lx;0,0,%06lx\n";
|
|
char tempfile[MAXFNAMELEN];
|
|
int true_rumor_count, false_rumor_count;
|
|
long true_rumor_size, false_rumor_size;
|
|
unsigned long true_rumor_offset, false_rumor_offset, eof_offset;
|
|
|
|
Sprintf(tempfile, DATA_TEMPLATE, "rumors.tmp");
|
|
filename[0] = '\0';
|
|
#ifdef FILE_PREFIX
|
|
Strcat(filename, file_prefix);
|
|
#endif
|
|
Sprintf(eos(filename), DATA_TEMPLATE, RUMOR_FILE);
|
|
if (!(ofp = fopen(filename, WRTMODE))) {
|
|
perror(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (!(tfp = fopen(tempfile, WRTMODE))) {
|
|
perror(tempfile);
|
|
Fclose(ofp);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
true_rumor_count = false_rumor_count = 0;
|
|
true_rumor_size = false_rumor_size = 0L;
|
|
true_rumor_offset = false_rumor_offset = eof_offset = 0L;
|
|
|
|
/* output a dummy header record; we'll replace it in final output */
|
|
Fprintf(tfp, rumors_header, Dont_Edit_Data, true_rumor_count,
|
|
true_rumor_size, true_rumor_offset, false_rumor_count,
|
|
false_rumor_size, false_rumor_offset, eof_offset);
|
|
/* record the current position; true rumors will start here */
|
|
true_rumor_offset = ftell(tfp);
|
|
|
|
false_rumor_offset = read_rumors_file(
|
|
".tru", &true_rumor_count, &true_rumor_size, true_rumor_offset);
|
|
if (!false_rumor_offset)
|
|
goto rumors_failure;
|
|
|
|
eof_offset = read_rumors_file(".fal", &false_rumor_count,
|
|
&false_rumor_size, false_rumor_offset);
|
|
if (!eof_offset)
|
|
goto rumors_failure;
|
|
|
|
/* get ready to transfer the contents of temp file to output file */
|
|
line = malloc(BUFSZ + MAXFNAMELEN);
|
|
Sprintf(line, "rewind of \"%s\"", tempfile);
|
|
if (rewind(tfp) != 0) {
|
|
perror(line);
|
|
free(line);
|
|
goto rumors_failure;
|
|
}
|
|
free(line);
|
|
|
|
/* output the header record */
|
|
Fprintf(ofp, rumors_header, Dont_Edit_Data, true_rumor_count,
|
|
true_rumor_size, true_rumor_offset, false_rumor_count,
|
|
false_rumor_size, false_rumor_offset, eof_offset);
|
|
/* skip the temp file's dummy header */
|
|
if (!(line = fgetline(tfp))) { /* "Don't Edit" */
|
|
perror(tempfile);
|
|
goto rumors_failure;
|
|
}
|
|
free(line);
|
|
if (!(line = fgetline(tfp))) { /* count,size,offset */
|
|
perror(tempfile);
|
|
goto rumors_failure;
|
|
}
|
|
free(line);
|
|
/* copy the rest of the temp file into the final output file */
|
|
while ((line = fgetline(tfp)) != 0) {
|
|
(void) fputs(line, ofp);
|
|
free(line);
|
|
}
|
|
/* all done; delete temp file */
|
|
Fclose(tfp);
|
|
Unlink(tempfile);
|
|
Fclose(ofp);
|
|
return;
|
|
|
|
rumors_failure:
|
|
Fclose(ofp);
|
|
Unlink(filename); /* kill empty or incomplete output file */
|
|
Fclose(tfp);
|
|
Unlink(tempfile); /* and temporary file */
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/*
|
|
* Use this to explicitly mask out features during version checks.
|
|
*
|
|
* ZEROCOMP, RLECOMP, and ZLIB_COMP describe compression features
|
|
* that the port/plaform which wrote the savefile was capable of
|
|
* dealing with. Don't reject a savefile just because the port
|
|
* reading the savefile doesn't match on all/some of them.
|
|
* The actual compression features used to produce the savefile are
|
|
* recorded in the savefile_info structure immediately following the
|
|
* version_info, and that is what needs to be checked against the
|
|
* feature set of the port that is reading the savefile back in.
|
|
* That check is done in src/restore.c now.
|
|
*
|
|
*/
|
|
#define IGNORED_FEATURES \
|
|
(0L | (1L << 19) /* SCORE_ON_BOTL */ \
|
|
| (1L << 27) /* ZEROCOMP */ \
|
|
| (1L << 28) /* RLECOMP */ \
|
|
)
|
|
|
|
static void
|
|
make_version()
|
|
{
|
|
register int i;
|
|
|
|
/*
|
|
* integer version number
|
|
*/
|
|
version.incarnation = ((unsigned long) VERSION_MAJOR << 24)
|
|
| ((unsigned long) VERSION_MINOR << 16)
|
|
| ((unsigned long) PATCHLEVEL << 8)
|
|
| ((unsigned long) EDITLEVEL);
|
|
/*
|
|
* encoded feature list
|
|
* Note: if any of these magic numbers are changed or reassigned,
|
|
* EDITLEVEL in patchlevel.h should be incremented at the same time.
|
|
* The actual values have no special meaning, and the category
|
|
* groupings are just for convenience.
|
|
*/
|
|
version.feature_set = (unsigned long) (0L
|
|
/* levels and/or topology (0..4) */
|
|
/* monsters (5..9) */
|
|
#ifdef MAIL
|
|
| (1L << 6)
|
|
#endif
|
|
/* objects (10..14) */
|
|
/* flag bits and/or other global variables (15..26) */
|
|
#ifdef TEXTCOLOR
|
|
| (1L << 17)
|
|
#endif
|
|
#ifdef INSURANCE
|
|
| (1L << 18)
|
|
#endif
|
|
#ifdef SCORE_ON_BOTL
|
|
| (1L << 19)
|
|
#endif
|
|
/* data format (27..31)
|
|
* External compression methods such as COMPRESS and ZLIB_COMP
|
|
* do not affect the contents and are thus excluded from here */
|
|
#ifdef ZEROCOMP
|
|
| (1L << 27)
|
|
#endif
|
|
#ifdef RLECOMP
|
|
| (1L << 28)
|
|
#endif
|
|
);
|
|
/*
|
|
* Value used for object & monster sanity check.
|
|
* (NROFARTIFACTS<<24) | (NUM_OBJECTS<<12) | (NUMMONS<<0)
|
|
*/
|
|
for (i = 1; artifact_names[i]; i++)
|
|
continue;
|
|
version.entity_count = (unsigned long) (i - 1);
|
|
for (i = 1; objects[i].oc_class != ILLOBJ_CLASS; i++)
|
|
continue;
|
|
version.entity_count = (version.entity_count << 12) | (unsigned long) i;
|
|
for (i = 0; mons[i].mlet; i++)
|
|
continue;
|
|
version.entity_count = (version.entity_count << 12) | (unsigned long) i;
|
|
/*
|
|
* Value used for compiler (word size/field alignment/padding) check.
|
|
*/
|
|
version.struct_sizes1 =
|
|
(((unsigned long) sizeof(struct context_info) << 24)
|
|
| ((unsigned long) sizeof(struct obj) << 17)
|
|
| ((unsigned long) sizeof(struct monst) << 10)
|
|
| ((unsigned long) sizeof(struct you)));
|
|
version.struct_sizes2 = (((unsigned long) sizeof(struct flag) << 10) |
|
|
/* free bits in here */
|
|
#ifdef SYSFLAGS
|
|
((unsigned long) sizeof(struct sysflag)));
|
|
#else
|
|
((unsigned long) 0L));
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
/* REPRODUCIBLE_BUILD will change this to TRUE */
|
|
static boolean date_via_env = FALSE;
|
|
|
|
static char *
|
|
version_string(outbuf, delim)
|
|
char *outbuf;
|
|
const char *delim;
|
|
{
|
|
Sprintf(outbuf, "%d%s%d%s%d", VERSION_MAJOR, delim, VERSION_MINOR, delim,
|
|
PATCHLEVEL);
|
|
#ifdef BETA
|
|
Sprintf(eos(outbuf), "-%d", EDITLEVEL);
|
|
#endif
|
|
return outbuf;
|
|
}
|
|
|
|
static char *
|
|
version_id_string(outbuf, build_date)
|
|
char *outbuf;
|
|
const char *build_date;
|
|
{
|
|
char subbuf[64], versbuf[64];
|
|
char betabuf[64];
|
|
|
|
#ifdef BETA
|
|
Strcpy(betabuf, " Beta");
|
|
#else
|
|
betabuf[0] = '\0';
|
|
#endif
|
|
|
|
subbuf[0] = '\0';
|
|
#ifdef PORT_SUB_ID
|
|
subbuf[0] = ' ';
|
|
Strcpy(&subbuf[1], PORT_SUB_ID);
|
|
#endif
|
|
|
|
Sprintf(outbuf, "%s NetHack%s Version %s%s - last %s %s.", PORT_ID,
|
|
subbuf, version_string(versbuf, "."), betabuf,
|
|
date_via_env ? "revision" : "build", build_date);
|
|
return outbuf;
|
|
}
|
|
|
|
static char *
|
|
bannerc_string(outbuf, build_date)
|
|
char *outbuf;
|
|
const char *build_date;
|
|
{
|
|
char subbuf[64], versbuf[64];
|
|
|
|
subbuf[0] = '\0';
|
|
#ifdef PORT_SUB_ID
|
|
subbuf[0] = ' ';
|
|
Strcpy(&subbuf[1], PORT_SUB_ID);
|
|
#endif
|
|
#ifdef BETA
|
|
Strcat(subbuf, " Beta");
|
|
#endif
|
|
|
|
Sprintf(outbuf, " Version %s %s%s, %s %s.",
|
|
version_string(versbuf, "."), PORT_ID, subbuf,
|
|
date_via_env ? "revised" : "built", &build_date[4]);
|
|
#if 0
|
|
Sprintf(outbuf, "%s NetHack%s %s Copyright 1985-%s (built %s)",
|
|
PORT_ID, subbuf, version_string(versbuf,"."), RELEASE_YEAR,
|
|
&build_date[4]);
|
|
#endif
|
|
return outbuf;
|
|
}
|
|
|
|
void
|
|
do_date()
|
|
{
|
|
#ifdef KR1ED
|
|
long clocktim = 0;
|
|
#else
|
|
time_t clocktim = 0;
|
|
#endif
|
|
char githash[BUFSZ], gitbranch[BUFSZ];
|
|
char *c, cbuf[60], buf[BUFSZ];
|
|
const char *ul_sfx;
|
|
|
|
/* before creating date.h, make sure that xxx_GRAPHICS and
|
|
DEFAULT_WINDOW_SYS have been set up in a viable fashion */
|
|
windowing_sanity();
|
|
|
|
filename[0] = '\0';
|
|
#ifdef FILE_PREFIX
|
|
Strcat(filename, file_prefix);
|
|
#endif
|
|
Sprintf(eos(filename), INCLUDE_TEMPLATE, DATE_FILE);
|
|
if (!(ofp = fopen(filename, WRTMODE))) {
|
|
perror(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
/* NB: We've moved on from SCCS, but this way this line
|
|
* won't get clobbered when downstream projects import
|
|
* this file into something more modern. */
|
|
Fprintf(ofp, "%s", Dont_Edit_Code);
|
|
|
|
(void) time(&clocktim);
|
|
#ifdef REPRODUCIBLE_BUILD
|
|
{
|
|
/*
|
|
* Use date+time of latest source file revision (set up in
|
|
* our environment rather than derived by scanning sources)
|
|
* instead of current date+time, so that later rebuilds of
|
|
* the same sources specifying the same configuration will
|
|
* produce the same result.
|
|
*
|
|
* Changing the configuration should be done by modifying
|
|
* config.h or <port>conf.h and setting SOURCE_DATE_EPOCH
|
|
* based on whichever changed most recently, not by using
|
|
* make CFLAGS='-Dthis -Dthat'
|
|
* to make alterations on the fly.
|
|
*
|
|
* Limited validation is performed to prevent dates in the
|
|
* future (beyond a leeway of 24 hours) or distant past.
|
|
*
|
|
* Assumes the value of time_t is in seconds, which is
|
|
* fundamental for Unix and mandated by POSIX. For any ports
|
|
* where that isn't true, leaving REPRODUCIBLE_BUILD disabled
|
|
* is probably preferrable to hacking this code....
|
|
*/
|
|
static struct tm nh360; /* static init should yield UTC timezone */
|
|
unsigned long sd_num, sd_earliest, sd_latest;
|
|
const char *sd_str = getenv("SOURCE_DATE_EPOCH");
|
|
|
|
if (sd_str) {
|
|
sd_num = strtoul(sd_str, (char **) 0, 10);
|
|
/*
|
|
* Note: this does not need to be updated for future
|
|
* releases. It serves as a sanity check for potentially
|
|
* mis-set environment, not a hard baseline for when the
|
|
* current version could have first been built.
|
|
*/
|
|
/* oldest date we'll accept: 7-Dec-2015 (release of 3.6.0) */
|
|
nh360.tm_mday = 7;
|
|
nh360.tm_mon = 12 - 1;
|
|
nh360.tm_year = 2015 - 1900;
|
|
sd_earliest = (unsigned long) mktime(&nh360);
|
|
/* 'youngest' date we'll accept: 24 hours in the future */
|
|
sd_latest = (unsigned long) clocktim + 24L * 60L * 60L;
|
|
|
|
if (sd_num >= sd_earliest && sd_num <= sd_latest) {
|
|
/* use SOURCE_DATE_EPOCH value */
|
|
clocktim = (time_t) sd_num;
|
|
date_via_env = TRUE;
|
|
} else {
|
|
Fprintf(stderr, "? Invalid value for SOURCE_DATE_EPOCH (%lu)",
|
|
sd_num);
|
|
if (sd_num > 0L && sd_num < sd_earliest)
|
|
Fprintf(stderr, ", older than %lu", sd_earliest);
|
|
else if (sd_num > sd_latest)
|
|
Fprintf(stderr, ", newer than %lu", sd_latest);
|
|
Fprintf(stderr, ".\n");
|
|
Fprintf(stderr, ": Reverting to current date+time (%lu).\n",
|
|
(unsigned long) clocktim);
|
|
(void) fflush(stderr);
|
|
}
|
|
} else {
|
|
/* REPRODUCIBLE_BUILD enabled but SOURCE_DATE_EPOCH is missing */
|
|
Fprintf(stderr, "? No value for SOURCE_DATE_EPOCH.\n");
|
|
Fprintf(stderr, ": Using current date+time (%lu).\n",
|
|
(unsigned long) clocktim);
|
|
(void) fflush(stderr);
|
|
}
|
|
Strcpy(cbuf, asctime(gmtime(&clocktim)));
|
|
}
|
|
#else
|
|
/* ordinary build: use current date+time */
|
|
Strcpy(cbuf, ctime(&clocktim));
|
|
#endif
|
|
|
|
if ((c = index(cbuf, '\n')) != 0)
|
|
*c = '\0'; /* strip off the '\n' */
|
|
#ifdef NHSTDC
|
|
ul_sfx = "UL";
|
|
#else
|
|
ul_sfx = "L";
|
|
#endif
|
|
if (date_via_env)
|
|
Fprintf(ofp, "#define SOURCE_DATE_EPOCH (%lu%s) /* via getenv() */\n",
|
|
(unsigned long) clocktim, ul_sfx);
|
|
Fprintf(ofp, "#define BUILD_DATE \"%s\"\n", cbuf);
|
|
if (date_via_env)
|
|
Fprintf(ofp, "#define BUILD_TIME SOURCE_DATE_EPOCH\n");
|
|
else
|
|
Fprintf(ofp, "#define BUILD_TIME (%lu%s)\n",
|
|
(unsigned long) clocktim, ul_sfx);
|
|
Fprintf(ofp, "\n");
|
|
Fprintf(ofp, "#define VERSION_NUMBER 0x%08lx%s\n", version.incarnation,
|
|
ul_sfx);
|
|
Fprintf(ofp, "#define VERSION_FEATURES 0x%08lx%s\n", version.feature_set,
|
|
ul_sfx);
|
|
#ifdef IGNORED_FEATURES
|
|
Fprintf(ofp, "#define IGNORED_FEATURES 0x%08lx%s\n",
|
|
(unsigned long) IGNORED_FEATURES, ul_sfx);
|
|
#endif
|
|
Fprintf(ofp, "#define VERSION_SANITY1 0x%08lx%s\n", version.entity_count,
|
|
ul_sfx);
|
|
Fprintf(ofp, "#define VERSION_SANITY2 0x%08lx%s\n", version.struct_sizes1,
|
|
ul_sfx);
|
|
Fprintf(ofp, "#define VERSION_SANITY3 0x%08lx%s\n", version.struct_sizes2,
|
|
ul_sfx);
|
|
Fprintf(ofp, "\n");
|
|
Fprintf(ofp, "#define VERSION_STRING \"%s\"\n", version_string(buf, "."));
|
|
Fprintf(ofp, "#define VERSION_ID \\\n \"%s\"\n",
|
|
version_id_string(buf, cbuf));
|
|
Fprintf(ofp, "#define COPYRIGHT_BANNER_C \\\n \"%s\"\n",
|
|
bannerc_string(buf, cbuf));
|
|
Fprintf(ofp, "\n");
|
|
if (get_gitinfo(githash, gitbranch)) {
|
|
Fprintf(ofp, "#define NETHACK_GIT_SHA \"%s\"\n", githash);
|
|
Fprintf(ofp, "#define NETHACK_GIT_BRANCH \"%s\"\n", gitbranch);
|
|
}
|
|
#ifdef AMIGA
|
|
{
|
|
struct tm *tm = localtime((time_t *) &clocktim);
|
|
|
|
Fprintf(ofp, "#define AMIGA_VERSION_STRING ");
|
|
Fprintf(ofp, "\"\\0$VER: NetHack %d.%d.%d (%d.%d.%d)\"\n",
|
|
VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL,
|
|
tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900);
|
|
}
|
|
#endif
|
|
Fclose(ofp);
|
|
return;
|
|
}
|
|
|
|
boolean
|
|
get_gitinfo(githash, gitbranch)
|
|
char *githash, *gitbranch;
|
|
{
|
|
FILE *gifp;
|
|
size_t len;
|
|
char infile[MAXFNAMELEN];
|
|
char *line, *strval, *opt, *c, *end;
|
|
boolean havebranch = FALSE, havehash = FALSE;
|
|
|
|
if (!githash || !gitbranch) return FALSE;
|
|
|
|
Sprintf(infile, DATA_IN_TEMPLATE, GITINFO_FILE);
|
|
if (!(gifp = fopen(infile, RDTMODE))) {
|
|
/* perror(infile); */
|
|
return FALSE;
|
|
}
|
|
|
|
/* read the gitinfo file */
|
|
while ((line = fgetline(gifp)) != 0) {
|
|
strval = index(line, '=');
|
|
if (strval && strlen(strval) < (BUFSZ-1)) {
|
|
opt = line;
|
|
*strval++ = '\0';
|
|
/* strip off the '\n' */
|
|
if ((c = index(strval, '\n')) != 0)
|
|
*c = '\0';
|
|
if ((c = index(opt, '\n')) != 0)
|
|
*c = '\0';
|
|
/* strip leading and trailing white space */
|
|
while (*strval == ' ' || *strval == '\t')
|
|
strval++;
|
|
end = eos(strval);
|
|
while (--end >= strval && (*end == ' ' || *end == '\t'))
|
|
*end = '\0';
|
|
while (*opt == ' ' || *opt == '\t')
|
|
opt++;
|
|
end = eos(opt);
|
|
while (--end >= opt && (*end == ' ' || *end == '\t'))
|
|
*end = '\0';
|
|
|
|
len = strlen(opt);
|
|
if ((len >= strlen("gitbranch")) && !case_insensitive_comp(opt, "gitbranch")) {
|
|
Strcpy(gitbranch, strval);
|
|
havebranch = TRUE;
|
|
}
|
|
if ((len >= strlen("githash")) && !case_insensitive_comp(opt, "githash")) {
|
|
Strcpy(githash, strval);
|
|
havehash = TRUE;
|
|
}
|
|
}
|
|
free(line);
|
|
}
|
|
Fclose(gifp);
|
|
if (havebranch && havehash)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
static int
|
|
case_insensitive_comp(s1, s2)
|
|
const char *s1;
|
|
const char *s2;
|
|
{
|
|
uchar u1, u2;
|
|
|
|
for (;; s1++, s2++) {
|
|
u1 = (uchar) *s1;
|
|
if (isupper(u1))
|
|
u1 = tolower(u1);
|
|
u2 = (uchar) *s2;
|
|
if (isupper(u2))
|
|
u2 = tolower(u2);
|
|
if (u1 == '\0' || u1 != u2)
|
|
break;
|
|
}
|
|
return u1 - u2;
|
|
}
|
|
|
|
static char save_bones_compat_buf[BUFSZ];
|
|
|
|
static void
|
|
build_savebones_compat_string()
|
|
{
|
|
#ifdef VERSION_COMPATIBILITY
|
|
unsigned long uver = VERSION_COMPATIBILITY;
|
|
#endif
|
|
Strcpy(save_bones_compat_buf,
|
|
"save and bones files accepted from version");
|
|
#ifdef VERSION_COMPATIBILITY
|
|
Sprintf(eos(save_bones_compat_buf), "s %lu.%lu.%lu through %d.%d.%d",
|
|
((uver & 0xFF000000L) >> 24), ((uver & 0x00FF0000L) >> 16),
|
|
((uver & 0x0000FF00L) >> 8), VERSION_MAJOR, VERSION_MINOR,
|
|
PATCHLEVEL);
|
|
#else
|
|
Sprintf(eos(save_bones_compat_buf), " %d.%d.%d only", VERSION_MAJOR,
|
|
VERSION_MINOR, PATCHLEVEL);
|
|
#endif
|
|
}
|
|
|
|
static const char *build_opts[] = {
|
|
#ifdef AMIGA_WBENCH
|
|
"Amiga WorkBench support",
|
|
#endif
|
|
#ifdef ANSI_DEFAULT
|
|
"ANSI default terminal",
|
|
#endif
|
|
#ifdef TEXTCOLOR
|
|
"color",
|
|
#endif
|
|
#ifdef TTY_TILES_ESCCODES
|
|
"console escape codes for tile hinting",
|
|
#endif
|
|
#ifdef COM_COMPL
|
|
"command line completion",
|
|
#endif
|
|
#ifdef LIFE
|
|
"Conway's Game of Life",
|
|
#endif
|
|
#ifdef COMPRESS
|
|
"data file compression",
|
|
#endif
|
|
#ifdef ZLIB_COMP
|
|
"ZLIB data file compression",
|
|
#endif
|
|
#ifdef DLB
|
|
"data librarian",
|
|
#endif
|
|
#ifdef DUMPLOG
|
|
"end-of-game dumplogs",
|
|
#endif
|
|
#ifdef HOLD_LOCKFILE_OPEN
|
|
"exclusive lock on level 0 file",
|
|
#endif
|
|
#if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
|
|
"external program as a message handler",
|
|
#endif
|
|
#ifdef MFLOPPY
|
|
"floppy drive support",
|
|
#endif
|
|
#ifdef INSURANCE
|
|
"insurance files for recovering from crashes",
|
|
#endif
|
|
#ifdef LOGFILE
|
|
"log file",
|
|
#endif
|
|
#ifdef XLOGFILE
|
|
"extended log file",
|
|
#endif
|
|
#ifdef PANICLOG
|
|
"errors and warnings log file",
|
|
#endif
|
|
#ifdef MAIL
|
|
"mail daemon",
|
|
#endif
|
|
#ifdef GNUDOS
|
|
"MSDOS protected mode",
|
|
#endif
|
|
#ifdef NEWS
|
|
"news file",
|
|
#endif
|
|
#ifdef OVERLAY
|
|
#ifdef MOVERLAY
|
|
"MOVE overlays",
|
|
#else
|
|
#ifdef VROOMM
|
|
"VROOMM overlays",
|
|
#else
|
|
"overlays",
|
|
#endif
|
|
#endif
|
|
#endif
|
|
/* pattern matching method will be substituted by nethack at run time */
|
|
"pattern matching via :PATMATCH:",
|
|
#ifdef USE_ISAAC64
|
|
"pseudo random numbers generated by ISAAC64",
|
|
#ifdef DEV_RANDOM
|
|
#ifdef __STDC__
|
|
/* include which specific one */
|
|
"strong PRNG seed available from " DEV_RANDOM,
|
|
#else
|
|
"strong PRNG seed available from DEV_RANDOM",
|
|
#endif
|
|
#else
|
|
#ifdef WIN32
|
|
"strong PRNG seed available from CNG BCryptGenRandom()",
|
|
#endif
|
|
#endif /* DEV_RANDOM */
|
|
#else /* ISAAC64 */
|
|
#ifdef RANDOM
|
|
"pseudo random numbers generated by random()",
|
|
#else
|
|
"pseudo random numbers generated by C rand()",
|
|
#endif
|
|
#endif
|
|
#ifdef SELECTSAVED
|
|
"restore saved games via menu",
|
|
#endif
|
|
#ifdef SCORE_ON_BOTL
|
|
"score on status line",
|
|
#endif
|
|
#ifdef CLIPPING
|
|
"screen clipping",
|
|
#endif
|
|
#ifdef NO_TERMS
|
|
#ifdef MAC
|
|
"screen control via mactty",
|
|
#endif
|
|
#ifdef SCREEN_BIOS
|
|
"screen control via BIOS",
|
|
#endif
|
|
#ifdef SCREEN_DJGPPFAST
|
|
"screen control via DJGPP fast",
|
|
#endif
|
|
#ifdef SCREEN_VGA
|
|
"screen control via VGA graphics",
|
|
#endif
|
|
#ifdef WIN32CON
|
|
"screen control via WIN32 console I/O",
|
|
#endif
|
|
#endif
|
|
#ifdef SHELL
|
|
"shell command",
|
|
#endif
|
|
"traditional status display",
|
|
#ifdef STATUS_HILITES
|
|
"status via windowport with highlighting",
|
|
#else
|
|
"status via windowport without highlighting",
|
|
#endif
|
|
#ifdef SUSPEND
|
|
"suspend command",
|
|
#endif
|
|
#ifdef TERMINFO
|
|
"terminal info library",
|
|
#else
|
|
#if defined(TERMLIB) \
|
|
|| ((!defined(MICRO) && !defined(WIN32)) && defined(TTY_GRAPHICS))
|
|
"terminal capability library",
|
|
#endif
|
|
#endif
|
|
#ifdef USE_XPM
|
|
"tile_file in XPM format",
|
|
#endif
|
|
#ifdef GRAPHIC_TOMBSTONE
|
|
"graphical RIP screen",
|
|
#endif
|
|
#ifdef TIMED_DELAY
|
|
"timed wait for display effects",
|
|
#endif
|
|
#ifdef USER_SOUNDS
|
|
"user sounds",
|
|
#endif
|
|
#ifdef PREFIXES_IN_USE
|
|
"variable playground",
|
|
#endif
|
|
#ifdef VISION_TABLES
|
|
"vision tables",
|
|
#endif
|
|
#ifdef ZEROCOMP
|
|
"zero-compressed save files",
|
|
#endif
|
|
#ifdef RLECOMP
|
|
"run-length compression of map in save files",
|
|
#endif
|
|
#ifdef SYSCF
|
|
"system configuration at run-time",
|
|
#endif
|
|
save_bones_compat_buf,
|
|
"and basic NetHack features"
|
|
};
|
|
|
|
struct win_info {
|
|
const char *id, /* DEFAULT_WINDOW_SYS string */
|
|
*name; /* description, often same as id */
|
|
};
|
|
static struct win_info window_opts[] = {
|
|
#ifdef TTY_GRAPHICS
|
|
{ "tty", "traditional tty-based graphics" },
|
|
#endif
|
|
#ifdef CURSES_GRAPHICS
|
|
{ "curses", "terminal-based graphics using curses libraries" },
|
|
#endif
|
|
#ifdef X11_GRAPHICS
|
|
{ "X11", "X11" },
|
|
#endif
|
|
#ifdef QT_GRAPHICS
|
|
{ "Qt", "Qt" },
|
|
#endif
|
|
#ifdef GNOME_GRAPHICS
|
|
{ "Gnome", "Gnome" },
|
|
#endif
|
|
#ifdef MAC
|
|
{ "mac", "Mac" },
|
|
#endif
|
|
#ifdef AMIGA_INTUITION
|
|
{ "amii", "Amiga Intuition" },
|
|
#endif
|
|
#ifdef GEM_GRAPHICS
|
|
{ "Gem", "Gem" },
|
|
#endif
|
|
#ifdef MSWIN_GRAPHICS
|
|
{ "mswin", "mswin" },
|
|
#endif
|
|
#ifdef BEOS_GRAPHICS
|
|
{ "BeOS", "BeOS InterfaceKit" },
|
|
#endif
|
|
{ 0, 0 }
|
|
};
|
|
|
|
static void
|
|
windowing_sanity()
|
|
{
|
|
#ifndef DEFAULT_WINDOW_SYS
|
|
/* pre-standard compilers didn't support #error; wait til run-time */
|
|
Fprintf(stderr,
|
|
"Configuration error: DEFAULT_WINDOW_SYS is not defined.\n");
|
|
exit(EXIT_FAILURE);
|
|
/*NOTREACHED*/
|
|
|
|
/* put in a dummy value so that do_options() will compile and makedefs
|
|
will build, otherwise the message above won't ever get delivered */
|
|
#define DEFAULT_WINDOW_SYS "<undefined>"
|
|
#else /*DEFAULT_WINDOW_SYS*/
|
|
|
|
if (!window_opts[0].id) {
|
|
Fprintf(stderr, "Configuration error: no windowing systems "
|
|
"(TTY_GRAPHICS, &c) enabled.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; window_opts[i].id; ++i)
|
|
if (!strcmp(window_opts[i].id, DEFAULT_WINDOW_SYS))
|
|
break;
|
|
if (!window_opts[i].id) { /* went through whole list without a match */
|
|
Fprintf(stderr, "Configuration error: DEFAULT_WINDOW_SYS (%s)\n",
|
|
DEFAULT_WINDOW_SYS);
|
|
Fprintf(stderr,
|
|
" does not match any enabled windowing system (%s%s).\n",
|
|
window_opts[0].id, window_opts[1].id ? ", &c" : "");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
#endif /*DEFAULT_WINDOW_SYS*/
|
|
}
|
|
|
|
void
|
|
do_options()
|
|
{
|
|
static const char indent[] = " ";
|
|
const char *str, *sep;
|
|
char *word, buf[BUFSZ];
|
|
int i, length, winsyscnt;
|
|
|
|
windowing_sanity();
|
|
|
|
filename[0] = '\0';
|
|
#ifdef FILE_PREFIX
|
|
Strcat(filename, file_prefix);
|
|
#endif
|
|
Sprintf(eos(filename), DATA_TEMPLATE, OPTIONS_FILE);
|
|
if (!(ofp = fopen(filename, WRTMODE))) {
|
|
perror(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
build_savebones_compat_string();
|
|
Fprintf(ofp,
|
|
#ifdef BETA
|
|
"\n NetHack version %d.%d.%d [beta]\n",
|
|
#else
|
|
"\n NetHack version %d.%d.%d\n",
|
|
#endif
|
|
VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL);
|
|
|
|
Fprintf(ofp, "\nOptions compiled into this edition:\n");
|
|
length = COLNO + 1; /* force 1st item onto new line */
|
|
for (i = 0; i < SIZE(build_opts); i++) {
|
|
str = strcat(strcpy(buf, build_opts[i]),
|
|
(i < SIZE(build_opts) - 1) ? "," : ".");
|
|
while (*str) {
|
|
word = index(str, ' ');
|
|
if (word)
|
|
*word = '\0';
|
|
if (length + strlen(str) > COLNO - 5)
|
|
Fprintf(ofp, "\n%s", indent), length = strlen(indent);
|
|
else
|
|
Fprintf(ofp, " "), length++;
|
|
Fprintf(ofp, "%s", str), length += strlen(str);
|
|
str += strlen(str) + (word ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
winsyscnt = SIZE(window_opts) - 1;
|
|
Fprintf(ofp, "\n\nSupported windowing system%s:\n",
|
|
(winsyscnt > 1) ? "s" : "");
|
|
length = COLNO + 1; /* force 1st item onto new line */
|
|
for (i = 0; i < winsyscnt; i++) {
|
|
str = window_opts[i].name;
|
|
if (length + strlen(str) > COLNO - 5)
|
|
Fprintf(ofp, "\n%s", indent), length = strlen(indent);
|
|
else
|
|
Fprintf(ofp, " "), length++;
|
|
Fprintf(ofp, "%s", str), length += strlen(str);
|
|
sep = (winsyscnt == 1)
|
|
? "."
|
|
: (winsyscnt == 2)
|
|
? ((i == 0) ? " and" : "")
|
|
: (i < winsyscnt - 2)
|
|
? ","
|
|
: ((i == winsyscnt - 2) ? ", and" : "");
|
|
Fprintf(ofp, "%s", sep), length += strlen(sep);
|
|
}
|
|
if (winsyscnt > 1)
|
|
Fprintf(ofp, "\n%swith a default of %s.", indent, DEFAULT_WINDOW_SYS);
|
|
Fprintf(ofp, "\n\n");
|
|
|
|
Fclose(ofp);
|
|
return;
|
|
}
|
|
|
|
/* routine to decide whether to discard something from data.base */
|
|
static boolean
|
|
d_filter(line)
|
|
char *line;
|
|
{
|
|
if (*line == '#')
|
|
return TRUE; /* ignore comment lines */
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
*
|
|
New format (v3.1) of 'data' file which allows much faster lookups [pr]
|
|
"do not edit" first record is a comment line
|
|
01234567 hexadecimal formatted offset to text area
|
|
name-a first name of interest
|
|
123,4 offset to name's text, and number of lines for it
|
|
name-b next name of interest
|
|
name-c multiple names which share same description also
|
|
456,7 share a single offset,count line
|
|
. sentinel to mark end of names
|
|
789,0 dummy record containing offset, count of EOF
|
|
text-a 4 lines of descriptive text for name-a
|
|
text-a at file position 0x01234567L + 123L
|
|
text-a
|
|
text-a
|
|
text-b/text-c 7 lines of text for names-b and -c
|
|
text-b/text-c at fseek(0x01234567L + 456L)
|
|
...
|
|
*
|
|
*/
|
|
|
|
void
|
|
do_data()
|
|
{
|
|
char infile[60], tempfile[60];
|
|
boolean ok;
|
|
long txt_offset;
|
|
int entry_cnt, line_cnt;
|
|
char *line;
|
|
|
|
Sprintf(tempfile, DATA_TEMPLATE, "database.tmp");
|
|
filename[0] = '\0';
|
|
#ifdef FILE_PREFIX
|
|
Strcat(filename, file_prefix);
|
|
#endif
|
|
Sprintf(eos(filename), DATA_TEMPLATE, DATA_FILE);
|
|
Sprintf(infile, DATA_IN_TEMPLATE, DATA_FILE);
|
|
#ifdef SHORT_FILENAMES
|
|
Strcat(infile, ".bas");
|
|
#else
|
|
Strcat(infile, ".base");
|
|
#endif
|
|
if (!(ifp = fopen(infile, RDTMODE))) { /* data.base */
|
|
perror(infile);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (!(ofp = fopen(filename, WRTMODE))) { /* data */
|
|
perror(filename);
|
|
Fclose(ifp);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (!(tfp = fopen(tempfile, WRTMODE))) { /* database.tmp */
|
|
perror(tempfile);
|
|
Fclose(ifp);
|
|
Fclose(ofp);
|
|
Unlink(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* output a dummy header record; we'll rewind and overwrite it later */
|
|
Fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, 0L);
|
|
|
|
entry_cnt = line_cnt = 0;
|
|
/* read through the input file and split it into two sections */
|
|
while ((line = fgetline(ifp)) != 0) {
|
|
if (d_filter(line)) {
|
|
free(line);
|
|
continue;
|
|
}
|
|
if (*line > ' ') { /* got an entry name */
|
|
/* first finish previous entry */
|
|
if (line_cnt)
|
|
Fprintf(ofp, "%d\n", line_cnt), line_cnt = 0;
|
|
/* output the entry name */
|
|
(void) fputs(line, ofp);
|
|
entry_cnt++; /* update number of entries */
|
|
} else if (entry_cnt) { /* got some descriptive text */
|
|
/* update previous entry with current text offset */
|
|
if (!line_cnt)
|
|
Fprintf(ofp, "%ld,", ftell(tfp));
|
|
/* save the text line in the scratch file */
|
|
(void) fputs(line, tfp);
|
|
line_cnt++; /* update line counter */
|
|
}
|
|
free(line);
|
|
}
|
|
/* output an end marker and then record the current position */
|
|
if (line_cnt)
|
|
Fprintf(ofp, "%d\n", line_cnt);
|
|
Fprintf(ofp, ".\n%ld,%d\n", ftell(tfp), 0);
|
|
txt_offset = ftell(ofp);
|
|
Fclose(ifp); /* all done with original input file */
|
|
|
|
/* reprocess the scratch file; 1st format an error msg, just in case */
|
|
line = malloc(BUFSZ + MAXFNAMELEN);
|
|
Sprintf(line, "rewind of \"%s\"", tempfile);
|
|
if (rewind(tfp) != 0)
|
|
goto dead_data;
|
|
free(line);
|
|
/* copy all lines of text from the scratch file into the output file */
|
|
while ((line = fgetline(tfp)) != 0) {
|
|
(void) fputs(line, ofp);
|
|
free(line);
|
|
}
|
|
|
|
/* finished with scratch file */
|
|
Fclose(tfp);
|
|
Unlink(tempfile); /* remove it */
|
|
|
|
/* update the first record of the output file; prepare error msg 1st */
|
|
line = malloc(BUFSZ + MAXFNAMELEN);
|
|
Sprintf(line, "rewind of \"%s\"", filename);
|
|
ok = (rewind(ofp) == 0);
|
|
if (ok) {
|
|
Sprintf(line, "header rewrite of \"%s\"", filename);
|
|
ok = (fprintf(ofp, "%s%08lx\n", Dont_Edit_Data,
|
|
(unsigned long) txt_offset) >= 0);
|
|
}
|
|
if (!ok) {
|
|
dead_data:
|
|
perror(line); /* report the problem */
|
|
free(line);
|
|
/* close and kill the aborted output file, then give up */
|
|
Fclose(ofp);
|
|
Unlink(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
free(line);
|
|
|
|
/* all done */
|
|
Fclose(ofp);
|
|
|
|
return;
|
|
}
|
|
|
|
/* routine to decide whether to discard something from oracles.txt */
|
|
static boolean
|
|
h_filter(line)
|
|
char *line;
|
|
{
|
|
static boolean skip = FALSE;
|
|
char *tag;
|
|
|
|
SpinCursor(3);
|
|
|
|
if (*line == '#')
|
|
return TRUE; /* ignore comment lines */
|
|
|
|
tag = malloc(strlen(line));
|
|
if (sscanf(line, "----- %s", tag) == 1) {
|
|
skip = FALSE;
|
|
} else if (skip && !strncmp(line, "-----", 5))
|
|
skip = FALSE;
|
|
free(tag);
|
|
return skip;
|
|
}
|
|
|
|
static const char *special_oracle[] = {
|
|
"\"...it is rather disconcerting to be confronted with the",
|
|
"following theorem from [Baker, Gill, and Solovay, 1975].", "",
|
|
"Theorem 7.18 There exist recursive languages A and B such that",
|
|
" (1) P(A) == NP(A), and", " (2) P(B) != NP(B)", "",
|
|
"This provides impressive evidence that the techniques that are",
|
|
"currently available will not suffice for proving that P != NP or "
|
|
" ",
|
|
"that P == NP.\" [Garey and Johnson, p. 185.]"
|
|
};
|
|
|
|
/*
|
|
The oracle file consists of a "do not edit" comment, a decimal count N
|
|
and set of N+1 hexadecimal fseek offsets, followed by N multiple-line
|
|
records, separated by "---" lines. The first oracle is a special case.
|
|
The input data contains just those multi-line records, separated by
|
|
"-----" lines.
|
|
*/
|
|
|
|
void
|
|
do_oracles()
|
|
{
|
|
char infile[60], tempfile[60];
|
|
boolean in_oracle, ok;
|
|
long fpos;
|
|
unsigned long txt_offset, offset;
|
|
int oracle_cnt;
|
|
register int i;
|
|
char *line;
|
|
|
|
Sprintf(tempfile, DATA_TEMPLATE, "oracles.tmp");
|
|
filename[0] = '\0';
|
|
#ifdef FILE_PREFIX
|
|
Strcat(filename, file_prefix);
|
|
#endif
|
|
Sprintf(eos(filename), DATA_TEMPLATE, ORACLE_FILE);
|
|
Sprintf(infile, DATA_IN_TEMPLATE, ORACLE_FILE);
|
|
Strcat(infile, ".txt");
|
|
if (!(ifp = fopen(infile, RDTMODE))) {
|
|
perror(infile);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (!(ofp = fopen(filename, WRTMODE))) {
|
|
perror(filename);
|
|
Fclose(ifp);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (!(tfp = fopen(tempfile, WRTMODE))) { /* oracles.tmp */
|
|
perror(tempfile);
|
|
Fclose(ifp);
|
|
Fclose(ofp);
|
|
Unlink(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* output a dummy header record; we'll rewind and overwrite it later */
|
|
Fprintf(ofp, "%s%5d\n", Dont_Edit_Data, 0);
|
|
|
|
/* handle special oracle; it must come first */
|
|
(void) fputs("---\n", tfp);
|
|
offset = (unsigned long) ftell(tfp);
|
|
Fprintf(ofp, "%05lx\n", offset); /* start pos of special oracle */
|
|
for (i = 0; i < SIZE(special_oracle); i++) {
|
|
(void) fputs(xcrypt(special_oracle[i]), tfp);
|
|
(void) fputc('\n', tfp);
|
|
}
|
|
SpinCursor(3);
|
|
|
|
oracle_cnt = 1;
|
|
(void) fputs("---\n", tfp);
|
|
offset = (unsigned long) ftell(tfp);
|
|
Fprintf(ofp, "%05lx\n", offset); /* start pos of first oracle */
|
|
in_oracle = FALSE;
|
|
|
|
while ((line = fgetline(ifp)) != 0) {
|
|
SpinCursor(3);
|
|
|
|
if (h_filter(line)) {
|
|
free(line);
|
|
continue;
|
|
}
|
|
if (!strncmp(line, "-----", 5)) {
|
|
if (!in_oracle) {
|
|
free(line);
|
|
continue;
|
|
}
|
|
in_oracle = FALSE;
|
|
oracle_cnt++;
|
|
(void) fputs("---\n", tfp);
|
|
offset = (unsigned long) ftell(tfp);
|
|
Fprintf(ofp, "%05lx\n", offset); /* start pos of this oracle */
|
|
} else {
|
|
in_oracle = TRUE;
|
|
(void) fputs(xcrypt(line), tfp);
|
|
}
|
|
free(line);
|
|
}
|
|
|
|
if (in_oracle) { /* need to terminate last oracle */
|
|
oracle_cnt++;
|
|
(void) fputs("---\n", tfp);
|
|
offset = (unsigned long) ftell(tfp);
|
|
Fprintf(ofp, "%05lx\n", offset); /* eof position */
|
|
}
|
|
|
|
/* record the current position */
|
|
txt_offset = (unsigned long) ftell(ofp);
|
|
Fclose(ifp); /* all done with original input file */
|
|
|
|
/* reprocess the scratch file; 1st format an error msg, just in case */
|
|
line = malloc(BUFSZ + MAXFNAMELEN);
|
|
Sprintf(line, "rewind of \"%s\"", tempfile);
|
|
if (rewind(tfp) != 0)
|
|
goto dead_data;
|
|
free(line);
|
|
/* copy all lines of text from the scratch file into the output file */
|
|
while ((line = fgetline(tfp)) != 0) {
|
|
(void) fputs(line, ofp);
|
|
free(line);
|
|
}
|
|
|
|
/* finished with scratch file */
|
|
Fclose(tfp);
|
|
Unlink(tempfile); /* remove it */
|
|
|
|
/* update the first record of the output file; prepare error msg 1st */
|
|
line = malloc(BUFSZ + MAXFNAMELEN);
|
|
Sprintf(line, "rewind of \"%s\"", filename);
|
|
ok = (rewind(ofp) == 0);
|
|
if (ok) {
|
|
Sprintf(line, "header rewrite of \"%s\"", filename);
|
|
ok = (fprintf(ofp, "%s%5d\n", Dont_Edit_Data, oracle_cnt) >= 0);
|
|
}
|
|
if (ok) {
|
|
Sprintf(line, "data rewrite of \"%s\"", filename);
|
|
for (i = 0; i <= oracle_cnt; i++) {
|
|
#ifndef VMS /* alpha/vms v1.0; this fflush seems to confuse ftell */
|
|
if (!(ok = (fflush(ofp) == 0)))
|
|
break;
|
|
#endif
|
|
if (!(ok = (fpos = ftell(ofp)) >= 0))
|
|
break;
|
|
if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0)))
|
|
break;
|
|
if (!(ok = (fscanf(ofp, "%5lx", &offset) == 1)))
|
|
break;
|
|
#ifdef MAC
|
|
#ifdef __MWERKS__
|
|
/*
|
|
MetroWerks CodeWarrior Pro 1's (AKA CW12) version of MSL
|
|
(ANSI C Libraries) needs this rewind or else the fprintf
|
|
stops working. This may also be true for CW11, but has
|
|
never been checked.
|
|
*/
|
|
rewind(ofp);
|
|
#endif
|
|
#endif
|
|
if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0)))
|
|
break;
|
|
offset += txt_offset;
|
|
if (!(ok = (fprintf(ofp, "%05lx\n", offset) >= 0)))
|
|
break;
|
|
}
|
|
}
|
|
if (!ok) {
|
|
dead_data:
|
|
perror(line); /* report the problem */
|
|
free(line);
|
|
/* close and kill the aborted output file, then give up */
|
|
Fclose(ofp);
|
|
Unlink(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
free(line);
|
|
|
|
/* all done */
|
|
Fclose(ofp);
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
do_dungeon()
|
|
{
|
|
char *line;
|
|
|
|
Sprintf(filename, DATA_IN_TEMPLATE, DGN_I_FILE);
|
|
if (!(ifp = fopen(filename, RDTMODE))) {
|
|
perror(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
filename[0] = '\0';
|
|
#ifdef FILE_PREFIX
|
|
Strcat(filename, file_prefix);
|
|
#endif
|
|
Sprintf(eos(filename), DGN_TEMPLATE, DGN_O_FILE);
|
|
if (!(ofp = fopen(filename, WRTMODE))) {
|
|
perror(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
Fprintf(ofp, "%s", Dont_Edit_Data);
|
|
|
|
tfp = getfp(DATA_TEMPLATE, "grep.tmp", WRTMODE);
|
|
grep0(ifp, tfp);
|
|
ifp = getfp(DATA_TEMPLATE, "grep.tmp", RDTMODE);
|
|
|
|
while ((line = fgetline(ifp)) != 0) {
|
|
SpinCursor(3);
|
|
|
|
if (line[0] == '#') {
|
|
free(line);
|
|
continue; /* discard comments */
|
|
}
|
|
(void) fputs(line, ofp);
|
|
free(line);
|
|
}
|
|
Fclose(ifp);
|
|
Fclose(ofp);
|
|
|
|
delete_file(DATA_TEMPLATE, "grep.tmp");
|
|
return;
|
|
}
|
|
|
|
void
|
|
do_monstr()
|
|
{
|
|
/* Don't break anything for ports that haven't been updated. */
|
|
printf("DEPRECATION WARNINGS:\n");
|
|
printf("'makedefs -m' is deprecated. Remove all references\n");
|
|
printf(" to it from the build process.\n");
|
|
printf("'monstr.c' is deprecated. Remove all references to\n");
|
|
printf(" it from the build process.\n");
|
|
printf("monstr[] is deprecated. Replace monstr[x] with\n");
|
|
printf(" mons[x].difficulty\n");
|
|
printf("monstr_init() is deprecated. Remove all references to it.\n");
|
|
|
|
/*
|
|
* create the source file, "monstr.c"
|
|
*/
|
|
filename[0] = '\0';
|
|
#ifdef FILE_PREFIX
|
|
Strcat(filename, file_prefix);
|
|
#endif
|
|
Sprintf(eos(filename), SOURCE_TEMPLATE, MON_STR_C);
|
|
if (!(ofp = fopen(filename, WRTMODE))) {
|
|
perror(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
Fprintf(ofp, "%s", Dont_Edit_Code);
|
|
Fprintf(ofp, "#include \"config.h\"\n");
|
|
Fprintf(ofp, "\nconst int monstrXXX[] = {\n");
|
|
Fprintf(ofp, "0};\n");
|
|
Fprintf(ofp, "/*\n");
|
|
Fprintf(ofp, "DEPRECATION WARNINGS:\n");
|
|
Fprintf(ofp, "'makedefs -m' is deprecated. Remove all references\n");
|
|
Fprintf(ofp, " to it from the build process.\n");
|
|
Fprintf(ofp, "'monstr.c' is deprecated. Remove all references to\n");
|
|
Fprintf(ofp, " it from the build process.\n");
|
|
Fprintf(ofp, "monstr[] is deprecated. Replace monstr[x] with\n");
|
|
Fprintf(ofp, " mons[x].difficulty\n");
|
|
Fprintf(ofp, "monstr_init() is deprecated. Remove all references to it.\n");
|
|
Fprintf(ofp, "*/\n");
|
|
|
|
Fprintf(ofp, "\nvoid NDECL(monstr_init);\n");
|
|
Fprintf(ofp, "\nvoid\n");
|
|
Fprintf(ofp, "monstr_init()\n");
|
|
Fprintf(ofp, "{\n");
|
|
Fprintf(ofp, " return;\n");
|
|
Fprintf(ofp, "}\n");
|
|
Fprintf(ofp, "\n/*monstr.c*/\n");
|
|
|
|
Fclose(ofp);
|
|
return;
|
|
}
|
|
|
|
void
|
|
do_permonst()
|
|
{
|
|
int i;
|
|
char *c, *nam;
|
|
|
|
filename[0] = '\0';
|
|
#ifdef FILE_PREFIX
|
|
Strcat(filename, file_prefix);
|
|
#endif
|
|
Sprintf(eos(filename), INCLUDE_TEMPLATE, MONST_FILE);
|
|
if (!(ofp = fopen(filename, WRTMODE))) {
|
|
perror(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
Fprintf(ofp, "%s", Dont_Edit_Code);
|
|
Fprintf(ofp, "#ifndef PM_H\n#define PM_H\n");
|
|
|
|
for (i = 0; mons[i].mlet; i++) {
|
|
SpinCursor(3);
|
|
|
|
Fprintf(ofp, "\n#define\tPM_");
|
|
if (mons[i].mlet == S_HUMAN && !strncmp(mons[i].mname, "were", 4))
|
|
Fprintf(ofp, "HUMAN_");
|
|
for (nam = c = tmpdup(mons[i].mname); *c; c++)
|
|
if (*c >= 'a' && *c <= 'z')
|
|
*c -= (char) ('a' - 'A');
|
|
else if (*c < 'A' || *c > 'Z')
|
|
*c = '_';
|
|
Fprintf(ofp, "%s\t%d", nam, i);
|
|
}
|
|
Fprintf(ofp, "\n\n#define\tNUMMONS\t%d\n", i);
|
|
Fprintf(ofp, "\n#endif /* PM_H */\n");
|
|
Fclose(ofp);
|
|
return;
|
|
}
|
|
|
|
/* Start of Quest text file processing. */
|
|
#include "qtext.h"
|
|
|
|
static struct qthdr qt_hdr;
|
|
static struct msghdr msg_hdr[N_HDR];
|
|
static struct qtmsg *curr_msg;
|
|
|
|
static int qt_line;
|
|
|
|
static boolean in_msg;
|
|
#define NO_MSG 1 /* strlen of a null line returned by fgets() */
|
|
|
|
static boolean
|
|
qt_comment(s)
|
|
char *s;
|
|
{
|
|
if (s[0] == '#')
|
|
return TRUE;
|
|
return (boolean) (!in_msg && strlen(s) == NO_MSG);
|
|
}
|
|
|
|
static boolean
|
|
qt_control(s)
|
|
char *s;
|
|
{
|
|
return (boolean) (s[0] == '%' && (s[1] == 'C' || s[1] == 'E'));
|
|
}
|
|
|
|
static int
|
|
get_hdr(code)
|
|
char *code;
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < qt_hdr.n_hdr; i++)
|
|
if (!strncmp(code, qt_hdr.id[i], LEN_HDR))
|
|
return ++i;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static boolean
|
|
new_id(code)
|
|
char *code;
|
|
{
|
|
if (qt_hdr.n_hdr >= N_HDR) {
|
|
Fprintf(stderr, OUT_OF_HEADERS, qt_line);
|
|
return FALSE;
|
|
}
|
|
|
|
strncpy(&qt_hdr.id[qt_hdr.n_hdr][0], code, LEN_HDR);
|
|
msg_hdr[qt_hdr.n_hdr].n_msg = 0;
|
|
qt_hdr.offset[qt_hdr.n_hdr++] = 0L;
|
|
return TRUE;
|
|
}
|
|
|
|
static boolean
|
|
known_msg(num, id)
|
|
int num, id;
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < msg_hdr[num].n_msg; i++)
|
|
if (msg_hdr[num].qt_msg[i].msgnum == id)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
new_msg(s, num, id)
|
|
char *s;
|
|
int num, id;
|
|
{
|
|
struct qtmsg *qt_msg;
|
|
|
|
if (msg_hdr[num].n_msg >= N_MSG) {
|
|
Fprintf(stderr, OUT_OF_MESSAGES, qt_line);
|
|
} else {
|
|
qt_msg = &(msg_hdr[num].qt_msg[msg_hdr[num].n_msg++]);
|
|
qt_msg->msgnum = id;
|
|
qt_msg->delivery = s[2];
|
|
qt_msg->offset = qt_msg->size = qt_msg->summary_size = 0L;
|
|
|
|
curr_msg = qt_msg;
|
|
}
|
|
}
|
|
|
|
/* check %E record for "[summary text]" that nethack can stuff into the
|
|
message history buffer when delivering text via window instead of pline */
|
|
static char *
|
|
valid_qt_summary(s, parsing)
|
|
char *s; /* end record: "%E" optionally followed by " [summary]" */
|
|
boolean parsing; /* curr_msg is valid iff this is True */
|
|
{
|
|
static char summary[BUFSZ];
|
|
char *p;
|
|
|
|
if (*s != '%' || *(s + 1) != 'E')
|
|
return (char *) 0;
|
|
if ((p = index(s, '[')) == 0)
|
|
return (char *) 0;
|
|
/* note: opening '[' and closing ']' will be retained in the output;
|
|
anything after ']' will be discarded by putting a newline there */
|
|
Strcpy(summary, p);
|
|
|
|
/* have an opening bracket; summary[] holds it and all text that follows
|
|
*/
|
|
p = eos(summary);
|
|
/* find closing bracket */
|
|
while (p > summary && *(p - 1) != ']')
|
|
--p;
|
|
|
|
if (p == summary) {
|
|
/* we backed up all the way to the start without finding a bracket */
|
|
if (parsing) /* malformed summary */
|
|
Fprintf(stderr, MAL_SUM, qt_line);
|
|
} else if (p == summary + 1) {
|
|
; /* ignore empty [] */
|
|
} else { /* got something */
|
|
/* p points one spot past ']', usually to '\n';
|
|
we need to include the \n as part of the size */
|
|
if (parsing) {
|
|
/* during the writing pass we won't be able to recheck
|
|
delivery, so any useless summary for a pline mode
|
|
message has to be carried along to the output file */
|
|
if (curr_msg->delivery == 'p')
|
|
Fprintf(stderr, DUMB_SUM, qt_line);
|
|
/* +1 is for terminating newline */
|
|
curr_msg->summary_size = (long) (p - summary) + 1L;
|
|
} else {
|
|
/* caller is writing rather than just parsing;
|
|
force newline after the closing bracket */
|
|
Strcpy(p, "\n");
|
|
}
|
|
return summary;
|
|
}
|
|
return (char *) 0;
|
|
}
|
|
|
|
static void
|
|
do_qt_control(s)
|
|
char *s;
|
|
{
|
|
char code[BUFSZ];
|
|
int num, id = 0;
|
|
|
|
if (!index(s, '\n'))
|
|
Fprintf(stderr, CTRL_TRUNC, qt_line);
|
|
|
|
switch (s[1]) {
|
|
case 'C':
|
|
if (in_msg) {
|
|
Fprintf(stderr, CREC_IN_MSG, qt_line);
|
|
break;
|
|
} else {
|
|
in_msg = TRUE;
|
|
if (sscanf(&s[4], "%s %5d", code, &id) != 2) {
|
|
Fprintf(stderr, UNREC_CREC, qt_line);
|
|
break;
|
|
}
|
|
num = get_hdr(code);
|
|
if (!num && !new_id(code))
|
|
break;
|
|
num = get_hdr(code) - 1;
|
|
if (known_msg(num, id))
|
|
Fprintf(stderr, DUP_MSG, qt_line);
|
|
else
|
|
new_msg(s, num, id);
|
|
}
|
|
break;
|
|
|
|
case 'E':
|
|
if (!in_msg) {
|
|
Fprintf(stderr, END_NOT_IN_MSG, qt_line);
|
|
} else {
|
|
/* sets curr_msg->summary_size if applicable */
|
|
(void) valid_qt_summary(s, TRUE);
|
|
in_msg = FALSE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Fprintf(stderr, UNREC_CREC, qt_line);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_qt_text(s)
|
|
char *s;
|
|
{
|
|
if (!in_msg) {
|
|
Fprintf(stderr, TEXT_NOT_IN_MSG, qt_line);
|
|
} else if (!index(s, '\n')) {
|
|
Fprintf(stderr, TEXT_TRUNC, qt_line);
|
|
}
|
|
|
|
curr_msg->size += strlen(s);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
adjust_qt_hdrs()
|
|
{
|
|
int i, j;
|
|
long count = 0L, hdr_offset = sizeof(int)
|
|
+ (sizeof(char) * LEN_HDR + sizeof(long))
|
|
* qt_hdr.n_hdr;
|
|
|
|
for (i = 0; i < qt_hdr.n_hdr; i++) {
|
|
qt_hdr.offset[i] = hdr_offset;
|
|
hdr_offset += sizeof(int) + sizeof(struct qtmsg) * msg_hdr[i].n_msg;
|
|
}
|
|
|
|
for (i = 0; i < qt_hdr.n_hdr; i++)
|
|
for (j = 0; j < msg_hdr[i].n_msg; j++) {
|
|
msg_hdr[i].qt_msg[j].offset = hdr_offset + count;
|
|
count +=
|
|
msg_hdr[i].qt_msg[j].size + msg_hdr[i].qt_msg[j].summary_size;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
put_qt_hdrs()
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* The main header record.
|
|
*/
|
|
if (debug)
|
|
Fprintf(stderr, "%ld: header info.\n", ftell(ofp));
|
|
(void) fwrite((genericptr_t) & (qt_hdr.n_hdr), sizeof(int), 1, ofp);
|
|
(void) fwrite((genericptr_t) & (qt_hdr.id[0][0]), sizeof(char) * LEN_HDR,
|
|
qt_hdr.n_hdr, ofp);
|
|
(void) fwrite((genericptr_t) & (qt_hdr.offset[0]), sizeof(long),
|
|
qt_hdr.n_hdr, ofp);
|
|
if (debug) {
|
|
for (i = 0; i < qt_hdr.n_hdr; i++)
|
|
Fprintf(stderr, "%s @ %ld, ", qt_hdr.id[i], qt_hdr.offset[i]);
|
|
Fprintf(stderr, "\n");
|
|
}
|
|
|
|
/*
|
|
* The individual class headers.
|
|
*/
|
|
for (i = 0; i < qt_hdr.n_hdr; i++) {
|
|
if (debug)
|
|
Fprintf(stderr, "%ld: %s header info.\n", ftell(ofp),
|
|
qt_hdr.id[i]);
|
|
(void) fwrite((genericptr_t) & (msg_hdr[i].n_msg), sizeof(int), 1,
|
|
ofp);
|
|
(void) fwrite((genericptr_t) & (msg_hdr[i].qt_msg[0]),
|
|
sizeof(struct qtmsg), msg_hdr[i].n_msg, ofp);
|
|
if (debug) {
|
|
int j;
|
|
|
|
for (j = 0; j < msg_hdr[i].n_msg; j++) {
|
|
Fprintf(stderr, "msg %d @ %ld (%ld)",
|
|
msg_hdr[i].qt_msg[j].msgnum,
|
|
msg_hdr[i].qt_msg[j].offset,
|
|
msg_hdr[i].qt_msg[j].size);
|
|
if (msg_hdr[i].qt_msg[j].summary_size)
|
|
Fprintf(stderr, " [%ld]",
|
|
msg_hdr[i].qt_msg[j].summary_size);
|
|
Fprintf(stderr, "\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
do_questtxt()
|
|
{
|
|
char *line;
|
|
|
|
Sprintf(filename, DATA_IN_TEMPLATE, QTXT_I_FILE);
|
|
if (!(ifp = fopen(filename, RDTMODE))) {
|
|
perror(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
filename[0] = '\0';
|
|
#ifdef FILE_PREFIX
|
|
Strcat(filename, file_prefix);
|
|
#endif
|
|
Sprintf(eos(filename), DATA_TEMPLATE, QTXT_O_FILE);
|
|
if (!(ofp = fopen(filename, WRBMODE))) {
|
|
perror(filename);
|
|
Fclose(ifp);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
qt_hdr.n_hdr = 0;
|
|
qt_line = 0;
|
|
in_msg = FALSE;
|
|
|
|
while ((line = fgetline(ifp)) != 0) {
|
|
SpinCursor(3);
|
|
|
|
qt_line++;
|
|
if (qt_control(line))
|
|
do_qt_control(line);
|
|
else if (qt_comment(line)) {
|
|
free(line);
|
|
continue;
|
|
} else
|
|
do_qt_text(line);
|
|
free(line);
|
|
}
|
|
|
|
(void) rewind(ifp);
|
|
in_msg = FALSE;
|
|
adjust_qt_hdrs();
|
|
put_qt_hdrs();
|
|
while ((line = fgetline(ifp)) != 0) {
|
|
if (qt_control(line)) {
|
|
char *summary_p = 0;
|
|
|
|
in_msg = (line[1] == 'C');
|
|
if (!in_msg)
|
|
summary_p = valid_qt_summary(line, FALSE);
|
|
/* don't write anything unless we've got a summary */
|
|
if (!summary_p) {
|
|
free(line);
|
|
continue;
|
|
}
|
|
/* we have summary text; replace raw %E record with it */
|
|
Strcpy(line, summary_p); /* (guaranteed to fit) */
|
|
} else if (qt_comment(line)) {
|
|
free(line);
|
|
continue;
|
|
}
|
|
if (debug)
|
|
Fprintf(stderr, "%ld: %s", ftell(stdout), line);
|
|
(void) fputs(xcrypt(line), ofp);
|
|
free(line);
|
|
}
|
|
Fclose(ifp);
|
|
Fclose(ofp);
|
|
return;
|
|
}
|
|
|
|
static char temp[32];
|
|
|
|
static char *limit(name, pref) /* limit a name to 30 characters length */
|
|
char *name;
|
|
int pref;
|
|
{
|
|
(void) strncpy(temp, name, pref ? 26 : 30);
|
|
temp[pref ? 26 : 30] = 0;
|
|
return temp;
|
|
}
|
|
|
|
void
|
|
do_objs()
|
|
{
|
|
int i, sum = 0;
|
|
char *c, *objnam;
|
|
int nspell = 0;
|
|
int prefix = 0;
|
|
char class = '\0';
|
|
boolean sumerr = FALSE;
|
|
|
|
filename[0] = '\0';
|
|
#ifdef FILE_PREFIX
|
|
Strcat(filename, file_prefix);
|
|
#endif
|
|
Sprintf(eos(filename), INCLUDE_TEMPLATE, ONAME_FILE);
|
|
if (!(ofp = fopen(filename, WRTMODE))) {
|
|
perror(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
Fprintf(ofp, "%s", Dont_Edit_Code);
|
|
Fprintf(ofp, "#ifndef ONAMES_H\n#define ONAMES_H\n\n");
|
|
|
|
for (i = 0; !i || objects[i].oc_class != ILLOBJ_CLASS; i++) {
|
|
SpinCursor(3);
|
|
|
|
objects[i].oc_name_idx = objects[i].oc_descr_idx = i; /* init */
|
|
if (!(objnam = tmpdup(OBJ_NAME(objects[i]))))
|
|
continue;
|
|
|
|
/* make sure probabilities add up to 1000 */
|
|
if (objects[i].oc_class != class) {
|
|
if (sum && sum != 1000) {
|
|
Fprintf(stderr, "prob error for class %d (%d%%)", class, sum);
|
|
(void) fflush(stderr);
|
|
sumerr = TRUE;
|
|
}
|
|
class = objects[i].oc_class;
|
|
sum = 0;
|
|
}
|
|
|
|
for (c = objnam; *c; c++)
|
|
if (*c >= 'a' && *c <= 'z')
|
|
*c -= (char) ('a' - 'A');
|
|
else if (*c < 'A' || *c > 'Z')
|
|
*c = '_';
|
|
|
|
switch (class) {
|
|
case WAND_CLASS:
|
|
Fprintf(ofp, "#define\tWAN_");
|
|
prefix = 1;
|
|
break;
|
|
case RING_CLASS:
|
|
Fprintf(ofp, "#define\tRIN_");
|
|
prefix = 1;
|
|
break;
|
|
case POTION_CLASS:
|
|
Fprintf(ofp, "#define\tPOT_");
|
|
prefix = 1;
|
|
break;
|
|
case SPBOOK_CLASS:
|
|
Fprintf(ofp, "#define\tSPE_");
|
|
prefix = 1;
|
|
nspell++;
|
|
break;
|
|
case SCROLL_CLASS:
|
|
Fprintf(ofp, "#define\tSCR_");
|
|
prefix = 1;
|
|
break;
|
|
case AMULET_CLASS:
|
|
/* avoid trouble with stupid C preprocessors */
|
|
Fprintf(ofp, "#define\t");
|
|
if (objects[i].oc_material == PLASTIC) {
|
|
Fprintf(ofp, "FAKE_AMULET_OF_YENDOR\t%d\n", i);
|
|
prefix = -1;
|
|
break;
|
|
}
|
|
break;
|
|
case GEM_CLASS:
|
|
/* avoid trouble with stupid C preprocessors */
|
|
if (objects[i].oc_material == GLASS) {
|
|
Fprintf(ofp, "/* #define\t%s\t%d */\n", objnam, i);
|
|
prefix = -1;
|
|
break;
|
|
}
|
|
/*FALLTHRU*/
|
|
default:
|
|
Fprintf(ofp, "#define\t");
|
|
}
|
|
if (prefix >= 0)
|
|
Fprintf(ofp, "%s\t%d\n", limit(objnam, prefix), i);
|
|
prefix = 0;
|
|
|
|
sum += objects[i].oc_prob;
|
|
}
|
|
|
|
/* check last set of probabilities */
|
|
if (sum && sum != 1000) {
|
|
Fprintf(stderr, "prob error for class %d (%d%%)", class, sum);
|
|
(void) fflush(stderr);
|
|
sumerr = TRUE;
|
|
}
|
|
|
|
Fprintf(ofp, "#define\tLAST_GEM\t(JADE)\n");
|
|
Fprintf(ofp, "#define\tMAXSPELL\t%d\n", nspell + 1);
|
|
Fprintf(ofp, "#define\tNUM_OBJECTS\t%d\n", i);
|
|
|
|
Fprintf(ofp, "\n/* Artifacts (unique objects) */\n\n");
|
|
|
|
for (i = 1; artifact_names[i]; i++) {
|
|
SpinCursor(3);
|
|
|
|
for (c = objnam = tmpdup(artifact_names[i]); *c; c++)
|
|
if (*c >= 'a' && *c <= 'z')
|
|
*c -= (char) ('a' - 'A');
|
|
else if (*c < 'A' || *c > 'Z')
|
|
*c = '_';
|
|
|
|
if (!strncmp(objnam, "THE_", 4))
|
|
objnam += 4;
|
|
/* fudge _platinum_ YENDORIAN EXPRESS CARD */
|
|
if (!strncmp(objnam, "PLATINUM_", 9))
|
|
objnam += 9;
|
|
Fprintf(ofp, "#define\tART_%s\t%d\n", limit(objnam, 1), i);
|
|
}
|
|
|
|
Fprintf(ofp, "#define\tNROFARTIFACTS\t%d\n", i - 1);
|
|
Fprintf(ofp, "\n#endif /* ONAMES_H */\n");
|
|
Fclose(ofp);
|
|
if (sumerr)
|
|
exit(EXIT_FAILURE);
|
|
return;
|
|
}
|
|
|
|
/* Read one line from input, up to and including the next newline
|
|
* character. Returns a pointer to the heap-allocated string, or a
|
|
* null pointer if no characters were read.
|
|
*/
|
|
static char *
|
|
fgetline(fd)
|
|
FILE *fd;
|
|
{
|
|
static const int inc = 256;
|
|
int len = inc;
|
|
char *c = malloc(len), *ret;
|
|
|
|
for (;;) {
|
|
ret = fgets(c + len - inc, inc, fd);
|
|
if (!ret) {
|
|
free(c);
|
|
c = NULL;
|
|
break;
|
|
} else if (index(c, '\n')) {
|
|
/* normal case: we have a full line */
|
|
break;
|
|
}
|
|
len += inc;
|
|
c = realloc(c, len);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
static char *
|
|
tmpdup(str)
|
|
const char *str;
|
|
{
|
|
static char buf[128];
|
|
|
|
if (!str)
|
|
return (char *) 0;
|
|
(void) strncpy(buf, str, 127);
|
|
return buf;
|
|
}
|
|
|
|
static char *
|
|
eos(str)
|
|
char *str;
|
|
{
|
|
while (*str)
|
|
str++;
|
|
return str;
|
|
}
|
|
|
|
/*
|
|
* macro used to control vision algorithms:
|
|
* VISION_TABLES => generate tables
|
|
*/
|
|
|
|
void
|
|
do_vision()
|
|
{
|
|
#ifdef VISION_TABLES
|
|
int i, j;
|
|
|
|
/* Everything is clear. xclear may be malloc'ed.
|
|
* Block the upper left corner (BLOCK_HEIGHTxBLOCK_WIDTH)
|
|
*/
|
|
for (i = 0; i < MAX_ROW; i++)
|
|
for (j = 0; j < MAX_COL; j++)
|
|
if (i < BLOCK_HEIGHT && j < BLOCK_WIDTH)
|
|
xclear[i][j] = '\000';
|
|
else
|
|
xclear[i][j] = '\001';
|
|
#endif /* VISION_TABLES */
|
|
|
|
SpinCursor(3);
|
|
|
|
/*
|
|
* create the include file, "vis_tab.h"
|
|
*/
|
|
filename[0] = '\0';
|
|
#ifdef FILE_PREFIX
|
|
Strcat(filename, file_prefix);
|
|
#endif
|
|
Sprintf(eos(filename), INCLUDE_TEMPLATE, VIS_TAB_H);
|
|
if (!(ofp = fopen(filename, WRTMODE))) {
|
|
perror(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
Fprintf(ofp, "%s", Dont_Edit_Code);
|
|
Fprintf(ofp, "#ifdef VISION_TABLES\n");
|
|
#ifdef VISION_TABLES
|
|
H_close_gen();
|
|
H_far_gen();
|
|
#endif /* VISION_TABLES */
|
|
Fprintf(ofp, "\n#endif /* VISION_TABLES */\n");
|
|
Fclose(ofp);
|
|
|
|
SpinCursor(3);
|
|
|
|
/*
|
|
* create the source file, "vis_tab.c"
|
|
*/
|
|
filename[0] = '\0';
|
|
#ifdef FILE_PREFIX
|
|
Strcat(filename, file_prefix);
|
|
#endif
|
|
Sprintf(eos(filename), SOURCE_TEMPLATE, VIS_TAB_C);
|
|
if (!(ofp = fopen(filename, WRTMODE))) {
|
|
perror(filename);
|
|
/* creating vis_tab.c failed; remove the vis_tab.h we just made */
|
|
filename[0] = '\0';
|
|
#ifdef FILE_PREFIX
|
|
Strcat(filename, file_prefix);
|
|
#endif
|
|
Sprintf(eos(filename), INCLUDE_TEMPLATE, VIS_TAB_H);
|
|
Unlink(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
Fprintf(ofp, "%s", Dont_Edit_Code);
|
|
Fprintf(ofp, "#include \"config.h\"\n");
|
|
Fprintf(ofp, "#ifdef VISION_TABLES\n");
|
|
Fprintf(ofp, "#include \"vis_tab.h\"\n");
|
|
|
|
SpinCursor(3);
|
|
|
|
#ifdef VISION_TABLES
|
|
C_close_gen();
|
|
C_far_gen();
|
|
Fprintf(ofp, "\nvoid vis_tab_init() { return; }\n");
|
|
#endif /* VISION_TABLES */
|
|
|
|
SpinCursor(3);
|
|
|
|
Fprintf(ofp, "\n#endif /* VISION_TABLES */\n");
|
|
Fprintf(ofp, "\n/*vis_tab.c*/\n");
|
|
|
|
Fclose(ofp);
|
|
return;
|
|
}
|
|
|
|
#ifdef VISION_TABLES
|
|
|
|
/*-------------- vision tables --------------*\
|
|
*
|
|
* Generate the close and far tables. This is done by setting up a
|
|
* fake dungeon and moving our source to different positions relative
|
|
* to a block and finding the first/last visible position. The fake
|
|
* dungeon is all clear execpt for the upper left corner (BLOCK_HEIGHT
|
|
* by BLOCK_WIDTH) is blocked. Then we move the source around relative
|
|
* to the corner of the block. For each new position of the source
|
|
* we check positions on rows "kittycorner" from the source. We check
|
|
* positions until they are either in sight or out of sight (depends on
|
|
* which table we are generating). The picture below shows the setup
|
|
* for the generation of the close table. The generation of the far
|
|
* table would switch the quadrants of the '@' and the "Check rows
|
|
* here".
|
|
*
|
|
*
|
|
* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
|
* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
|
* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,, Check rows here ,,,,,,,,,,,,
|
|
* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
|
* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXB,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
|
* ...............................
|
|
* ...............................
|
|
* .........@.....................
|
|
* ...............................
|
|
*
|
|
* Table generation figure (close_table). The 'X's are blocked points.
|
|
* The 'B' is a special blocked point. The '@' is the source. The ','s
|
|
* are the target area. The '.' are just open areas.
|
|
*
|
|
*
|
|
* Example usage of close_table[][][].
|
|
*
|
|
* The table is as follows:
|
|
*
|
|
* dy = |row of '@' - row of 'B'| - 1
|
|
* dx = |col of '@' - col of 'B'|
|
|
*
|
|
* The first indices are the deltas from the source '@' and the block 'B'.
|
|
* You must check for the value inside the abs value bars being zero. If
|
|
* so then the block is on the same row and you don't need to do a table
|
|
* lookup. The last value:
|
|
*
|
|
* dcy = |row of block - row to be checked|
|
|
*
|
|
* Is the value of the first visible spot on the check row from the
|
|
* block column. So
|
|
*
|
|
* first visible col = close_table[dy][dx][dcy] + col of 'B'
|
|
*
|
|
\*-------------- vision tables --------------*/
|
|
|
|
static void
|
|
H_close_gen()
|
|
{
|
|
Fprintf(ofp, "\n/* Close */\n");
|
|
Fprintf(ofp,
|
|
"#define CLOSE_MAX_SB_DY %2d\t/* |src row - block row| - 1\t*/\n",
|
|
TEST_HEIGHT - 1);
|
|
Fprintf(ofp,
|
|
"#define CLOSE_MAX_SB_DX %2d\t/* |src col - block col|\t*/\n",
|
|
TEST_WIDTH);
|
|
Fprintf(ofp,
|
|
"#define CLOSE_MAX_BC_DY %2d\t/* |block row - check row|\t*/\n",
|
|
TEST_HEIGHT);
|
|
Fprintf(ofp, "typedef struct {\n");
|
|
Fprintf(ofp,
|
|
" unsigned char close[CLOSE_MAX_SB_DX][CLOSE_MAX_BC_DY];\n");
|
|
Fprintf(ofp, "} close2d;\n");
|
|
Fprintf(ofp, "extern close2d close_table[CLOSE_MAX_SB_DY];\n");
|
|
return;
|
|
}
|
|
|
|
static void
|
|
H_far_gen()
|
|
{
|
|
Fprintf(ofp, "\n/* Far */\n");
|
|
Fprintf(ofp, "#define FAR_MAX_SB_DY %2d\t/* |src row - block row|\t*/\n",
|
|
TEST_HEIGHT);
|
|
Fprintf(ofp,
|
|
"#define FAR_MAX_SB_DX %2d\t/* |src col - block col| - 1\t*/\n",
|
|
TEST_WIDTH - 1);
|
|
Fprintf(ofp,
|
|
"#define FAR_MAX_BC_DY %2d\t/* |block row - check row| - 1\t*/\n",
|
|
TEST_HEIGHT - 1);
|
|
Fprintf(ofp, "typedef struct {\n");
|
|
Fprintf(ofp, " unsigned char far_q[FAR_MAX_SB_DX][FAR_MAX_BC_DY];\n");
|
|
Fprintf(ofp, "} far2d;\n");
|
|
Fprintf(ofp, "extern far2d far_table[FAR_MAX_SB_DY];\n");
|
|
return;
|
|
}
|
|
|
|
static void
|
|
C_close_gen()
|
|
{
|
|
int i, dx, dy;
|
|
int src_row, src_col; /* source */
|
|
int block_row, block_col; /* block */
|
|
int this_row;
|
|
int no_more;
|
|
const char *delim;
|
|
|
|
block_row = BLOCK_HEIGHT - 1;
|
|
block_col = BLOCK_WIDTH - 1;
|
|
|
|
Fprintf(ofp, "\n#ifndef FAR_TABLE_ONLY\n");
|
|
Fprintf(ofp, "\nclose2d close_table[CLOSE_MAX_SB_DY] = {\n");
|
|
#ifndef no_vision_progress
|
|
Fprintf(stderr, "\nclose:");
|
|
#endif
|
|
|
|
for (dy = 1; dy < TEST_HEIGHT; dy++) {
|
|
src_row = block_row + dy;
|
|
Fprintf(ofp, "/* DY = %2d (- 1)*/\n {{\n", dy);
|
|
#ifndef no_vision_progress
|
|
Fprintf(stderr, " %2d", dy), (void) fflush(stderr);
|
|
#endif
|
|
for (dx = 0; dx < TEST_WIDTH; dx++) {
|
|
src_col = block_col - dx;
|
|
Fprintf(ofp, " /*%2d*/ {", dx);
|
|
|
|
no_more = 0;
|
|
for (this_row = 0; this_row < TEST_HEIGHT; this_row++) {
|
|
delim = (this_row < TEST_HEIGHT - 1) ? "," : "";
|
|
if (no_more) {
|
|
Fprintf(ofp, "%s%s", CLOSE_OFF_TABLE_STRING, delim);
|
|
continue;
|
|
}
|
|
SpinCursor(3);
|
|
|
|
/* Find the first column that we can see. */
|
|
for (i = block_col + 1; i < MAX_COL; i++) {
|
|
if (clear_path(src_row, src_col, block_row - this_row, i))
|
|
break;
|
|
}
|
|
|
|
if (i == MAX_COL)
|
|
no_more = 1;
|
|
Fprintf(ofp, "%2d%s", i - block_col, delim);
|
|
}
|
|
Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n");
|
|
}
|
|
Fprintf(ofp, " }},\n");
|
|
}
|
|
|
|
Fprintf(ofp, "}; /* close_table[] */\n"); /* closing brace for table */
|
|
Fprintf(ofp, "#endif /* !FAR_TABLE_ONLY */\n");
|
|
#ifndef no_vision_progress
|
|
Fprintf(stderr, "\n");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
static void
|
|
C_far_gen()
|
|
{
|
|
int i, dx, dy;
|
|
int src_row, src_col; /* source */
|
|
int block_row, block_col; /* block */
|
|
int this_row;
|
|
const char *delim;
|
|
|
|
block_row = BLOCK_HEIGHT - 1;
|
|
block_col = BLOCK_WIDTH - 1;
|
|
|
|
Fprintf(ofp, "\n#ifndef CLOSE_TABLE_ONLY\n");
|
|
Fprintf(ofp, "\nfar2d far_table[FAR_MAX_SB_DY] = {\n");
|
|
#ifndef no_vision_progress
|
|
Fprintf(stderr, "\n_far_:");
|
|
#endif
|
|
|
|
for (dy = 0; dy < TEST_HEIGHT; dy++) {
|
|
src_row = block_row - dy;
|
|
Fprintf(ofp, "/* DY = %2d */\n {{\n", dy);
|
|
#ifndef no_vision_progress
|
|
Fprintf(stderr, " %2d", dy), (void) fflush(stderr);
|
|
#endif
|
|
for (dx = 1; dx < TEST_WIDTH; dx++) {
|
|
src_col = block_col + dx;
|
|
Fprintf(ofp, " /*%2d(-1)*/ {", dx);
|
|
|
|
for (this_row = block_row + 1; this_row < block_row + TEST_HEIGHT;
|
|
this_row++) {
|
|
delim = (this_row < block_row + TEST_HEIGHT - 1) ? "," : "";
|
|
|
|
SpinCursor(3);
|
|
/* Find first col that we can see. */
|
|
for (i = 0; i <= block_col; i++) {
|
|
if (clear_path(src_row, src_col, this_row, i))
|
|
break;
|
|
}
|
|
|
|
if (block_col - i < 0)
|
|
Fprintf(ofp, "%s%s", FAR_OFF_TABLE_STRING, delim);
|
|
else
|
|
Fprintf(ofp, "%2d%s", block_col - i, delim);
|
|
}
|
|
Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n");
|
|
}
|
|
Fprintf(ofp, " }},\n");
|
|
}
|
|
|
|
Fprintf(ofp, "}; /* far_table[] */\n"); /* closing brace for table */
|
|
Fprintf(ofp, "#endif /* !CLOSE_TABLE_ONLY */\n");
|
|
#ifndef no_vision_progress
|
|
Fprintf(stderr, "\n");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* "Draw" a line from the hero to the given location. Stop if we hit a
|
|
* wall.
|
|
*
|
|
* Generalized integer Bresenham's algorithm (fast line drawing) for
|
|
* all quadrants. From _Procedural Elements for Computer Graphics_, by
|
|
* David F. Rogers. McGraw-Hill, 1985.
|
|
*
|
|
* I have tried a little bit of optimization by pulling compares out of
|
|
* the inner loops.
|
|
*
|
|
* NOTE: This had better *not* be called from a position on the
|
|
* same row as the hero.
|
|
*/
|
|
static int
|
|
clear_path(you_row, you_col, y2, x2)
|
|
int you_row, you_col, y2, x2;
|
|
{
|
|
int dx, dy, s1, s2;
|
|
register int i, error, x, y, dxs, dys;
|
|
|
|
x = you_col;
|
|
y = you_row;
|
|
dx = abs(x2 - you_col);
|
|
dy = abs(y2 - you_row);
|
|
s1 = sign(x2 - you_col);
|
|
s2 = sign(y2 - you_row);
|
|
|
|
if (s1 == 0) { /* same column */
|
|
if (s2 == 1) { /* below (larger y2 value) */
|
|
for (i = you_row + 1; i < y2; i++)
|
|
if (!xclear[i][you_col])
|
|
return 0;
|
|
} else { /* above (smaller y2 value) */
|
|
for (i = y2 + 1; i < you_row; i++)
|
|
if (!xclear[i][you_col])
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Lines at 0 and 90 degrees have been weeded out.
|
|
*/
|
|
if (dy > dx) {
|
|
error = dx;
|
|
dx = dy;
|
|
dy = error; /* swap the values */
|
|
dxs = dx << 1; /* save the shifted values */
|
|
dys = dy << 1;
|
|
error = dys - dx; /* NOTE: error is used as a temporary above */
|
|
|
|
for (i = 0; i < dx; i++) {
|
|
if (!xclear[y][x])
|
|
return 0; /* plot point */
|
|
|
|
while (error >= 0) {
|
|
x += s1;
|
|
error -= dxs;
|
|
}
|
|
y += s2;
|
|
error += dys;
|
|
}
|
|
} else {
|
|
dxs = dx << 1; /* save the shifted values */
|
|
dys = dy << 1;
|
|
error = dys - dx;
|
|
|
|
for (i = 0; i < dx; i++) {
|
|
if (!xclear[y][x])
|
|
return 0; /* plot point */
|
|
|
|
while (error >= 0) {
|
|
y += s2;
|
|
error -= dxs;
|
|
}
|
|
x += s1;
|
|
error += dys;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
#endif /* VISION_TABLES */
|
|
|
|
#ifdef STRICT_REF_DEF
|
|
NEARDATA struct flag flags;
|
|
#ifdef ATTRIB_H
|
|
struct attribs attrmax, attrmin;
|
|
#endif
|
|
#endif /* STRICT_REF_DEF */
|
|
|
|
/*makedefs.c*/
|