/* ibmpc/ms_misc.c: MSDOS support code

   Copyright (c) 1989-94 James E. Wilson, Don Kneller

   This file is part of Umoria.

   Umoria is free software; you can redistribute it and/or modify 
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   Umoria is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License 
   along with Umoria.  If not, see <http://www.gnu.org/licenses/>. */

#ifdef __TURBOC__
#include	<conio.h>
#endif /* __TURBOC__ */
 
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <fcntl.h>
#include <stdio.h>

#include "config.h"
#include "constant.h"
#include "types.h"
#include "externs.h"

#ifdef MSDOS
#ifndef USING_TCIO
/* We don't want to include curses.h when using the tcio.c file.  */
#include <curses.h>
#endif
#ifdef ANSI
#include "ms_ansi.h"
#endif

#ifdef LINT_ARGS
void exit(int);
static FILE *fopenp(char *, char *, char *);
static	unsigned int ioctl(int ,int ,unsigned int );
#else
void exit();
static unsigned int ioctl();
#endif

extern char *getenv();

#define PATHLEN	80
char	moriatop[PATHLEN];
char	moriasav[PATHLEN];
int	saveprompt = TRUE;
int	ibmbios;
static	int	rawio;
int8u	floorsym = '.';
int8u	wallsym = '#';

/* UNIX compatability routines */
void user_name(buf)
char *buf;
{
  strcpy(buf, getlogin());
}

char	*
getlogin()
{
	char	*cp;

	if ((cp = getenv("USER")) == NULL)
		cp = "player";
	return cp;
}

#ifndef __TURBOC__
unsigned int
sleep(secs)
int	secs;
{
	time_t	finish_time;

	finish_time = time((long *) NULL) + secs;
	while (time((long *) NULL) < finish_time)
		/* nothing */;
	return 0;
}
#endif

#ifdef OLD
/* This is the old code.  It is not strictly correct; it is retained in
   case the correct code below is not portable.  You will also have to
   change the declarations for these functions in externs.h.  */
void
error(fmt, a1, a2, a3, a4)
char	*fmt;
int	a1, a2, a3, a4;
{
	fprintf(stderr, "MORIA error: ");
	fprintf(stderr, fmt, a1, a2, a3, a4);
	(void) sleep(2);
	exit(1);
}

void
warn(fmt, a1, a2, a3, a4)
char	*fmt;
int	a1, a2, a3, a4;
{
	fprintf(stderr, "MORIA warning: ");
	fprintf(stderr, fmt, a1, a2, a3, a4);
	(void) sleep(2);
}
#else

#include <stdarg.h>

void
error (char *fmt, ...)
{
  va_list p_arg;

  va_start (p_arg, fmt);
  fprintf (stderr, "Moria error: ");
  vfprintf (stderr, fmt, p_arg);
  sleep (2);
  exit (1);
}

void
warn(char *fmt, ...)
{
  va_list p_arg;

  va_start(p_arg, fmt);
  fprintf(stderr, "MORIA warning: ");
  vfprintf(stderr, fmt, p_arg);
  sleep(2);
}
#endif

/* Search the path for a file of name "name".  The directory is
 * filled in with the directory part of the path.
 */
static FILE *
fopenp(name, mode, directory)
char *name, *mode, directory[];
{
	char *dp, *pathp, *getenv(), lastch;
	FILE *fp;

	/* Try the default directory first.  If the file can't be opened,
	 * start looking along the path.
	 */
	fp = fopen (name, mode);
	if (fp) {
		directory[0] = '\0';
		return fp;
	}
	pathp = getenv("PATH");
	while (pathp && *pathp) {
		dp = directory;
		while (*pathp && *pathp != ';')
			lastch = *dp++ = *pathp++;
		if (lastch != '\\' && lastch != '/' && lastch != ':')
			*dp++ = '\\';
		(void) strcpy(dp, name);
		fp = fopen (directory, mode);
		if (fp) {
			*dp = '\0';
			return fp;
		}
		if (*pathp)
			pathp++;
	}
	directory[0] = '\0';
	return NULL;
}

/* Read the configuration.
 */
void
msdos_init()
{
	char	buf[BUFSIZ], *bp, opt[PATHLEN];
	int	arg1, arg2, cnt;
	FILE	*fp;

	buf[0] = '\0';
	bp = MORIA_CNF_NAME;
	fp = fopenp(bp, "r", buf);
	(void) strcpy(moriatop, buf);
	(void) strcat(moriatop, MORIA_TOP_NAME);
	(void) strcpy(moriasav, buf);
	(void) strcat(moriasav, MORIA_SAV_NAME);
	if (fp == NULL) {
		warn("Can't find configuration file `%s'\n", bp);
		return;
	}
	printf("Reading configuration from %s%s\n", buf, bp);
	(void) sleep(1);
	while (fgets(buf, sizeof buf, fp)) {
		if (*buf == '#')
			continue;

		cnt = sscanf(buf, "%s", opt);
		/* Turbo C will return EOF when reading an empty line,
		   MSC will correctly read a NULL character */
		if (cnt == 0 ||
#ifdef __TURBOC__
		    cnt == EOF ||
#endif
		    opt[0] == '\0')
			continue;

		/* Go through possible variables
		 */
		if (strcmpi(opt, "GRAPHICS") == 0) {
			cnt = sscanf(buf, "%*s%d %d\n", &arg1, &arg2);
			if (cnt != 2)
				warn("GRAPHICS did not contain 2 values\n");
			else {
				wallsym = (int8u) arg1;
				floorsym = (int8u) arg2;

				/* Adjust lists that depend on '#' and '.' */
				object_list[OBJ_SECRET_DOOR].tchar = wallsym;
			}
		}
		else if (strcmpi(opt, "SAVE") == 0) {
			cnt = sscanf(buf, "%*s%s", opt);
			if (cnt == 0)
				warn("SAVE option requires a filename\n");
			else {
			        bp = strchr (opt, ';');
				if (bp) {
					*bp++ = '\0';
					if (*bp == 'n' || *bp == 'N')
						saveprompt = FALSE;
				}
				if (opt[0])
					(void) strcpy(moriasav, opt);
			}
		}
		else if (strcmpi(opt, "SCORE") == 0) {
			cnt = sscanf(buf, "%*s%s", opt);
			if (cnt == 0)
				warn("SCORE option requires a filename\n");
			else
				(void) strcpy(moriatop, opt);
		}
		else if (strcmpi(opt, "KEYBOARD") == 0) {
			cnt = sscanf(buf, "%*s%s", opt);
			if (cnt == 0)
				warn("KEYBOARD option requires a value\n");
			else if (strcmpi(opt, "ROGUE") == 0)
				rogue_like_commands = TRUE;
			else if (strcmpi(opt, "VMS") == 0)
				rogue_like_commands = FALSE;
		}
		else if (strcmpi(opt, "IBMBIOS") == 0)
			ibmbios = TRUE;
		else if (strcmpi(opt, "RAWIO") == 0)
			rawio = TRUE;
#ifdef ANSI
		/* Usage: ANSI [ check_ansi [ domoveopt [ tgoto ] ] ]
		 * where check_ansi and domoveopt are "Y"es unless explicitly
		 * set to "N"o.	 Tgoto is "N"o unless set to "Y"es.
		 */
		else if (strcmpi(opt, "ANSI") == 0) {
			cnt=sscanf(buf, "%*s%1s%1s%1s",&opt[0],&opt[1],&opt[2]);
			ansi_prep(cnt < 1 || opt[0] == 'y' || opt[0] == 'Y',
				 cnt < 2 || opt[1] == 'y' || opt[1] == 'Y',
				 cnt >= 3 && (opt[2] == 'y' || opt[2] == 'Y'));
		}
#endif
		else
			warn("Unknown configuration line: `%s'\n", buf);
	}
	fclose(fp);

	/* The only text file has been read.  Switch to binary mode */
}

#include <dos.h>
#define DEVICE	0x80
#define RAW	0x20
#define IOCTL	0x44
#define STDIN	fileno(stdin)
#define STDOUT	fileno(stdout)
#define GETBITS	0
#define SETBITS	1

static unsigned	old_stdin, old_stdout, ioctl();

void
msdos_raw() {
	if (!rawio)
		return;
	old_stdin = ioctl(STDIN, GETBITS, 0);
	old_stdout = ioctl(STDOUT, GETBITS, 0);
	if (old_stdin & DEVICE)
		ioctl(STDIN, SETBITS, old_stdin | RAW);
	if (old_stdout & DEVICE)
		ioctl(STDOUT, SETBITS, old_stdout | RAW);
}

void
msdos_noraw() {
	if (!rawio)
		return;
	if (old_stdin)
		(void) ioctl(STDIN, SETBITS, old_stdin);
	if (old_stdout)
		(void) ioctl(STDOUT, SETBITS, old_stdout);
}

static unsigned int
ioctl(handle, mode, setvalue)
unsigned int setvalue;
{
	union REGS regs;

	regs.h.ah = IOCTL;
	regs.h.al = (unsigned char) mode;
	regs.x.bx = handle;
	regs.h.dl = (unsigned char) setvalue;
	regs.h.dh = 0;			/* Zero out dh */
	intdos(&regs, &regs);
	return (regs.x.dx);
}

/* Normal characters are output when the shift key is not pushed.
 * Shift characters are output when either shift key is pushed.
 */
#define KEYPADHI	83
#define KEYPADLOW	71
#define ISKEYPAD(x)	(KEYPADLOW <= (x) && (x) <= KEYPADHI)
#undef CTRL
#define CTRL(x)		(x - '@')
typedef struct {
	char normal, shift, numlock;
} KEY;
static KEY roguekeypad[KEYPADHI - KEYPADLOW + 1] = {
	{'y', 'Y', CTRL('Y')},			/* 7 */
	{'k', 'K', CTRL('K')},			/* 8 */
	{'u', 'U', CTRL('U')},			/* 9 */
	{'.', '.', '.'},			/* - */
	{'h', 'H', CTRL('H')},			/* 4 */
	{'.', '.', '.'},			/* 5 */
	{'l', 'L', CTRL('L')},			/* 6 */
	{CTRL('P'), CTRL('P'), CTRL('P')},	/* + */
	{'b', 'B', CTRL('B')},			/* 1 */
	{'j', 'J', CTRL('J')},			/* 2 */
	{'n', 'N', CTRL('N')},			/* 3 */
	{'i', 'i', 'i'},			/* Ins */
	{'.', '.', '.'}				/* Del */
};
static KEY originalkeypad[KEYPADHI - KEYPADLOW + 1] = {
	{'7', '7', '7'},			/* 7 */
	{'8', '8', '8'},			/* 8 */
	{'9', '9', '9'},			/* 9 */
	{'-', '-', '-'},			/* - */
	{'4', '4', '4'},			/* 4 */
	{'5', '5', '5'},			/* 5  - move */
	{'6', '6', '6'},			/* 6 */
	{CTRL('M'), CTRL('M'), CTRL('M')},	/* + */
	{'1', '1', '1'},			/* 1 */
	{'2', '2', '2'},			/* 2 */
	{'3', '3', '3'},			/* 3 */
	{'i', 'i', 'i'},		/* Ins */
	{'.', '.', '.'}			/* Del */
};

/* bios_getch gets keys directly with a BIOS call.
 */
#define SHIFT		(0x1 | 0x2)
#define NUMLOCK		0x20
#define KEYBRD_BIOS	0x16

int
bios_getch()
{
	unsigned char scan, shift;
	int	ch;
	KEY	*kp;
	union REGS regs;

	if (rogue_like_commands)
		kp = roguekeypad;
	else
		kp = originalkeypad;

	/* Get scan code.
	 */
	regs.h.ah = 0;
	int86(KEYBRD_BIOS, &regs, &regs);
	ch = regs.h.al;
	scan = regs.h.ah;

	/* Get shift status.
	 */
	regs.h.ah = 2;
	int86(KEYBRD_BIOS, &regs, &regs);
	shift = regs.h.al;

	/* If scan code is for the keypad, translate it.
	 */
	if (ISKEYPAD(scan)) {
		if (shift & NUMLOCK)
			ch = kp[scan - KEYPADLOW].numlock;
		else if (shift & SHIFT)
			ch = kp[scan - KEYPADLOW].shift;
		else
			ch = kp[scan - KEYPADLOW].normal;
	}
	return ch;
}

int
msdos_getch()
{
	int	ch;

	if (ibmbios)
		ch = bios_getch();
	else {
		ch = getch();
		if (ch == 0)
			ch = getch();
	}
	return ch;
}

#if 0
/* This intro message deleted because it is obsolete.  */

/* Hardcode the introductory message in */
void
msdos_intro()
{
        char buf[80];

	clear_screen();
        wmove(stdscr,0,0);
	waddstr(stdscr,"                         *********************");
	wmove(stdscr,1,0);
        sprintf(buf,"                         **   Moria %d.%d    **",
	  CUR_VERSION_MAJ, CUR_VERSION_MIN);
        waddstr(stdscr,buf);
        wmove(stdscr,2,0);
	waddstr(stdscr,"                         *********************");
        wmove(stdscr,3,0);
	waddstr(stdscr,"                   COPYRIGHT (c) Robert Alan Koeneke");
        wmove(stdscr,5,0);
	waddstr(stdscr,"Programmers : Robert Alan Koeneke / University of Oklahoma");
        wmove(stdscr,6,0);
	waddstr(stdscr,"              Jimmey Wayne Todd   / University of Oklahoma");
        wmove(stdscr,8,0);
	waddstr(stdscr,"UNIX Port   : James E. Wilson     / Cygnus Support");
        wmove(stdscr,10,0);
	waddstr(stdscr,"MSDOS Port  : Don Kneller         / 1349 - 10th ave");
        wmove(stdscr,11,0);
	waddstr(stdscr,
		"                                  / San Francisco, CA 94122");
        wmove(stdscr,12,0);
	waddstr(stdscr,"                                  / Dec 12, 1988");
	pause_line(23);
}
#endif

#ifdef PC_CURSES
/* Seems to be a bug in PCcurses whereby it won't really clear the screen
 * if there are characters there it doesn't know about.
 */
#define VIDEOINT	0x10
void
bios_clear()
{
	union REGS regs;
	unsigned char	nocols, activepage;

#ifdef ANSI
	if (ansi)
		return;
#endif

	/* get video attributes */
	regs.h.ah = 15;
	int86(VIDEOINT, &regs, &regs);
	nocols = regs.h.ah;
	activepage = regs.h.bh;

	/* Move to lower right corner */
	regs.h.ah = 2;
	regs.h.dh = (unsigned char) 24;
	regs.h.dl = nocols - 1;	/* lower right col */
	regs.h.bh = activepage;
	int86(VIDEOINT, &regs, &regs);

	/* get current attribute into bh */
	regs.h.ah = 8;
	regs.h.bh = activepage;
	int86(VIDEOINT, &regs, &regs);
	regs.h.bh = regs.h.ah;

	regs.h.cl = 0;	/* upper left row */
	regs.h.ch = 0;	/* upper left col */
	regs.h.dh = (unsigned char) 24;	/* lower right row */
	regs.h.dl = nocols - 1;	/* lower right col */
	regs.h.al = 0;	/* clear window */
	regs.h.ah = 7;	/* scroll down */
	int86(VIDEOINT, &regs, &regs);
}
#endif

#endif
