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