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