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