nrelease - fix/improve livecd
[dragonfly.git] / games / adventure / io.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
514 rspeak(int msg)
515 {
516         if (msg != 0)
517                 speak(&rtext[msg]);
518 }
519
520
521 void
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
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
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 }