/*	$OpenBSD: io.c,v 1.16 2006/03/27 00:10:14 tedu 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.
 */

#ifndef lint
#if 0
static char sccsid[] = "@(#)io.c	8.1 (Berkeley) 5/31/93";
#else
static char rcsid[] = "$OpenBSD: io.c,v 1.16 2006/03/27 00:10:14 tedu Exp $";
#endif
#endif /* not lint */

/*	Re-coding of advent in C: file i/o and user i/o			*/

#include <err.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "hdr.h"
#include "extern.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 encryption tape	*/

int
next(void)			/* next virtual char, bump adr	*/
{
	int ch;

	ch=(*inptr ^ random()) & 0xFF;	/* Decrypt input data		*/
	if (outsw) {		/* putting data in tmp file	*/
		if (*tape == 0)
			tape = iotape;	/* rewind encryption tape	*/
		*inptr = ch ^ *tape++;	/* re-encrypt 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 */
	srandom(SEED);		/* which is lightly encrypted.	*/

	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 encryption 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<delimiter>		*/
			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(&ltext[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(&ltext[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, decrypt, 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 decryption 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, decrypt an 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 = (char *) 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 decryption tape	*/
		for (numst = s; (*s ^= *tape++) != TAB; s++)
			; /* get number	*/

		save = *s; /* Temporarily trash the string (cringe)	*/
		*s++ = 0; /* decrypting 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);
}