Home | History | Annotate | Line # | Download | only in adventure
io.c revision 1.18
      1 /*	$NetBSD: io.c,v 1.18 2006/03/18 23:33:38 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1991, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * The game adventure was originally written in Fortran by Will Crowther
      8  * and Don Woods.  It was later translated to C and enhanced by Jim
      9  * Gillogly.  This code is derived from software contributed to Berkeley
     10  * by Jim Gillogly at The Rand Corporation.
     11  *
     12  * Redistribution and use in source and binary forms, with or without
     13  * modification, are permitted provided that the following conditions
     14  * are met:
     15  * 1. Redistributions of source code must retain the above copyright
     16  *    notice, this list of conditions and the following disclaimer.
     17  * 2. Redistributions in binary form must reproduce the above copyright
     18  *    notice, this list of conditions and the following disclaimer in the
     19  *    documentation and/or other materials provided with the distribution.
     20  * 3. Neither the name of the University nor the names of its contributors
     21  *    may be used to endorse or promote products derived from this software
     22  *    without specific prior written permission.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34  * SUCH DAMAGE.
     35  */
     36 
     37 #include <sys/cdefs.h>
     38 #ifndef lint
     39 #if 0
     40 static char sccsid[] = "@(#)io.c	8.1 (Berkeley) 5/31/93";
     41 #else
     42 __RCSID("$NetBSD: io.c,v 1.18 2006/03/18 23:33:38 christos Exp $");
     43 #endif
     44 #endif /* not lint */
     45 
     46 /*      Re-coding of advent in C: file i/o and user i/o                 */
     47 
     48 #include <err.h>
     49 #include <stdio.h>
     50 #include <string.h>
     51 #include <stdlib.h>
     52 #include "hdr.h"
     53 #include "extern.h"
     54 
     55 
     56 /* get command from user        */
     57 /* no prompt, usually           */
     58 void
     59 getin(char **wrd1, char **wrd2)
     60 {
     61 	char   *s;
     62 	static char wd1buf[MAXSTR], wd2buf[MAXSTR];
     63 	int     first, numch;
     64 
     65 	*wrd1 = wd1buf;				/* return ptr to internal str */
     66 	*wrd2 = wd2buf;
     67 	wd2buf[0] = 0;				/* in case it isn't set here */
     68 	for (s = wd1buf, first = 1, numch = 0;;) {
     69 		if ((*s = getchar()) >= 'A' && *s <= 'Z')
     70 			*s = *s - ('A' - 'a');
     71 		/* convert to upper case */
     72 		switch (*s) {			/* start reading from user */
     73 		case '\n':
     74 			*s = 0;
     75 			return;
     76 		case ' ':
     77 			if (s == wd1buf || s == wd2buf)	/* initial blank */
     78 				continue;
     79 			*s = 0;
     80 			if (first) {		/* finished 1st wd; start 2nd */
     81 				first = numch = 0;
     82 				s = wd2buf;
     83 				break;
     84 			} else {		/* finished 2nd word */
     85 				FLUSHLINE;
     86 				*s = 0;
     87 				return;
     88 			}
     89 		case EOF:
     90 			printf("user closed input stream, quitting...\n");
     91 			exit(0);
     92 		default:
     93 			if (++numch >= MAXSTR) {	/* string too long */
     94 				printf("Give me a break!!\n");
     95 				wd1buf[0] = wd2buf[0] = 0;
     96 				FLUSHLINE;
     97 				return;
     98 			}
     99 			s++;
    100 		}
    101 	}
    102 }
    103 
    104 /* confirm with rspeak          */
    105 int
    106 yes(int x, int y, int z)
    107 {
    108 	int     result = TRUE;	/* pacify gcc */
    109 	int    ch;
    110 	for (;;) {
    111 		rspeak(x);	/* tell him what we want */
    112 		if ((ch = getchar()) == 'y')
    113 			result = TRUE;
    114 		else if (ch == 'n')
    115 			result = FALSE;
    116 		else if (ch == EOF) {
    117 			printf("user closed input stream, quitting...\n");
    118 			exit(0);
    119 		}
    120 		FLUSHLINE;
    121 		if (ch == 'y' || ch == 'n')
    122 			break;
    123 		printf("Please answer the question.\n");
    124 	}
    125 	if (result == TRUE)
    126 		rspeak(y);
    127 	if (result == FALSE)
    128 		rspeak(z);
    129 	return (result);
    130 }
    131 
    132 /* confirm with mspeak          */
    133 int
    134 yesm(int x, int y, int z)
    135 {
    136 	int     result = TRUE;	/* pacify gcc */
    137 	int    ch;
    138 	for (;;) {
    139 		mspeak(x);	/* tell him what we want */
    140 		if ((ch = getchar()) == 'y')
    141 			result = TRUE;
    142 		else if (ch == 'n')
    143 			result = FALSE;
    144 		else if (ch == EOF) {
    145 			printf("user closed input stream, quitting...\n");
    146 			exit(0);
    147 		}
    148 		FLUSHLINE;
    149 		if (ch == 'y' || ch == 'n')
    150 			break;
    151 		printf("Please answer the question.\n");
    152 	}
    153 	if (result == TRUE)
    154 		mspeak(y);
    155 	if (result == FALSE)
    156 		mspeak(z);
    157 	return (result);
    158 }
    159 /* FILE *inbuf,*outbuf; */
    160 
    161 char   *inptr;			/* Pointer into virtual disk    */
    162 
    163 int     outsw = 0;		/* putting stuff to data file?  */
    164 
    165 const char    iotape[] = "Ax3F'\003tt$8h\315qer*h\017nGKrX\207:!l";
    166 const char   *tape = iotape;		/* pointer to encryption tape   */
    167 
    168 /* next virtual char, bump adr  */
    169 int
    170 next(void)
    171 {
    172 	int     ch;
    173 
    174 	ch = (*inptr ^ random()) & 0xFF;	/* Decrypt input data  */
    175 	if (outsw) {		/* putting data in tmp file     */
    176 		if (*tape == 0)
    177 			tape = iotape;	/* rewind encryption tape       */
    178 		*inptr = ch ^ *tape++;	/* re-encrypt and replace value */
    179 	}
    180 	inptr++;
    181 	return (ch);
    182 }
    183 
    184 char    breakch;		/* tell which char ended rnum   */
    185 
    186 /* "read" data from virtual file */
    187 void
    188 rdata(void)
    189 {
    190 	int     sect;
    191 	char    ch;
    192 
    193 	inptr = data_file;	/* Pointer to virtual data file */
    194 	srandom(SEED);		/* which is lightly encrypted.  */
    195 
    196 	clsses = 1;
    197 	for (;;) {		/* read data sections           */
    198 		sect = next() - '0';	/* 1st digit of section number  */
    199 #ifdef VERBOSE
    200 		printf("Section %c", sect + '0');
    201 #endif
    202 		if ((ch = next()) != LF) {	/* is there a second digit?     */
    203 			FLUSHLF;
    204 #ifdef VERBOSE
    205 			putchar(ch);
    206 #endif
    207 			sect = 10 * sect + ch - '0';
    208 		}
    209 #ifdef VERBOSE
    210 		putchar('\n');
    211 #endif
    212 		switch (sect) {
    213 		case 0:	/* finished reading database    */
    214 			return;
    215 		case 1:	/* long form descriptions       */
    216 			rdesc(1);
    217 			break;
    218 		case 2:	/* short form descriptions      */
    219 			rdesc(2);
    220 			break;
    221 		case 3:	/* travel table                 */
    222 			rtrav();
    223 			break;
    224 		case 4:	/* vocabulary                   */
    225 			rvoc();
    226 			break;
    227 		case 5:	/* object descriptions          */
    228 			rdesc(5);
    229 			break;
    230 		case 6:	/* arbitrary messages           */
    231 			rdesc(6);
    232 			break;
    233 		case 7:	/* object locations             */
    234 			rlocs();
    235 			break;
    236 		case 8:	/* action defaults              */
    237 			rdflt();
    238 			break;
    239 		case 9:	/* liquid assets                */
    240 			rliq();
    241 			break;
    242 		case 10:	/* class messages               */
    243 			rdesc(10);
    244 			break;
    245 		case 11:	/* hints                        */
    246 			rhints();
    247 			break;
    248 		case 12:	/* magic messages               */
    249 			rdesc(12);
    250 			break;
    251 		default:
    252 			printf("Invalid data section number: %d\n", sect);
    253 			for (;;)
    254 				putchar(next());
    255 		}
    256 		if (breakch != LF)	/* routines return after "-1"   */
    257 			FLUSHLF;
    258 	}
    259 }
    260 
    261 char    nbf[12];
    262 
    263 /* read initial location num    */
    264 int
    265 rnum(void)
    266 {
    267 	char   *s;
    268 	tape = iotape;		/* restart encryption tape      */
    269 	for (s = nbf, *s = 0;; s++)
    270 		if ((*s = next()) == TAB || *s == '\n' || *s == LF)
    271 			break;
    272 	breakch = *s;		/* save char for rtrav()        */
    273 	*s = 0;			/* got the number as ascii      */
    274 	if (nbf[0] == '-')
    275 		return (-1);	/* end of data                  */
    276 	return (atoi(nbf));	/* convert it to integer        */
    277 }
    278 
    279 char   *seekhere;
    280 
    281 /* read description-format msgs */
    282 void
    283 rdesc(int sect)
    284 {
    285 	int     locc;
    286 	char   *seekstart, *maystart;
    287 
    288 	seekhere = inptr;	/* Where are we in virtual file? */
    289 	outsw = 1;		/* these msgs go into tmp file  */
    290 	for (oldloc = -1, seekstart = seekhere;;) {
    291 		maystart = inptr;	/* maybe starting new entry     */
    292 		if ((locc = rnum()) != oldloc && oldloc >= 0 /* finished msg */
    293   		    /* unless sect 5 */
    294 		    && !(sect == 5 && (locc == 0 || locc >= 100))) {
    295 			switch (sect) {	/* now put it into right table  */
    296 			case 1:/* long descriptions            */
    297 				ltext[oldloc].seekadr = seekhere;
    298 				ltext[oldloc].txtlen = maystart - seekstart;
    299 				break;
    300 			case 2:/* short descriptions           */
    301 				stext[oldloc].seekadr = seekhere;
    302 				stext[oldloc].txtlen = maystart - seekstart;
    303 				break;
    304 			case 5:/* object descriptions          */
    305 				ptext[oldloc].seekadr = seekhere;
    306 				ptext[oldloc].txtlen = maystart - seekstart;
    307 				break;
    308 			case 6:/* random messages              */
    309 				if (oldloc >= RTXSIZ)
    310 					errx(1,"Too many random msgs");
    311 				rtext[oldloc].seekadr = seekhere;
    312 				rtext[oldloc].txtlen = maystart - seekstart;
    313 				break;
    314 			case 10:	/* class messages               */
    315 				ctext[clsses].seekadr = seekhere;
    316 				ctext[clsses].txtlen = maystart - seekstart;
    317 				cval[clsses++] = oldloc;
    318 				break;
    319 			case 12:	/* magic messages               */
    320 				if (oldloc >= MAGSIZ)
    321 					errx(1,"Too many magic msgs");
    322 				mtext[oldloc].seekadr = seekhere;
    323 				mtext[oldloc].txtlen = maystart - seekstart;
    324 				break;
    325 			default:
    326 				errx(1,"rdesc called with bad section");
    327 			}
    328 			seekhere += maystart - seekstart;
    329 		}
    330 		if (locc < 0) {
    331 			outsw = 0;	/* turn off output              */
    332 			seekhere += 3;	/* -1<delimiter>                */
    333 			return;
    334 		}
    335 		if (sect != 5 || (locc > 0 && locc < 100)) {
    336 			if (oldloc != locc)	/* starting a new message */
    337 				seekstart = maystart;
    338 			oldloc = locc;
    339 		}
    340 		FLUSHLF;	/* scan the line                */
    341 	}
    342 }
    343 
    344 /* read travel table            */
    345 void
    346 rtrav(void)
    347 {
    348 	int     locc;
    349 	struct travlist *t = NULL;
    350 	char   *s;
    351 	char    buf[12];
    352 	int     len, m, n, entries = 0;
    353 
    354 	for (oldloc = -1;;) {	/* get another line             */
    355 		/* end of entry */
    356 		if ((locc = rnum()) != oldloc && oldloc >= 0 && t) {
    357 			t->next = 0;	/* terminate the old entry      */
    358 			/* printf("%d:%d entries\n",oldloc,entries);       */
    359 			/* twrite(oldloc);                                 */
    360 		}
    361 		if (locc == -1)
    362 			return;
    363 		if (locc != oldloc) {	/* getting a new entry         */
    364 			t = travel[locc] = (struct travlist *)
    365 				malloc(sizeof(struct travlist));
    366 			if ( t == NULL)
    367 				err(1, NULL);
    368 			/* printf("New travel list for %d\n",locc);        */
    369 			entries = 0;
    370 			oldloc = locc;
    371 		}
    372 		for (s = buf;; s++)	/* get the newloc number /ASCII */
    373 			if ((*s = next()) == TAB || *s == LF)
    374 				break;
    375 		*s = 0;
    376 		len = length(buf) - 1;	/* quad long number handling    */
    377 		/* printf("Newloc: %s (%d chars)\n",buf,len);              */
    378 		if (len < 4) {	/* no "m" conditions            */
    379 			m = 0;
    380 			n = atoi(buf);	/* newloc mod 1000 = newloc     */
    381 		} else {	/* a long integer               */
    382 			n = atoi(buf + len - 3);
    383 			buf[len - 3] = 0;	/* terminate newloc/1000  */
    384 			m = atoi(buf);
    385 		}
    386 		while (breakch != LF) {	/* only do one line at a time   */
    387 			if (t && entries++) {
    388 				t->next = malloc(sizeof(struct travlist));
    389 				if (t->next == NULL)
    390 					err(1, NULL);
    391 				t = t->next;
    392 			}
    393 			t->tverb = rnum();	/* get verb from the file */
    394 			t->tloc = n;	/* table entry mod 1000         */
    395 			t->conditions = m;	/* table entry / 1000   */
    396 			/* printf("entry %d for %d\n",entries,locc);    */
    397 		}
    398 	}
    399 }
    400 #ifdef DEBUG
    401 
    402 /* travel options from this loc */
    403 void
    404 twrite(int loq)
    405 {
    406 	struct travlist *t;
    407 	printf("If");
    408 	speak(&ltext[loq]);
    409 	printf("then\n");
    410 	for (t = travel[loq]; t != 0; t = t->next) {
    411 		printf("verb %d takes you to ", t->tverb);
    412 		if (t->tloc <= 300)
    413 			speak(&ltext[t->tloc]);
    414 		else
    415 			if (t->tloc <= 500)
    416 				printf("special code %d\n", t->tloc - 300);
    417 			else
    418 				rspeak(t->tloc - 500);
    419 		printf("under conditions %d\n", t->conditions);
    420 	}
    421 }
    422 #endif				/* DEBUG */
    423 
    424 /* read the vocabulary          */
    425 void
    426 rvoc(void)
    427 {
    428 	char   *s;
    429 	int     idx;
    430 	char    buf[6];
    431 	for (;;) {
    432 		idx = rnum();
    433 		if (idx < 0)
    434 			break;
    435 		for (s = buf, *s = 0;; s++)	/* get the word  */
    436 			if ((*s = next()) == TAB || *s == '\n' || *s == LF
    437 			    || *s == ' ')
    438 				break;
    439 		/* terminate word with newline, LF, tab, blank  */
    440 		if (*s != '\n' && *s != LF)
    441 			FLUSHLF;/* can be comments    */
    442 		*s = 0;
    443 		/* printf("\"%s\"=%d\n",buf,idx); */
    444 		vocab(buf, -2, idx);
    445 	}
    446 /*	prht();	*/
    447 }
    448 
    449 /* initial object locations     */
    450 void
    451 rlocs(void)
    452 {
    453 	for (;;) {
    454 		if ((obj = rnum()) < 0)
    455 			break;
    456 		plac[obj] = rnum();	/* initial loc for this obj     */
    457 		if (breakch == TAB)	/* there's another entry        */
    458 			fixd[obj] = rnum();
    459 		else
    460 			fixd[obj] = 0;
    461 	}
    462 }
    463 
    464 /* default verb messages        */
    465 void
    466 rdflt(void)
    467 {
    468 	for (;;) {
    469 		if ((verb = rnum()) < 0)
    470 			break;
    471 		actspk[verb] = rnum();
    472 	}
    473 }
    474 
    475 /* liquid assets &c: cond bits  */
    476 void
    477 rliq(void)
    478 {
    479 	int     bitnum;
    480 	for (;;) {		/* read new bit list            */
    481 		if ((bitnum = rnum()) < 0)
    482 			break;
    483 		for (;;) {	/* read locs for bits           */
    484 			int n = rnum();
    485 			if (n < 0)
    486 				break;
    487 			cond[n] |= setbit[bitnum];
    488 			if (breakch == LF)
    489 				break;
    490 		}
    491 	}
    492 }
    493 
    494 void
    495 rhints(void)
    496 {
    497 	int     hintnum, i;
    498 	hntmax = 0;
    499 	for (;;) {
    500 		if ((hintnum = rnum()) < 0)
    501 			break;
    502 		for (i = 1; i < 5; i++)
    503 			hints[hintnum][i] = rnum();
    504 		if (hintnum > hntmax)
    505 			hntmax = hintnum;
    506 	}
    507 }
    508 
    509 
    510 void
    511 rspeak(int msg)
    512 {
    513 	if (msg != 0)
    514 		speak(&rtext[msg]);
    515 }
    516 
    517 
    518 void
    519 mspeak(int msg)
    520 {
    521 	if (msg != 0)
    522 		speak(&mtext[msg]);
    523 }
    524 
    525 
    526 /* read, decrypt, and print a message (not ptext)      */
    527 /* msg is a pointer to seek address and length of mess */
    528 void
    529 speak(const struct text *msg)
    530 {
    531 	char   *s, nonfirst;
    532 
    533 	s = msg->seekadr;
    534 	nonfirst = 0;
    535 	while (s - msg->seekadr < msg->txtlen) { /* read a line at a time */
    536 		tape = iotape;	/* restart decryption tape      */
    537 		while ((*s++ ^ *tape++) != TAB); /* read past loc num       */
    538 		/* assume tape is longer than location number           */
    539 		/* plus the lookahead put together                    */
    540 		if ((*s ^ *tape) == '>' &&
    541 		    (*(s + 1) ^ *(tape + 1)) == '$' &&
    542 		    (*(s + 2) ^ *(tape + 2)) == '<')
    543 			break;
    544 		if (blklin && !nonfirst++)
    545 			putchar('\n');
    546 		do {
    547 			if (*tape == 0)
    548 				tape = iotape;	/* rewind decryp tape */
    549 			putchar(*s ^ *tape);
    550 		} while ((*s++ ^ *tape++) != LF); /* better end with LF   */
    551 	}
    552 }
    553 
    554 /* read, decrypt and print a ptext message  */
    555 /* msg is the number of all the p msgs for this place  */
    556 /* assumes object 1 doesn't have prop 1, obj 2 no prop 2 &c */
    557 void
    558 pspeak(int m, int skip)
    559 {
    560 	char   *s, nonfirst;
    561 	char   *numst;
    562 	struct text *msg;
    563 	char   *tbuf;
    564 
    565 	msg = &ptext[m];
    566 	if ((tbuf = (char *) malloc(msg->txtlen + 1)) == NULL)
    567 		err(1, NULL);
    568 	memcpy(tbuf, msg->seekadr, msg->txtlen + 1);	/* Room to null */
    569 	s = tbuf;
    570 
    571 	nonfirst = 0;
    572 	while (s - tbuf < msg->txtlen) {	/* read line at a time */
    573 		tape = iotape;	/* restart decryption tape      */
    574 		for (numst = s; (*s ^= *tape++) != TAB; s++); /* get number  */
    575 
    576 				/* Temporarily trash the string (cringe) */
    577 		*s++ = 0;	/* decrypting number within the string   */
    578 
    579 		if (atoi(numst) != 100 * skip && skip >= 0) {
    580 			while ((*s++ ^ *tape++) != LF)	/* flush the line    */
    581 				if (*tape == 0)
    582 					tape = iotape;
    583 			continue;
    584 		}
    585 		if ((*s ^ *tape) == '>' && (*(s + 1) ^ *(tape + 1)) == '$' &&
    586 		    (*(s + 2) ^ *(tape + 2)) == '<')
    587 			break;
    588 		if (blklin && !nonfirst++)
    589 			putchar('\n');
    590 		do {
    591 			if (*tape == 0)
    592 				tape = iotape;
    593 			putchar(*s ^ *tape);
    594 		} while ((*s++ ^ *tape++) != LF); /* better end with LF   */
    595 		if (skip < 0)
    596 			break;
    597 	}
    598 	free(tbuf);
    599 }
    600