Home | History | Annotate | Line # | Download | only in adventure
      1 /*	$NetBSD: io.c,v 1.23 2021/05/02 12:50:43 rillig 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.23 2021/05/02 12:50:43 rillig 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 static int next(void);
     56 static void rdesc(int);
     57 static void rdefault(void);
     58 static void rhints(void);
     59 static void rliq(void);
     60 static void rlocs(void);
     61 static int rnum(void);
     62 static void rtrav(void);
     63 static void rvoc(void);
     64 
     65 /* get command from user        */
     66 /* no prompt, usually           */
     67 void
     68 getin(char **wrd1, char **wrd2)
     69 {
     70 	char   *s;
     71 	static char wd1buf[MAXSTR], wd2buf[MAXSTR];
     72 	int     first, numch, c;
     73 
     74 	*wrd1 = wd1buf;				/* return ptr to internal str */
     75 	*wrd2 = wd2buf;
     76 	wd2buf[0] = 0;				/* in case it isn't set here */
     77 	for (s = wd1buf, first = 1, numch = 0;;) {
     78 		c = getchar();
     79 		if ((*s = (char)c) >= 'A' && *s <= 'Z')
     80 			*s = *s - ('A' - 'a');
     81 		/* convert to upper case */
     82 		switch (c) {			/* start reading from user */
     83 		case '\n':
     84 			*s = 0;
     85 			return;
     86 		case ' ':
     87 			if (s == wd1buf || s == wd2buf)	/* initial blank */
     88 				continue;
     89 			*s = 0;
     90 			if (first) {		/* finished 1st wd; start 2nd */
     91 				first = numch = 0;
     92 				s = wd2buf;
     93 				break;
     94 			} else {		/* finished 2nd word */
     95 				FLUSHLINE;
     96 				*s = 0;
     97 				return;
     98 			}
     99 		case EOF:
    100 			printf("user closed input stream, quitting...\n");
    101 			exit(0);
    102 		default:
    103 			if (++numch >= MAXSTR) {	/* string too long */
    104 				printf("Give me a break!!\n");
    105 				wd1buf[0] = wd2buf[0] = 0;
    106 				FLUSHLINE;
    107 				return;
    108 			}
    109 			s++;
    110 		}
    111 	}
    112 }
    113 
    114 /* confirm with rspeak          */
    115 int
    116 yes(int x, int y, int z)
    117 {
    118 	int     result = TRUE;	/* pacify gcc */
    119 	int    ch;
    120 	for (;;) {
    121 		rspeak(x);	/* tell him what we want */
    122 		if ((ch = getchar()) == 'y')
    123 			result = TRUE;
    124 		else if (ch == 'n')
    125 			result = FALSE;
    126 		else if (ch == EOF) {
    127 			printf("user closed input stream, quitting...\n");
    128 			exit(0);
    129 		}
    130 		FLUSHLINE;
    131 		if (ch == 'y' || ch == 'n')
    132 			break;
    133 		printf("Please answer the question.\n");
    134 	}
    135 	if (result == TRUE)
    136 		rspeak(y);
    137 	if (result == FALSE)
    138 		rspeak(z);
    139 	return (result);
    140 }
    141 
    142 /* confirm with mspeak          */
    143 int
    144 yesm(int x, int y, int z)
    145 {
    146 	int     result = TRUE;	/* pacify gcc */
    147 	int    ch;
    148 	for (;;) {
    149 		mspeak(x);	/* tell him what we want */
    150 		if ((ch = getchar()) == 'y')
    151 			result = TRUE;
    152 		else if (ch == 'n')
    153 			result = FALSE;
    154 		else if (ch == EOF) {
    155 			printf("user closed input stream, quitting...\n");
    156 			exit(0);
    157 		}
    158 		FLUSHLINE;
    159 		if (ch == 'y' || ch == 'n')
    160 			break;
    161 		printf("Please answer the question.\n");
    162 	}
    163 	if (result == TRUE)
    164 		mspeak(y);
    165 	if (result == FALSE)
    166 		mspeak(z);
    167 	return (result);
    168 }
    169 /* FILE *inbuf,*outbuf; */
    170 
    171 static char *inptr;		/* Pointer into virtual disk    */
    172 
    173 static int outsw = 0;		/* putting stuff to data file?  */
    174 
    175 static const char    iotape[] = "Ax3F'\003tt$8h\315qer*h\017nGKrX\207:!l";
    176 static const char   *tape = iotape;	/* pointer to encryption tape   */
    177 
    178 /* next virtual char, bump adr  */
    179 static int
    180 next(void)
    181 {
    182 	int     ch;
    183 
    184 	ch = (*inptr ^ random()) & 0xFF;	/* Decrypt input data  */
    185 	if (outsw) {		/* putting data in tmp file     */
    186 		if (*tape == 0)
    187 			tape = iotape;	/* rewind encryption tape       */
    188 		*inptr = ch ^ *tape++;	/* re-encrypt and replace value */
    189 	}
    190 	inptr++;
    191 	return (ch);
    192 }
    193 
    194 static char breakch;		/* tell which char ended rnum   */
    195 
    196 /* "read" data from virtual file */
    197 void
    198 rdata(void)
    199 {
    200 	int     sect;
    201 	char    ch;
    202 
    203 	inptr = data_file;	/* Pointer to virtual data file */
    204 	srandom(SEED);		/* which is lightly encrypted.  */
    205 
    206 	classes = 1;
    207 	for (;;) {		/* read data sections           */
    208 		sect = next() - '0';	/* 1st digit of section number  */
    209 #ifdef VERBOSE
    210 		printf("Section %c", sect + '0');
    211 #endif
    212 		if ((ch = next()) != LF) {	/* is there a second digit?     */
    213 			FLUSHLF;
    214 #ifdef VERBOSE
    215 			putchar(ch);
    216 #endif
    217 			sect = 10 * sect + ch - '0';
    218 		}
    219 #ifdef VERBOSE
    220 		putchar('\n');
    221 #endif
    222 		switch (sect) {
    223 		case 0:	/* finished reading database    */
    224 			return;
    225 		case 1:	/* long form descriptions       */
    226 			rdesc(1);
    227 			break;
    228 		case 2:	/* short form descriptions      */
    229 			rdesc(2);
    230 			break;
    231 		case 3:	/* travel table                 */
    232 			rtrav();
    233 			break;
    234 		case 4:	/* vocabulary                   */
    235 			rvoc();
    236 			break;
    237 		case 5:	/* object descriptions          */
    238 			rdesc(5);
    239 			break;
    240 		case 6:	/* arbitrary messages           */
    241 			rdesc(6);
    242 			break;
    243 		case 7:	/* object locations             */
    244 			rlocs();
    245 			break;
    246 		case 8:	/* action defaults              */
    247 			rdefault();
    248 			break;
    249 		case 9:	/* liquid assets                */
    250 			rliq();
    251 			break;
    252 		case 10:	/* class messages               */
    253 			rdesc(10);
    254 			break;
    255 		case 11:	/* hints                        */
    256 			rhints();
    257 			break;
    258 		case 12:	/* magic messages               */
    259 			rdesc(12);
    260 			break;
    261 		default:
    262 			printf("Invalid data section number: %d\n", sect);
    263 			for (;;)
    264 				putchar(next());
    265 		}
    266 		if (breakch != LF)	/* routines return after "-1"   */
    267 			FLUSHLF;
    268 	}
    269 }
    270 
    271 static char nbf[12];
    272 
    273 /* read initial location num    */
    274 static int
    275 rnum(void)
    276 {
    277 	char   *s;
    278 	tape = iotape;		/* restart encryption tape      */
    279 	for (s = nbf, *s = 0;; s++)
    280 		if ((*s = next()) == TAB || *s == '\n' || *s == LF)
    281 			break;
    282 	breakch = *s;		/* save char for rtrav()        */
    283 	*s = 0;			/* got the number as ascii      */
    284 	if (nbf[0] == '-')
    285 		return (-1);	/* end of data                  */
    286 	return (atoi(nbf));	/* convert it to integer        */
    287 }
    288 
    289 static char *seekhere;
    290 
    291 /* read description-format msgs */
    292 static void
    293 rdesc(int sect)
    294 {
    295 	int     locc;
    296 	char   *seekstart, *maystart;
    297 
    298 	seekhere = inptr;	/* Where are we in virtual file? */
    299 	outsw = 1;		/* these msgs go into tmp file  */
    300 	for (oldloc = -1, seekstart = seekhere;;) {
    301 		maystart = inptr;	/* maybe starting new entry     */
    302 		if ((locc = rnum()) != oldloc && oldloc >= 0 /* finished msg */
    303   		    /* unless sect 5 */
    304 		    && !(sect == 5 && (locc == 0 || locc >= 100))) {
    305 			switch (sect) {	/* now put it into right table  */
    306 			case 1:/* long descriptions            */
    307 				ltext[oldloc].seekadr = seekhere;
    308 				ltext[oldloc].txtlen = maystart - seekstart;
    309 				break;
    310 			case 2:/* short descriptions           */
    311 				stext[oldloc].seekadr = seekhere;
    312 				stext[oldloc].txtlen = maystart - seekstart;
    313 				break;
    314 			case 5:/* object descriptions          */
    315 				ptext[oldloc].seekadr = seekhere;
    316 				ptext[oldloc].txtlen = maystart - seekstart;
    317 				break;
    318 			case 6:/* random messages              */
    319 				if (oldloc >= RTXSIZE)
    320 					errx(1,"Too many random msgs");
    321 				rtext[oldloc].seekadr = seekhere;
    322 				rtext[oldloc].txtlen = maystart - seekstart;
    323 				break;
    324 			case 10:	/* class messages               */
    325 				ctext[classes].seekadr = seekhere;
    326 				ctext[classes].txtlen = maystart - seekstart;
    327 				cval[classes++] = oldloc;
    328 				break;
    329 			case 12:	/* magic messages               */
    330 				if (oldloc >= MAGSIZE)
    331 					errx(1,"Too many magic msgs");
    332 				mtext[oldloc].seekadr = seekhere;
    333 				mtext[oldloc].txtlen = maystart - seekstart;
    334 				break;
    335 			default:
    336 				errx(1,"rdesc called with bad section");
    337 			}
    338 			seekhere += maystart - seekstart;
    339 		}
    340 		if (locc < 0) {
    341 			outsw = 0;	/* turn off output              */
    342 			seekhere += 3;	/* -1<delimiter>                */
    343 			return;
    344 		}
    345 		if (sect != 5 || (locc > 0 && locc < 100)) {
    346 			if (oldloc != locc)	/* starting a new message */
    347 				seekstart = maystart;
    348 			oldloc = locc;
    349 		}
    350 		FLUSHLF;	/* scan the line                */
    351 	}
    352 }
    353 
    354 /* read travel table            */
    355 static void
    356 rtrav(void)
    357 {
    358 	int     locc;
    359 	struct travlist *t = NULL;
    360 	char   *s;
    361 	char    buf[12];
    362 	int     len, m, n, entries = 0;
    363 
    364 	for (oldloc = -1;;) {	/* get another line             */
    365 		/* end of entry */
    366 		if ((locc = rnum()) != oldloc && oldloc >= 0 && t) {
    367 			t->next = 0;	/* terminate the old entry      */
    368 			/* printf("%d:%d entries\n",oldloc,entries);       */
    369 			/* twrite(oldloc);                                 */
    370 		}
    371 		if (locc == -1)
    372 			return;
    373 		if (locc != oldloc) {	/* getting a new entry         */
    374 			t = travel[locc] = calloc(1, sizeof(*t));
    375 			if (t == NULL)
    376 				err(1, NULL);
    377 			/* printf("New travel list for %d\n",locc);        */
    378 			entries = 0;
    379 			oldloc = locc;
    380 		}
    381 		for (s = buf;; s++)	/* get the newloc number /ASCII */
    382 			if ((*s = next()) == TAB || *s == LF)
    383 				break;
    384 		*s = 0;
    385 		len = length(buf) - 1;	/* quad long number handling    */
    386 		/* printf("Newloc: %s (%d chars)\n",buf,len);              */
    387 		if (len < 4) {	/* no "m" conditions            */
    388 			m = 0;
    389 			n = atoi(buf);	/* newloc mod 1000 = newloc     */
    390 		} else {	/* a long integer               */
    391 			n = atoi(buf + len - 3);
    392 			buf[len - 3] = 0;	/* terminate newloc/1000  */
    393 			m = atoi(buf);
    394 		}
    395 		while (breakch != LF) {	/* only do one line at a time   */
    396 			if (t == NULL)
    397 				abort();
    398 			if (entries++) {
    399 				t->next = calloc(1, sizeof(*t));
    400 				if (t->next == NULL)
    401 					err(1, NULL);
    402 				t = t->next;
    403 			}
    404 			t->tverb = rnum();	/* get verb from the file */
    405 			t->tloc = n;	/* table entry mod 1000         */
    406 			t->conditions = m;	/* table entry / 1000   */
    407 			/* printf("entry %d for %d\n",entries,locc);    */
    408 		}
    409 	}
    410 }
    411 #ifdef DEBUG
    412 
    413 /* travel options from this loc */
    414 void
    415 twrite(int loq)
    416 {
    417 	struct travlist *t;
    418 	printf("If");
    419 	speak(&ltext[loq]);
    420 	printf("then\n");
    421 	for (t = travel[loq]; t != 0; t = t->next) {
    422 		printf("verb %d takes you to ", t->tverb);
    423 		if (t->tloc <= 300)
    424 			speak(&ltext[t->tloc]);
    425 		else
    426 			if (t->tloc <= 500)
    427 				printf("special code %d\n", t->tloc - 300);
    428 			else
    429 				rspeak(t->tloc - 500);
    430 		printf("under conditions %d\n", t->conditions);
    431 	}
    432 }
    433 #endif				/* DEBUG */
    434 
    435 /* read the vocabulary          */
    436 static void
    437 rvoc(void)
    438 {
    439 	char   *s;
    440 	int     idx;
    441 	char    buf[6];
    442 	for (;;) {
    443 		idx = rnum();
    444 		if (idx < 0)
    445 			break;
    446 		for (s = buf, *s = 0;; s++)	/* get the word  */
    447 			if ((*s = next()) == TAB || *s == '\n' || *s == LF
    448 			    || *s == ' ')
    449 				break;
    450 		/* terminate word with newline, LF, tab, blank  */
    451 		if (*s != '\n' && *s != LF)
    452 			FLUSHLF;/* can be comments    */
    453 		*s = 0;
    454 		/* printf("\"%s\"=%d\n",buf,idx); */
    455 		vocab(buf, -2, idx);
    456 	}
    457 /*	prht();	*/
    458 }
    459 
    460 /* initial object locations     */
    461 static void
    462 rlocs(void)
    463 {
    464 	for (;;) {
    465 		if ((obj = rnum()) < 0)
    466 			break;
    467 		plac[obj] = rnum();	/* initial loc for this obj     */
    468 		if (breakch == TAB)	/* there's another entry        */
    469 			fixd[obj] = rnum();
    470 		else
    471 			fixd[obj] = 0;
    472 	}
    473 }
    474 
    475 /* default verb messages        */
    476 static void
    477 rdefault(void)
    478 {
    479 	for (;;) {
    480 		if ((verb = rnum()) < 0)
    481 			break;
    482 		actspeak[verb] = rnum();
    483 	}
    484 }
    485 
    486 /* liquid assets &c: cond bits  */
    487 static void
    488 rliq(void)
    489 {
    490 	int     bitnum;
    491 	for (;;) {		/* read new bit list            */
    492 		if ((bitnum = rnum()) < 0)
    493 			break;
    494 		for (;;) {	/* read locs for bits           */
    495 			int n = rnum();
    496 			if (n < 0)
    497 				break;
    498 			cond[n] |= setbit[bitnum];
    499 			if (breakch == LF)
    500 				break;
    501 		}
    502 	}
    503 }
    504 
    505 static void
    506 rhints(void)
    507 {
    508 	int     hintnum, i;
    509 	hintmax = 0;
    510 	for (;;) {
    511 		if ((hintnum = rnum()) < 0)
    512 			break;
    513 		for (i = 1; i < 5; i++)
    514 			hints[hintnum][i] = rnum();
    515 		if (hintnum > hintmax)
    516 			hintmax = hintnum;
    517 	}
    518 }
    519 
    520 
    521 void
    522 rspeak(int msg)
    523 {
    524 	if (msg != 0)
    525 		speak(&rtext[msg]);
    526 }
    527 
    528 
    529 void
    530 mspeak(int msg)
    531 {
    532 	if (msg != 0)
    533 		speak(&mtext[msg]);
    534 }
    535 
    536 
    537 /* read, decrypt, and print a message (not ptext)      */
    538 /* msg is a pointer to seek address and length of mess */
    539 void
    540 speak(const struct text *msg)
    541 {
    542 	char   *s, nonfirst;
    543 
    544 	s = msg->seekadr;
    545 	nonfirst = 0;
    546 	while (s - msg->seekadr < msg->txtlen) { /* read a line at a time */
    547 		tape = iotape;	/* restart decryption tape      */
    548 		while ((*s++ ^ *tape++) != TAB); /* read past loc num       */
    549 		/* assume tape is longer than location number           */
    550 		/* plus the lookahead put together                    */
    551 		if ((*s ^ *tape) == '>' &&
    552 		    (*(s + 1) ^ *(tape + 1)) == '$' &&
    553 		    (*(s + 2) ^ *(tape + 2)) == '<')
    554 			break;
    555 		if (blklin && !nonfirst++)
    556 			putchar('\n');
    557 		do {
    558 			if (*tape == 0)
    559 				tape = iotape;	/* rewind decryp tape */
    560 			putchar(*s ^ *tape);
    561 		} while ((*s++ ^ *tape++) != LF); /* better end with LF   */
    562 	}
    563 }
    564 
    565 /* read, decrypt and print a ptext message  */
    566 /* msg is the number of all the p msgs for this place  */
    567 /* assumes object 1 doesn't have prop 1, obj 2 no prop 2 &c */
    568 void
    569 pspeak(int m, int skip)
    570 {
    571 	char   *s, nonfirst;
    572 	char   *numst;
    573 	struct text *msg;
    574 	char   *tbuf;
    575 
    576 	msg = &ptext[m];
    577 	if ((tbuf = (char *) malloc(msg->txtlen + 1)) == NULL)
    578 		err(1, NULL);
    579 	memcpy(tbuf, msg->seekadr, msg->txtlen + 1);	/* Room to null */
    580 	s = tbuf;
    581 
    582 	nonfirst = 0;
    583 	while (s - tbuf < msg->txtlen) {	/* read line at a time */
    584 		tape = iotape;	/* restart decryption tape      */
    585 		for (numst = s; (*s ^= *tape++) != TAB; s++); /* get number  */
    586 
    587 				/* Temporarily trash the string (cringe) */
    588 		*s++ = 0;	/* decrypting number within the string   */
    589 
    590 		if (atoi(numst) != 100 * skip && skip >= 0) {
    591 			while ((*s++ ^ *tape++) != LF)	/* flush the line    */
    592 				if (*tape == 0)
    593 					tape = iotape;
    594 			continue;
    595 		}
    596 		if ((*s ^ *tape) == '>' && (*(s + 1) ^ *(tape + 1)) == '$' &&
    597 		    (*(s + 2) ^ *(tape + 2)) == '<')
    598 			break;
    599 		if (blklin && !nonfirst++)
    600 			putchar('\n');
    601 		do {
    602 			if (*tape == 0)
    603 				tape = iotape;
    604 			putchar(*s ^ *tape);
    605 		} while ((*s++ ^ *tape++) != LF); /* better end with LF   */
    606 		if (skip < 0)
    607 			break;
    608 	}
    609 	free(tbuf);
    610 }
    611