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