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