d5bb247a7a47dfd733a865755700be0f42dcf42b
[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  * @(#)io.c     8.1 (Berkeley) 5/31/93
39  * $FreeBSD: src/games/adventure/io.c,v 1.8.2.1 2001/03/05 11:43:11 kris Exp $
40  * $DragonFly: src/games/adventure/io.c,v 1.3 2007/04/18 18:32:12 swildner Exp $
41  */
42
43 /*      Re-coding of advent in C: file i/o and user i/o                 */
44
45 #include "hdr.h"
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <err.h>
50
51 static int next (void);
52 static int rnum (void);
53 static void rdesc (int);
54 static void rdflt (void);
55 static void rhints (void);
56 static void rliq (void);
57 static void rlocs (void);
58 static void rtrav (void);
59 static void rvoc (void);
60 #ifdef DEBUG
61 static void twrite (int);
62 #endif
63
64 /* get command from user        */
65 /* no prompt, usually           */
66 void
67 getin(char **wrd1, char **wrd2)
68 {       char *s;
69         static char wd1buf[MAXSTR],wd2buf[MAXSTR];
70         int first, numch;
71
72         *wrd1=wd1buf;                   /* return ptr to internal string*/
73         *wrd2=wd2buf;
74         wd2buf[0]=0;                    /* in case it isn't set here    */
75         for (s=wd1buf, first=1, numch=0;;)
76         {       if ((*s=getchar())>='A' && *s <='Z') *s = *s - ('A' -'a');
77                                         /* convert to upper case        */
78                 switch(*s)              /* start reading from user      */
79                 {   case '\n':
80                         *s=0;
81                         return;
82                     case ' ':
83                         if (s==wd1buf||s==wd2buf)  /* initial blank   */
84                                 continue;
85                         *s=0;
86                         if (first)      /* finished 1st wd; start 2nd   */
87                         {       first=numch=0;
88                                 s=wd2buf;
89                                 break;
90                         }
91                         else            /* finished 2nd word            */
92                         {       FLUSHLINE;
93                                 *s=0;
94                                 return;
95                         }
96                     case EOF:
97                         printf("user closed input stream, quitting...\n");
98                         exit(0);
99                     default:
100                         if (++numch>=MAXSTR)    /* string too long      */
101                         {       printf("Give me a break!!\n");
102                                 wd1buf[0]=wd2buf[0]=0;
103                                 FLUSHLINE;
104                                 return;
105                         }
106                         s++;
107                 }
108         }
109 }
110
111 /* confirm with rspeak          */
112 int
113 yes(int x, int y, int z)
114 {       int result;
115         int ch;
116
117         result = FALSE;
118         for (;;)
119         {       rspeak(x);                     /* tell him what we want*/
120                 if ((ch=getchar())=='y')
121                         result=TRUE;
122                 else if (ch=='n') result=FALSE;
123                 else if (ch == EOF) {
124                         printf("user closed input stream, quitting...\n");
125                         exit(0);
126                 }
127                 FLUSHLINE;
128                 if (ch=='y'|| ch=='n') break;
129                 printf("Please answer the question.\n");
130         }
131         if (result==TRUE) rspeak(y);
132         if (result==FALSE) rspeak(z);
133         return(result);
134 }
135
136 /* confirm with mspeak          */
137 int
138 yesm(int x, int y, int z)
139 {       int result;
140         int ch;
141
142         result = FALSE;
143         for (;;)
144         {       mspeak(x);                     /* tell him what we want*/
145                 if ((ch=getchar())=='y')
146                         result=TRUE;
147                 else if (ch=='n') result=FALSE;
148                 else if (ch == EOF) {
149                         printf("user closed input stream, quitting...\n");
150                         exit(0);
151                 }
152                 FLUSHLINE;
153                 if (ch=='y'|| ch=='n') break;
154                 printf("Please answer the question.\n");
155         }
156         if (result==TRUE) mspeak(y);
157         if (result==FALSE) mspeak(z);
158         return(result);
159 }
160
161 /* FILE *inbuf,*outbuf; */
162
163 char *inptr;                            /* Pointer into virtual disk    */
164
165 int outsw = 0;                          /* putting stuff to data file?  */
166
167 const char iotape[] = "Ax3F'\003tt$8h\315qer*h\017nGKrX\207:!l";
168 const char *tape = iotape;              /* pointer to encryption tape   */
169
170 /* next virtual char, bump adr  */
171 static int
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) tape=iotape;  /* rewind encryption tape       */
179             *inptr = ch ^ *tape++;      /* re-encrypt and replace value */
180         }
181         inptr++;
182         return(ch);
183 }
184
185 char breakch;                           /* tell which char ended rnum   */
186
187 /* "read" data from virtual file*/
188 void
189 rdata(void)
190 {       int sect;
191         char ch;
192
193         inptr = data_file;              /* Pointer to virtual data file */
194         srandom(SEED);                  /* which is lightly encrypted.  */
195
196         clsses=1;
197         for (;;)                        /* read data sections           */
198         {       sect=next()-'0';        /* 1st digit of section number  */
199 #ifdef VERBOSE
200                 printf("Section %c",sect+'0');
201 #endif
202                 if ((ch=next())!=LF)    /* is there a second digit?     */
203                 {
204                         FLUSHLF;
205 #ifdef VERBOSE
206                         putchar(ch);
207 #endif
208                         sect=10*sect+ch-'0';
209                 }
210 #ifdef VERBOSE
211                 putchar('\n');
212 #endif
213                 switch(sect)
214                 {   case 0:             /* finished reading database    */
215                         return;
216                     case 1:             /* long form descriptions       */
217                         rdesc(1);
218                         break;
219                     case 2:             /* short form descriptions      */
220                         rdesc(2);
221                         break;
222                     case 3:             /* travel table                 */
223                         rtrav();   break;
224                     case 4:             /* vocabulary                   */
225                         rvoc();
226                         break;
227                     case 5:             /* object descriptions          */
228                         rdesc(5);
229                         break;
230                     case 6:             /* arbitrary messages           */
231                         rdesc(6);
232                         break;
233                     case 7:             /* object locations             */
234                         rlocs();   break;
235                     case 8:             /* action defaults              */
236                         rdflt();   break;
237                     case 9:             /* liquid assets                */
238                         rliq();    break;
239                     case 10:            /* class messages               */
240                         rdesc(10);
241                         break;
242                     case 11:            /* hints                        */
243                         rhints();  break;
244                     case 12:            /* magic messages               */
245                         rdesc(12);
246                         break;
247                     default:
248                         printf("Invalid data section number: %d\n",sect);
249                         for (;;) putchar(next());
250                 }
251                 if (breakch!=LF)        /* routines return after "-1"   */
252                         FLUSHLF;
253         }
254 }
255
256 char nbf[12];
257
258
259 /* read initial location num    */
260 static int
261 rnum(void)
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 /* read description-format msgs */
276 static void
277 rdesc(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 /* read travel table            */
341 static void
342 rtrav(void)
343 {       int locc;
344         struct travlist *t;
345         char *s;
346         char buf[12];
347         int len,m,n,entries;
348
349         entries = 0;
350         t = NULL;
351         for (oldloc= -1;;)              /* get another line             */
352         {       if ((locc=rnum())!=oldloc && oldloc>=0) /* end of entry */
353                 {
354                         t->next = 0;    /* terminate the old entry      */
355 #if DEBUG
356                         printf("%d:%d entries\n",oldloc,entries);
357                         twrite(oldloc);
358 #endif
359                 }
360                 if (locc== -1) return;
361                 if (locc!=oldloc)        /* getting a new entry         */
362                 {       t=travel[locc]=(struct travlist *) malloc(sizeof (struct travlist));
363                 /*      printf("New travel list for %d\n",locc);        */
364                         if (t == NULL)
365                                 errx(1, "Out of memory!");
366                         entries=0;
367                         oldloc=locc;
368                 }
369                 s = buf;
370                 for (;; s++)      /* get the newloc number /ASCII */
371                         if ((*s=next())==TAB || *s==LF) break;
372                 *s=0;
373                 len=strlen(buf);      /* quad long number handling    */
374         /*      printf("Newloc: %s (%d chars)\n",buf,len);              */
375                 if (len<4)              /* no "m" conditions            */
376                 {       m=0;
377                         n=atoi(buf);    /* newloc mod 1000 = newloc     */
378                 }
379                 else                    /* a long integer               */
380                 {       n=atoi(buf+len-3);
381                         buf[len-3]=0;   /* terminate newloc/1000        */
382                         m=atoi(buf);
383                 }
384                 while (breakch!=LF)     /* only do one line at a time   */
385                 {       if (entries++) {
386                                 t=t->next=(struct travlist *) malloc(sizeof (struct travlist));
387                                 if (t == NULL)
388                                         errx(1, "Out of memory!");
389                         }
390                         t->tverb=rnum();/* get verb from the file       */
391                         t->tloc=n;      /* table entry mod 1000         */
392                         t->conditions=m;/* table entry / 1000           */
393                 /*      printf("entry %d for %d\n",entries,locc);       */
394                 }
395         }
396 }
397
398 #ifdef DEBUG
399
400 /* travel options from this loc */
401 static void
402 twrite(int loq)
403 {       struct travlist *t;
404         printf("If");
405         speak(&ltext[loq]);
406         printf("then\n");
407         for (t=travel[loq]; t!=0; t=t->next)
408         {       printf("verb %d takes you to ",t->tverb);
409                 if (t->tloc<=300)
410                         speak(&ltext[t->tloc]);
411                 else if (t->tloc<=500)
412                         printf("special code %d\n",t->tloc-300);
413                 else
414                         rspeak(t->tloc-500);
415                 printf("under conditions %d\n",t->conditions);
416         }
417 }
418
419 #endif /* DEBUG */
420
421 static void
422 rvoc(void)
423 {       char *s;               /* read the vocabulary          */
424         int rv_index;
425         char buf[6];
426         for (;;)
427         {       rv_index=rnum();
428                 if (rv_index<0) break;
429                 for (s=buf,*s=0;; s++)  /* get the word                 */
430                         if ((*s=next())==TAB || *s=='\n' || *s==LF
431                                 || *s==' ') break;
432                         /* terminate word with newline, LF, tab, blank  */
433                 if (*s!='\n' && *s!=LF) FLUSHLF;  /* can be comments    */
434                 *s=0;
435         /*      printf("\"%s\"=%d\n",buf,index);*/
436                 vocab(buf,-2,rv_index);
437         }
438 }
439
440
441 /* initial object locations     */
442 static void
443 rlocs(void)
444 {       for (;;)
445         {       if ((obj=rnum())<0) break;
446                 plac[obj]=rnum();       /* initial loc for this obj     */
447                 if (breakch==TAB)       /* there's another entry        */
448                         fixd[obj]=rnum();
449                 else    fixd[obj]=0;
450         }
451 }
452
453 /* default verb messages        */
454 static void
455 rdflt(void)
456 {       for (;;)
457         {       if ((verb=rnum())<0) break;
458                 actspk[verb]=rnum();
459         }
460 }
461
462 /* liquid assets &c: cond bits  */
463 static void
464 rliq(void)
465 {       int bitnum;
466         for (;;)                        /* read new bit list            */
467         {       if ((bitnum=rnum())<0) break;
468                 for (;;)                /* read locs for bits           */
469                 {       cond[rnum()] |= setbit[bitnum];
470                         if (breakch==LF) break;
471                 }
472         }
473 }
474
475 static void
476 rhints(void)
477 {       int hintnum,i;
478         hntmax=0;
479         for (;;)
480         {       if ((hintnum=rnum())<0) break;
481                 for (i=1; i<5; i++)
482                         hints[hintnum][i]=rnum();
483                 if (hintnum>hntmax) hntmax=hintnum;
484         }
485 }
486
487
488 void
489 rspeak(int msg)
490 {       if (msg!=0) speak(&rtext[msg]);
491 }
492
493
494 void
495 mspeak(int msg)
496 {       if (msg!=0) speak(&mtext[msg]);
497 }
498
499
500 /* read, decrypt, and print a message (not ptext)      */
501 /* msg is a pointer to seek address and length of mess */
502 void
503 speak(const struct text *msg)
504 {
505         char *s, nonfirst;
506
507         s = msg->seekadr;
508         nonfirst=0;
509         while (s - msg->seekadr < msg->txtlen)  /* read a line at a time */
510         {       tape=iotape;            /* restart decryption tape      */
511                 while ((*s++ ^ *tape++) != TAB); /* read past loc num       */
512                 /* assume tape is longer than location number           */
513                 /*   plus the lookahead put together                    */
514                 if ((*s ^ *tape) == '>' &&
515                         (*(s+1) ^ *(tape+1)) == '$' &&
516                         (*(s+2) ^ *(tape+2)) == '<') break;
517                 if (blklin && !nonfirst++) putchar('\n');
518                 do
519                 {       if (*tape == 0) tape = iotape;/* rewind decryp tape */
520                         putchar(*s ^ *tape);
521                 } while ((*s++ ^ *tape++) != LF);   /* better end with LF   */
522         }
523 }
524
525
526 /* read, decrypt an print a ptext message              */
527 /* msg is the number of all the p msgs for this place  */
528 /* assumes object 1 doesn't have prop 1, obj 2 no prop 2 &c*/
529 void
530 pspeak(int m, int skip)
531 {
532         char *s,nonfirst;
533         char *numst, ps_save;
534         struct text *msg;
535         char *tbuf;
536
537         msg = &ptext[m];
538         if ((tbuf=(char *) malloc(msg->txtlen + 1)) == 0)
539                 errx(1, "Out of memory!");
540         memcpy(tbuf, msg->seekadr, (u_int)msg->txtlen + 1);   /* Room to null */
541         s = tbuf;
542
543         nonfirst=0;
544         while (s - tbuf < msg->txtlen) /* read line at a time */
545         {       tape=iotape;            /* restart decryption tape      */
546                 for (numst=s; (*s^= *tape++)!=TAB; s++); /* get number  */
547
548                 ps_save = *s; /* Temporarily trash the string (cringe) */
549                 *s++ = 0; /* decrypting number within the string          */
550
551                 if (atoi(numst) != 100 * skip && skip >= 0)
552                 {       while ((*s++^*tape++)!=LF) /* flush the line    */
553                                 if (*tape==0) tape=iotape;
554                         continue;
555                 }
556                 if ((*s^*tape)=='>' && (*(s+1)^*(tape+1))=='$' &&
557                         (*(s+2)^*(tape+2))=='<') break;
558                 if (blklin && ! nonfirst++) putchar('\n');
559                 do
560                 {       if (*tape==0) tape=iotape;
561                         putchar(*s^*tape);
562                 } while ((*s++^*tape++)!=LF);   /* better end with LF   */
563                 if (skip<0) break;
564         }
565         free(tbuf);
566 }