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