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