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