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