/* $OpenBSD: io.c,v 1.21 2016/03/08 10:48:39 mestre Exp $ */ /* $NetBSD: io.c,v 1.3 1995/04/24 12:21:37 cgd Exp $ */ /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * The game adventure was originally written in Fortran by Will Crowther * and Don Woods. It was later translated to C and enhanced by Jim * Gillogly. This code is derived from software contributed to Berkeley * by Jim Gillogly at The Rand Corporation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* Re-coding of advent in C: file i/o and user i/o */ #include #include #include #include "extern.h" #include "hdr.h" /* Get command from user. No prompt, usually. */ void getin(char *wrd1, size_t siz1, char *wrd2, size_t siz2) { char *s, *slast; int ch, first; *wrd2 = 0; /* in case it isn't set here */ for (s = wrd1, first = 1, slast = wrd1 + siz1 - 1;;) { if ((ch = getchar()) >= 'A' && ch <= 'Z') ch = ch - ('A' - 'a'); /* convert to upper case */ switch (ch) { /* start reading from user */ case '\n': *s = 0; return; case ' ': if (s == wrd1 || s == wrd2) /* initial blank */ continue; *s = 0; if (first) { /* finished 1st wd; start 2nd */ first = 0; s = wrd2; slast = wrd2 + siz2 - 1; break; } else { /* finished 2nd word */ FLUSHLINE; *s = 0; return; } case EOF: printf("user closed input stream, quitting...\n"); exit(0); default: if (s == slast) { /* string too long */ printf("Give me a break!!\n"); *wrd1 = *wrd2 = 0; FLUSHLINE; return; } *s++ = ch; } } } #if 0 /* Not used */ int confirm(char *mesg) /* confirm irreversible action */ { int result; int ch; printf("%s", mesg); /* tell him what he did */ if ((ch = getchar()) == 'y') /* was his first letter a 'y'? */ result = 1; else if (ch == EOF) { printf("user closed input stream, quitting...\n"); exit(0); } else result = 0; FLUSHLINE; return (result); } #endif int yes(int x, int y, int z) /* confirm with rspeak */ { int result; int ch; for (;;) { rspeak(x); /* tell him what we want*/ if ((ch = getchar())=='y') result = TRUE; else if (ch=='n') result = FALSE; else if (ch == EOF) { printf("user closed input stream, quitting...\n"); exit(0); } if (ch != '\n') FLUSHLINE; if (ch == 'y' || ch == 'n') break; printf("Please answer the question.\n"); } if (result == TRUE) rspeak(y); if (result == FALSE) rspeak(z); return (result); } int yesm(int x, int y, int z) /* confirm with mspeak */ { int result; int ch; for (;;) { mspeak(x); /* tell him what we want */ if ((ch = getchar()) == 'y') result = TRUE; else if (ch == 'n') result = FALSE; else if (ch == EOF) { printf("user closed input stream, quitting...\n"); exit(0); } if (ch != '\n') FLUSHLINE; if (ch == 'y' || ch == 'n') break; printf("Please answer the question.\n"); } if (result == TRUE) mspeak(y); if (result == FALSE) mspeak(z); return (result); } /* FILE *inbuf,*outbuf; */ char *inptr; /* Pointer into virtual disk */ int outsw = 0; /* putting stuff to data file? */ const char iotape[] = "Ax3F'\003tt$8h\315qer*h\017nGKrX\207:!l"; const char *tape = iotape; /* pointer to obfuscation tape */ int next(void) /* next virtual char, bump adr */ { int ch; ch=(*inptr ^ random()) & 0xFF; /* Deobfuscate input data */ if (outsw) { /* putting data in tmp file */ if (*tape == 0) tape = iotape; /* rewind obfuscation tape */ *inptr = ch ^ *tape++; /* re-obfuscate and replace value */ } inptr++; return (ch); } char breakch; /* tell which char ended rnum */ void rdata(void) /* "read" data from virtual file */ { int sect; char ch; inptr = data_file; /* Pointer to virtual data file */ clsses = 1; for (;;) { /* read data sections */ sect = next() - '0'; /* 1st digit of section number */ #ifdef VERBOSE printf("Section %c", sect + '0'); #endif if ((ch = next()) != LF) { /* is there a second digit? */ FLUSHLF; #ifdef VERBOSE putchar(ch); #endif sect = 10 * sect + ch - '0'; } #ifdef VERBOSE putchar('\n'); #endif switch (sect) { case 0: /* finished reading database */ return; case 1: /* long form descriptions */ rdesc(1); break; case 2: /* short form descriptions */ rdesc(2); break; case 3: /* travel table */ rtrav(); break; case 4: /* vocabulary */ rvoc(); break; case 5: /* object descriptions */ rdesc(5); break; case 6: /* arbitrary messages */ rdesc(6); break; case 7: /* object locations */ rlocs(); break; case 8: /* action defaults */ rdflt(); break; case 9: /* liquid assets */ rliq(); break; case 10: /* class messages */ rdesc(10); break; case 11: /* hints */ rhints(); break; case 12: /* magic messages */ rdesc(12); break; default: printf("Invalid data section number: %d\n", sect); for (;;) putchar(next()); } if (breakch != LF) /* routines return after "-1" */ FLUSHLF; } } char nbf[12]; int rnum(void) /* read initial location num */ { char *s; tape = iotape; /* restart obfuscation tape */ for (s = nbf, *s = 0;; s++) if ((*s = next()) == TAB || *s == '\n' || *s == LF) break; breakch = *s; /* save char for rtrav() */ *s = 0; /* got the number as ascii */ if (nbf[0] == '-') return (-1); /* end of data */ return (atoi(nbf)); /* convert it to integer */ } char *seekhere; void rdesc(int sect) /* read description-format msgs */ { int locc; char *seekstart, *maystart; seekhere = inptr; /* Where are we in virtual file?*/ outsw = 1; /* these msgs go into tmp file */ for (oldloc = -1, seekstart = seekhere;;) { maystart = inptr; /* maybe starting new entry */ if ((locc = rnum()) != oldloc && oldloc >= 0 /* finished msg */ && !(sect == 5 && (locc == 0 || locc >= 100)))/* unless sect 5*/ { switch (sect) { /* now put it into right table */ case 1: /* long descriptions */ ltext[oldloc].seekadr = seekhere; ltext[oldloc].txtlen = maystart - seekstart; break; case 2: /* short descriptions */ stext[oldloc].seekadr = seekhere; stext[oldloc].txtlen = maystart - seekstart; break; case 5: /* object descriptions */ ptext[oldloc].seekadr = seekhere; ptext[oldloc].txtlen = maystart - seekstart; break; case 6: /* random messages */ if (oldloc >= RTXSIZ) errx(1, "Too many random msgs"); rtext[oldloc].seekadr = seekhere; rtext[oldloc].txtlen = maystart - seekstart; break; case 10:/* class messages */ ctext[clsses].seekadr = seekhere; ctext[clsses].txtlen = maystart - seekstart; cval[clsses++] = oldloc; break; case 12:/* magic messages */ if (oldloc >= MAGSIZ) errx(1, "Too many magic msgs"); mtext[oldloc].seekadr = seekhere; mtext[oldloc].txtlen = maystart - seekstart; break; default: errx(1, "rdesc called with bad section"); } seekhere += maystart - seekstart; } if (locc < 0) { outsw = 0; /* turn off output */ seekhere += 3; /* -1 */ return; } if (sect != 5 || (locc > 0 && locc < 100)) { if (oldloc != locc)/* starting a new message */ seekstart = maystart; oldloc = locc; } FLUSHLF; /* scan the line */ } } void rtrav(void) /* read travel table */ { int locc; struct travlist *t; char *s; char buf[12]; int len, m, n, entries; for (oldloc = -1;;) { /* get another line */ if ((locc = rnum()) != oldloc && oldloc >= 0) { /* end of entry */ t->next = NULL; /* terminate the old entry */ /* printf("%d:%d entries\n", oldloc, entries); */ /* twrite(oldloc); */ } if (locc == -1) return; if (locc != oldloc) { /* getting a new entry */ t = travel[locc] = calloc(1, sizeof(*t)); if (t == NULL) err(1, NULL); /* printf("New travel list for %d\n", locc); */ entries = 0; oldloc = locc; } for (s = buf; ; *s++) /* get the newloc number /ASCII */ if ((*s = next()) == TAB || *s == LF) break; *s = 0; len = length(buf) - 1; /* quad long number handling */ /* printf("Newloc: %s (%d chars)\n", buf, len); */ if (len < 4) { /* no "m" conditions */ m = 0; n = atoi(buf); /* newloc mod 1000 = newloc */ } else { /* a long integer */ n = atoi(buf + len - 3); buf[len - 3] = 0; /* terminate newloc/1000*/ m = atoi(buf); } while (breakch != LF) { /* only do one line at a time */ if (t == NULL) errx(1, "corrupt file"); if (entries++) { t->next = calloc(1, sizeof (*t->next)); if (t->next == NULL) err(1, NULL); t = t->next; } t->tverb = rnum();/* get verb from the file */ t->tloc = n; /* table entry mod 1000 */ t->conditions = m;/* table entry / 1000 */ /* printf("entry %d for %d\n", entries, locc); */ } } } #ifdef DEBUG void twrite(int loq) /* travel options from this loc */ { struct travlist *t; printf("If"); speak(<ext[loq]); printf("then\n"); for (t = travel[loq]; t != 0; t = t->next) { printf("verb %d takes you to ", t->tverb); if (t->tloc <= 300) speak(<ext[t->tloc]); else if (t->tloc <= 500) printf("special code %d\n", t->tloc - 300); else rspeak(t->tloc - 500); printf("under conditions %d\n", t->conditions); } } #endif /* DEBUG */ void rvoc(void) { char *s; /* read the vocabulary */ int index; char buf[6]; for (;;) { index = rnum(); if (index < 0) break; for (s = buf, *s = 0;; s++) /* get the word */ if ((*s = next()) == TAB || *s == '\n' || *s == LF || *s == ' ') break; /* terminate word with newline, LF, tab, blank */ if (*s != '\n' && *s != LF) FLUSHLF; /* can be comments */ *s = 0; /* printf("\"%s\"=%d\n", buf, index);*/ vocab(buf, -2, index); } /* prht(); */ } void rlocs(void) /* initial object locations */ { for (;;) { if ((obj = rnum()) < 0) break; plac[obj] = rnum(); /* initial loc for this obj */ if (breakch == TAB) /* there's another entry */ fixd[obj] = rnum(); else fixd[obj] = 0; } } void rdflt(void) /* default verb messages */ { for (;;) { if ((verb = rnum()) < 0) break; actspk[verb] = rnum(); } } void rliq(void) /* liquid assets &c: cond bits */ { int bitnum; for (;;) { /* read new bit list */ if ((bitnum = rnum()) < 0) break; for (;;) { /* read locs for bits */ cond[rnum()] |= setbit[bitnum]; if (breakch == LF) break; } } } void rhints(void) { int hintnum, i; hntmax = 0; for (;;) { if ((hintnum = rnum()) < 0) break; for (i = 1; i < 5; i++) hints[hintnum][i] = rnum(); if (hintnum > hntmax) hntmax = hintnum; } } void rspeak(int msg) { if (msg != 0) speak(&rtext[msg]); } void mspeak(int msg) { if (msg != 0) speak(&mtext[msg]); } /* * Read, deobfuscate, and print a message (not ptext) * msg is a pointer to seek address and length of mess */ void speak(const struct text *msg) { char *s, nonfirst; s = msg->seekadr; nonfirst = 0; while (s - msg->seekadr < msg->txtlen) { /* read a line at a time */ tape = iotape; /* restart deobfuscation tape */ while ((*s++ ^ *tape++) != TAB); /* read past loc num */ /* assume tape is longer than location number */ /* plus the lookahead put together */ if ((*s ^ *tape) == '>' && (*(s + 1) ^ *(tape + 1)) == '$' && (*(s + 2) ^ *(tape + 2)) == '<') break; if (blklin && !nonfirst++) putchar('\n'); do { if (*tape == 0) tape = iotape;/* rewind decryp tape */ putchar(*s ^ *tape); } while ((*s++ ^ *tape++) != LF); /* better end with LF */ } } /* * Read, deobfuscate, and print a ptext message * msg is the number of all the p msgs for this place * assumes object 1 doesn't have prop 1, obj 2 no prop 2 &c */ void pspeak(int m, int skip) { char *s, nonfirst; char *numst, save; struct text *msg; char *tbuf; msg = &ptext[m]; if ((tbuf = malloc(msg->txtlen + 1)) == 0) err(1, NULL); memcpy(tbuf, msg->seekadr, msg->txtlen + 1); /* Room to null */ s = tbuf; nonfirst = 0; while (s - tbuf < msg->txtlen) { /* read line at a time */ tape = iotape; /* restart dobfuscation tape */ for (numst = s; (*s ^= *tape++) != TAB; s++) ; /* get number */ save = *s; /* Temporarily trash the string (cringe) */ *s++ = 0; /* deobfuscation number within the string */ if (atoi(numst) != 100 * skip && skip >= 0) { while ((*s++ ^ * tape++) != LF) /* flush the line */ if (*tape == 0) tape = iotape; continue; } if ((*s^ * tape) == '>' && (*(s + 1) ^ * (tape + 1)) == '$' && (*(s + 2) ^ * (tape + 2)) == '<') break; if (blklin && !nonfirst++) putchar('\n'); do { if (*tape == 0) tape = iotape; putchar(*s^ * tape); } while ((*s++ ^ * tape++) != LF); /* better end with LF */ if (skip < 0) break; } free(tbuf); }