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