xref: /original-bsd/games/adventure/io.c (revision 8dd2e574)
1 /*-
2  * Copyright (c) 1991, 1993 The Regents of the University of California.
3  * 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  * %sccs.include.redist.c%
11  */
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)io.c	5.2 (Berkeley) 05/12/93";
15 #endif /* not lint */
16 
17 /*      Re-coding of advent in C: file i/o and user i/o                 */
18 
19 #include "hdr.h"
20 #include <stdio.h>
21 
22 
23 getin(wrd1,wrd2)                        /* get command from user        */
24 char **wrd1,**wrd2;                     /* no prompt, usually           */
25 {       register char *s;
26 	static char wd1buf[MAXSTR],wd2buf[MAXSTR];
27 	int first, numch;
28 
29 	*wrd1=wd1buf;                   /* return ptr to internal string*/
30 	*wrd2=wd2buf;
31 	wd2buf[0]=0;                    /* in case it isn't set here    */
32 	for (s=wd1buf, first=1, numch=0;;)
33 	{       if ((*s=getchar())>='A' && *s <='Z') *s = *s - ('A' -'a');
34 					/* convert to upper case        */
35 		switch(*s)              /* start reading from user      */
36 		{   case '\n':
37 			*s=0;
38 			return;
39 		    case ' ':
40 			if (s==wd1buf||s==wd2buf)  /* initial blank   */
41 				continue;
42 			*s=0;
43 			if (first)      /* finished 1st wd; start 2nd   */
44 			{       first=numch=0;
45 				s=wd2buf;
46 				break;
47 			}
48 			else            /* finished 2nd word            */
49 			{       FLUSHLINE;
50 				*s=0;
51 				return;
52 			}
53 		    default:
54 			if (++numch>=MAXSTR)    /* string too long      */
55 			{       printf("Give me a break!!\n");
56 				wd1buf[0]=wd2buf[0]=0;
57 				FLUSHLINE;
58 				return;
59 			}
60 			s++;
61 		}
62 	}
63 }
64 
65 
66 confirm(mesg)                           /* confirm irreversible action  */
67 char *mesg;
68 {       register int result;
69 	printf("%s",mesg);              /* tell him what he did         */
70 	if (getchar()=='y')             /* was his first letter a 'y'?  */
71 		result=1;
72 	else    result=0;
73 	FLUSHLINE;
74 	return(result);
75 }
76 
77 yes(x,y,z)                              /* confirm with rspeak          */
78 int x,y,z;
79 {       register int result;
80 	register char ch;
81 	for (;;)
82 	{       rspeak(x);                     /* tell him what we want*/
83 		if ((ch=getchar())=='y')
84 			result=TRUE;
85 		else if (ch=='n') result=FALSE;
86 		FLUSHLINE;
87 		if (ch=='y'|| ch=='n') break;
88 		printf("Please answer the question.\n");
89 	}
90 	if (result==TRUE) rspeak(y);
91 	if (result==FALSE) rspeak(z);
92 	return(result);
93 }
94 
95 yesm(x,y,z)                             /* confirm with mspeak          */
96 int x,y,z;
97 {       register int result;
98 	register char ch;
99 	for (;;)
100 	{       mspeak(x);                     /* tell him what we want*/
101 		if ((ch=getchar())=='y')
102 			result=TRUE;
103 		else if (ch=='n') result=FALSE;
104 		FLUSHLINE;
105 		if (ch=='y'|| ch=='n') break;
106 		printf("Please answer the question.\n");
107 	}
108 	if (result==TRUE) mspeak(y);
109 	if (result==FALSE) mspeak(z);
110 	return(result);
111 }
112 
113 /* FILE *inbuf,*outbuf; */
114 
115 char *inptr;                            /* Pointer into virtual disk    */
116 
117 int outsw = 0;				/* putting stuff to data file?  */
118 
119 char iotape[] = "Ax3F'\003tt$8h\315qer*h\017nGKrX\207:!l";
120 char *tape = iotape;			/* pointer to encryption tape   */
121 
122 next()                                  /* next virtual char, bump adr  */
123 {
124 	int ch;
125 
126 	ch=(*inptr ^ random()) & 0xFF;  /* Decrypt input data           */
127 	if (outsw)                      /* putting data in tmp file     */
128 	{   if (*tape==0) tape=iotape;  /* rewind encryption tape       */
129 	    *inptr = ch ^ *tape++;      /* re-encrypt and replace value */
130 	}
131 	inptr++;
132 	return(ch);
133 }
134 
135 char breakch;                           /* tell which char ended rnum   */
136 
137 rdata()                                 /* "read" data from virtual file*/
138 {       register int sect;
139 	register char ch;
140 
141 	inptr = data_file;              /* Pointer to virtual data file */
142 	srandom(SEED);                  /* which is lightly encrypted.  */
143 
144 	clsses=1;
145 	for (;;)                        /* read data sections           */
146 	{       sect=next()-'0';        /* 1st digit of section number  */
147 #ifdef VERBOSE
148 		printf("Section %c",sect+'0');
149 #endif
150 		if ((ch=next())!=LF)    /* is there a second digit?     */
151 		{
152 			FLUSHLF;
153 #ifdef VERBOSE
154 			putchar(ch);
155 #endif
156 			sect=10*sect+ch-'0';
157 		}
158 #ifdef VERBOSE
159 		putchar('\n');
160 #endif
161 		switch(sect)
162 		{   case 0:             /* finished reading database    */
163 			return;
164 		    case 1:             /* long form descriptions       */
165 			rdesc(1);
166 			break;
167 		    case 2:             /* short form descriptions      */
168 			rdesc(2);
169 			break;
170 		    case 3:             /* travel table                 */
171 			rtrav();   break;
172 		    case 4:             /* vocabulary                   */
173 			rvoc();
174 			break;
175 		    case 5:             /* object descriptions          */
176 			rdesc(5);
177 			break;
178 		    case 6:             /* arbitrary messages           */
179 			rdesc(6);
180 			break;
181 		    case 7:             /* object locations             */
182 			rlocs();   break;
183 		    case 8:             /* action defaults              */
184 			rdflt();   break;
185 		    case 9:             /* liquid assets                */
186 			rliq();    break;
187 		    case 10:            /* class messages               */
188 			rdesc(10);
189 			break;
190 		    case 11:            /* hints                        */
191 			rhints();  break;
192 		    case 12:            /* magic messages               */
193 			rdesc(12);
194 			break;
195 		    default:
196 			printf("Invalid data section number: %d\n",sect);
197 			for (;;) putchar(next());
198 		}
199 		if (breakch!=LF)        /* routines return after "-1"   */
200 			FLUSHLF;
201 	}
202 }
203 
204 char nbf[12];
205 
206 
207 rnum()                                  /* read initial location num    */
208 {       register char *s;
209 	tape = iotape;                  /* restart encryption tape      */
210 	for (s=nbf,*s=0;; s++)
211 		if ((*s=next())==TAB || *s=='\n' || *s==LF)
212 			break;
213 	breakch= *s;                    /* save char for rtrav()        */
214 	*s=0;                           /* got the number as ascii      */
215 	if (nbf[0]=='-') return(-1);    /* end of data                  */
216 	return(atoi(nbf));              /* convert it to integer        */
217 }
218 
219 char *seekhere;
220 
221 rdesc(sect)                             /* read description-format msgs */
222 int sect;
223 {       register char *s,*t;
224 	register int locc;
225 	char *seekstart, *maystart, *adrstart;
226 	char *entry;
227 
228 	seekhere = inptr;               /* Where are we in virtual file?*/
229 	outsw=1;                        /* these msgs go into tmp file  */
230 	for (oldloc= -1, seekstart=seekhere;;)
231 	{       maystart=inptr;         /* maybe starting new entry     */
232 		if ((locc=rnum())!=oldloc && oldloc>=0  /* finished msg */
233 		    && ! (sect==5 && (locc==0 || locc>=100)))/* unless sect 5*/
234 		{       switch(sect)    /* now put it into right table  */
235 			{   case 1:     /* long descriptions            */
236 				ltext[oldloc].seekadr=seekhere;
237 				ltext[oldloc].txtlen=maystart-seekstart;
238 				break;
239 			    case 2:     /* short descriptions           */
240 				stext[oldloc].seekadr=seekhere;
241 				stext[oldloc].txtlen=maystart-seekstart;
242 				break;
243 			    case 5:     /* object descriptions          */
244 				ptext[oldloc].seekadr=seekhere;
245 				ptext[oldloc].txtlen=maystart-seekstart;
246 				break;
247 			    case 6:     /* random messages              */
248 				if (oldloc>RTXSIZ)
249 				{       printf("Too many random msgs\n");
250 					exit(0);
251 				}
252 				rtext[oldloc].seekadr=seekhere;
253 				rtext[oldloc].txtlen=maystart-seekstart;
254 				break;
255 			    case 10:    /* class messages               */
256 				ctext[clsses].seekadr=seekhere;
257 				ctext[clsses].txtlen=maystart-seekstart;
258 				cval[clsses++]=oldloc;
259 				break;
260 			    case 12:    /* magic messages               */
261 				if (oldloc>MAGSIZ)
262 				{       printf("Too many magic msgs\n");
263 					exit(0);
264 				}
265 				mtext[oldloc].seekadr=seekhere;
266 				mtext[oldloc].txtlen=maystart-seekstart;
267 				break;
268 			    default:
269 				printf("rdesc called with bad section\n");
270 				exit(0);
271 			}
272 			seekhere += maystart-seekstart;
273 		}
274 		if (locc<0)
275 		{       outsw=0;        /* turn off output              */
276 			seekhere += 3;  /* -1<delimiter>                */
277 			return;
278 		}
279 		if (sect!=5 || (locc>0 && locc<100))
280 		{       if (oldloc!=locc)/* starting a new message       */
281 				seekstart=maystart;
282 			oldloc=locc;
283 		}
284 		FLUSHLF;                /* scan the line                */
285 	}
286 }
287 
288 
289 rtrav()                                 /* read travel table            */
290 {       register int locc;
291 	register struct travlist *t;
292 	register char *s;
293 	char buf[12];
294 	int len,m,n,entries;
295 	for (oldloc= -1;;)              /* get another line             */
296 	{       if ((locc=rnum())!=oldloc && oldloc>=0) /* end of entry */
297 		{
298 			t->next = 0;    /* terminate the old entry      */
299 		/*      printf("%d:%d entries\n",oldloc,entries);       */
300 		/*      twrite(oldloc);                                 */
301 		}
302 		if (locc== -1) return;
303 		if (locc!=oldloc)        /* getting a new entry         */
304 		{       t=travel[locc]=(struct travlist *) malloc(sizeof (struct travlist));
305 		/*      printf("New travel list for %d\n",locc);        */
306 			entries=0;
307 			oldloc=locc;
308 		}
309 		for (s=buf;; *s++)      /* get the newloc number /ASCII */
310 			if ((*s=next())==TAB || *s==LF) break;
311 		*s=0;
312 		len=length(buf)-1;      /* quad long number handling    */
313 	/*      printf("Newloc: %s (%d chars)\n",buf,len);              */
314 		if (len<4)              /* no "m" conditions            */
315 		{       m=0;
316 			n=atoi(buf);    /* newloc mod 1000 = newloc     */
317 		}
318 		else                    /* a long integer               */
319 		{       n=atoi(buf+len-3);
320 			buf[len-3]=0;   /* terminate newloc/1000        */
321 			m=atoi(buf);
322 		}
323 		while (breakch!=LF)     /* only do one line at a time   */
324 		{       if (entries++) t=t->next=(struct travlist *) malloc(sizeof (struct travlist));
325 			t->tverb=rnum();/* get verb from the file       */
326 			t->tloc=n;      /* table entry mod 1000         */
327 			t->conditions=m;/* table entry / 1000           */
328 		/*      printf("entry %d for %d\n",entries,locc);       */
329 		}
330 	}
331 }
332 
333 #ifdef DEBUG
334 
335 twrite(loq)                             /* travel options from this loc */
336 int loq;
337 {       register struct travlist *t;
338 	printf("If");
339 	speak(&ltext[loq]);
340 	printf("then\n");
341 	for (t=travel[loq]; t!=0; t=t->next)
342 	{       printf("verb %d takes you to ",t->tverb);
343 		if (t->tloc<=300)
344 			speak(&ltext[t->tloc]);
345 		else if (t->tloc<=500)
346 			printf("special code %d\n",t->tloc-300);
347 		else
348 			rspeak(t->tloc-500);
349 		printf("under conditions %d\n",t->conditions);
350 	}
351 }
352 
353 #endif DEBUG
354 
355 rvoc()
356 {       register char *s;               /* read the vocabulary          */
357 	register int index;
358 	char buf[6];
359 	for (;;)
360 	{       index=rnum();
361 		if (index<0) break;
362 		for (s=buf,*s=0;; s++)  /* get the word                 */
363 			if ((*s=next())==TAB || *s=='\n' || *s==LF
364 				|| *s==' ') break;
365 			/* terminate word with newline, LF, tab, blank  */
366 		if (*s!='\n' && *s!=LF) FLUSHLF;  /* can be comments    */
367 		*s=0;
368 	/*      printf("\"%s\"=%d\n",buf,index);*/
369 		vocab(buf,-2,index);
370 	}
371 /*	prht();	*/
372 }
373 
374 
375 rlocs()                                 /* initial object locations     */
376 {	for (;;)
377 	{       if ((obj=rnum())<0) break;
378 		plac[obj]=rnum();       /* initial loc for this obj     */
379 		if (breakch==TAB)       /* there's another entry        */
380 			fixd[obj]=rnum();
381 		else    fixd[obj]=0;
382 	}
383 }
384 
385 rdflt()                                 /* default verb messages        */
386 {	for (;;)
387 	{       if ((verb=rnum())<0) break;
388 		actspk[verb]=rnum();
389 	}
390 }
391 
392 rliq()                                  /* liquid assets &c: cond bits  */
393 {       register int bitnum;
394 	for (;;)                        /* read new bit list            */
395 	{       if ((bitnum=rnum())<0) break;
396 		for (;;)                /* read locs for bits           */
397 		{       cond[rnum()] |= setbit[bitnum];
398 			if (breakch==LF) break;
399 		}
400 	}
401 }
402 
403 rhints()
404 {       register int hintnum,i;
405 	hntmax=0;
406 	for (;;)
407 	{       if ((hintnum=rnum())<0) break;
408 		for (i=1; i<5; i++)
409 			hints[hintnum][i]=rnum();
410 		if (hintnum>hntmax) hntmax=hintnum;
411 	}
412 }
413 
414 
415 rspeak(msg)
416 int msg;
417 {       if (msg!=0) speak(&rtext[msg]);
418 }
419 
420 
421 mspeak(msg)
422 int msg;
423 {       if (msg!=0) speak(&mtext[msg]);
424 }
425 
426 
427 speak(msg)       /* read, decrypt, and print a message (not ptext)      */
428 struct text *msg;/* msg is a pointer to seek address and length of mess */
429 {
430 	register char *s, nonfirst;
431 
432 	s = msg->seekadr;
433 	nonfirst=0;
434 	while (s - msg->seekadr < msg->txtlen)  /* read a line at a time */
435 	{       tape=iotape;            /* restart decryption tape      */
436 		while ((*s++ ^ *tape++) != TAB); /* read past loc num       */
437 		/* assume tape is longer than location number           */
438 		/*   plus the lookahead put together                    */
439 		if ((*s ^ *tape) == '>' &&
440 			(*(s+1) ^ *(tape+1)) == '$' &&
441 			(*(s+2) ^ *(tape+2)) == '<') break;
442 		if (blklin && !nonfirst++) putchar('\n');
443 		do
444 		{       if (*tape == 0) tape = iotape;/* rewind decryp tape */
445 			putchar(*s ^ *tape);
446 		} while ((*s++ ^ *tape++) != LF);   /* better end with LF   */
447 	}
448 }
449 
450 
451 pspeak(m,skip) /* read, decrypt an print a ptext message              */
452 int m;         /* msg is the number of all the p msgs for this place  */
453 int skip;       /* assumes object 1 doesn't have prop 1, obj 2 no prop 2 &c*/
454 {
455 	register char *s,nonfirst;
456 	char *numst, save;
457 	struct text *msg;
458 	char *tbuf;
459 
460 	msg = &ptext[m];
461 	if ((tbuf=(char *) malloc(msg->txtlen + 1)) == 0) bug(108);
462 	memcpy(tbuf, msg->seekadr, msg->txtlen + 1);   /* Room to null */
463 	s = tbuf;
464 
465 	nonfirst=0;
466 	while (s - tbuf < msg->txtlen) /* read line at a time */
467 	{       tape=iotape;            /* restart decryption tape      */
468 		for (numst=s; (*s^= *tape++)!=TAB; s++); /* get number  */
469 
470 		save = *s; /* Temporarily trash the string (cringe) */
471 		*s++ = 0; /* decrypting number within the string          */
472 
473 		if (atoi(numst) != 100 * skip && skip >= 0)
474 		{       while ((*s++^*tape++)!=LF) /* flush the line    */
475 				if (*tape==0) tape=iotape;
476 			continue;
477 		}
478 		if ((*s^*tape)=='>' && (*(s+1)^*(tape+1))=='$' &&
479 			(*(s+2)^*(tape+2))=='<') break;
480 		if (blklin && ! nonfirst++) putchar('\n');
481 		do
482 		{       if (*tape==0) tape=iotape;
483 			putchar(*s^*tape);
484 		} while ((*s++^*tape++)!=LF);   /* better end with LF   */
485 		if (skip<0) break;
486 	}
487 	free(tbuf);
488 }
489