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