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