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