/* File: main-dos.c */

/*
 * Copyright (c) 1997 Ben Harrison, and others
 *
 * This software may be copied and distributed for educational, research,
 * and not for profit purposes provided that this copyright and statement
 * are included in all such copies.
 */


/*
 * This file allows Angband to be compiled for DOS-386 machines, using the
 * "DJGPP v2" compiler, and the Allegro graphics package, which allows the
 * use of "sub-windows" and/or "real" a graphic bitmap.
 *
 * Adapted from "main-ibm.c".
 *
 * Support for DJGPP v2 by "Scott Egashira (egashira@u.washington.edu)"
 *
 * Extensive modifications by "Ben Harrison (benh@voicenet.com)",
 *
 * SVGA Graphic mode by Robert Ruehlmann (rr9@inf.tu-dresden.de)
 * (see "http://www.inf.tu-dresden.de/~rr9/angband.html")
 *
 * Rename "Makefile.dos" to "Makefile" for use with this file.
 *
 * Compile with the DJGPP compiler (see "http://www.delorie.com/djgpp/").
 *
 * Include the "Allegro" header files, and link with the "Allegro" library
 * (see "http://www.talula.demon.co.uk/allegro/").
 *
 * Both "shift" keys are treated as "identical", and all the modifier keys
 * (control, shift, alt) are ignored when used with "normal" keys, unless
 * they modify the underlying "ascii" value of the key.  You must use the
 * new "user pref files" to be able to interact with the keypad and such.
 *
 * The "lib/user/pref-dos.prf" file contains macro definitions and possible
 * alternative color set definitions.
 *
 * Note the "Term_user_dos()" function hook, which could allow the user
 * to interact with the "main-dos.c" visual system.  Currently this hook
 * is unused, but, for example, it could allow the user to toggle "sound"
 * or "graphics" modes, or to select the number of screen rows, with the
 * extra screen rows being used for the mirror window.
 */


#include "angband.h"


#ifdef USE_DOS


/*
 * Include the main "Allegro" header file
 */
#include "allegro.h"


/*
 * Include the keyboard header file
 */
#include "keys.h"


/*
 * Include the definition for the tombstone_hook
 */
#include "files.h"


/*
 * Include the definition for the save_screen_hook
 */
#include "cmd4.h"


/*
 * Use the enhanced bitmap from ZAngband to support the new races and classes
 */
#define EXTENDED_GRAPHICS


/*
 * Bitmaps for the tiles and the cursor
 */
BITMAP *tiles;
BITMAP *cursor;


/*
 * Size of the bitmap tiles
 */
int bitmap_tile_width;
int bitmap_tile_height;


/*
 * Index of the first standard Angband color.
 *
 * All colors below this index are defined by
 * the palette of the tiles-bitmap.
 */
#define angband_colors 240


/*
 * On my Windows NT box, modes "VESA1" and "VESA2B" seem to work, but
 * result in the game running with no visible output.  Mode "VESA2L"
 * clears the screen and then fails silently.  All other modes fail
 * instantly.  To recover from invisible modes, hit control-c, then
 * escape many times, then control-x, then escape.  XXX XXX XXX
 */


/*
 * List of the available videomodes to reduce executable size
 */
DECLARE_GFX_DRIVER_LIST(
	GFX_DRIVER_VBEAF
	GFX_DRIVER_VESA2L
	GFX_DRIVER_VESA2B
	GFX_DRIVER_ATI
	GFX_DRIVER_MACH64
	GFX_DRIVER_CIRRUS64
	GFX_DRIVER_CIRRUS54
	GFX_DRIVER_PARADISE
	GFX_DRIVER_S3
	GFX_DRIVER_TRIDENT
	GFX_DRIVER_ET3000
	GFX_DRIVER_ET4000
	GFX_DRIVER_VIDEO7
	GFX_DRIVER_VESA1
)


/*
 * Declare the videomode list
 */
DECLARE_COLOR_DEPTH_LIST(COLOR_DEPTH_8)


/*
 * Forward declare
 */
typedef struct term_data term_data;

/*
 * Extra "term" data
 */
struct term_data
{
	term t;

	int rows;
	int cols;

	int x;
	int y;

	int w;
	int h;

	bool stretch;

	FONT *font;

#ifdef USE_BACKGROUND

	BITMAP *background;

#endif /* USE_BACKGROUND */
};


/*
 * Maximum number of terminals
 */
#define MAX_TERM_DATA 8


/*
 * An array of term_data's
 */
static term_data data[MAX_TERM_DATA];




#include <bios.h>
#include <dos.h>

#if __DJGPP__ > 1
# include <pc.h>
# include <osfcn.h>
#else /* __DJGPP__ > 1 */
# ifdef __DJGPP__
#  error "Upgrade to version 2.0 of DJGPP"
# else
#  error "This file needs the DJGPP compiler"
# endif /* __DJGPP__ */
#endif /* __DJGPP__ > 1 */




/*
 * Keypress input modifier flags (hard-coded by DOS)
 */
#define K_RSHIFT	0	/* Right shift key down */
#define K_LSHIFT	1	/* Left shift key down */
#define K_CTRL		2	/* Ctrl key down */
#define K_ALT		3	/* Alt key down */
#define K_SCROLL	4	/* Scroll lock on */
#define K_NUM		5	/* Num lock on */
#define K_CAPS		6	/* Caps lock on */
#define K_INSERT	7	/* Insert on */


/*
 * Dump the screen to "lib/user/dump.bmp"
 */
void dos_dump_screen(void)
{
	/* Bitmap and palette of the screen */
	BITMAP *bmp;
	PALETTE pal;

	/* Filename */
	char filename[1024];

	/* Get bitmap and palette of the screen */
	bmp = create_sub_bitmap(screen, 0, 0, SCREEN_W, SCREEN_H);
	get_palette(pal);

	/* Build the filename for the screen-dump */
	path_build(filename, 1024, ANGBAND_DIR_USER, "dump.bmp");

	/* Save it */
	save_bmp(filename, bmp, pal);

	/* Free up the memory */
	if (bmp) destroy_bitmap(bmp);

	/* Success message */
	msg_print("Screen dump saved.");
	msg_print(NULL);
}


/*
 * Process an event (check for a keypress)
 *
 * The keypress processing code is often the most system dependant part
 * of Angband, since sometimes even the choice of compiler is important.
 *
 * For DOS, we divide all keypresses into two catagories, first, the
 * "normal" keys, including all keys required to play Angband, and second,
 * the "special" keys, such as keypad keys, function keys, and various keys
 * used in combination with various modifier keys.
 *
 * To simplify this file, we use Angband's "macro processing" ability, in
 * combination with a specialized "pref-dos.prf" file, to handle most of the
 * "special" keys, instead of attempting to fully analyze them here.  This
 * file only has to determine when a "special" key has been pressed, and
 * translate it into a simple string which signals the use of a "special"
 * key, the set of modifiers used, if any, and the hardware scan code of
 * the actual key which was pressed.  To simplify life for the user, we
 * treat both "shift" keys as identical modifiers.
 *
 * The final encoding is "^_MMMxSS\r", where "MMM" encodes the modifiers
 * ("C" for control, "S" for shift, "A" for alt, or any ordered combination),
 * and "SS" encodes the keypress (as the two "digit" hexidecimal encoding of
 * the scan code of the key that was pressed), and the "^_" and "x" and "\r"
 * delimit the encoding for recognition by the macro processing code.
 *
 * Some important facts about scan codes follow.  All "normal" keys use
 * scan codes from 1-58.  The "function" keys use 59-68 (and 133-134).
 * The "keypad" keys use 69-83.  Escape uses 1.  Enter uses 28.  Control
 * uses 29.  Left Shift uses 42.  Right Shift uses 54.  PrtScrn uses 55.
 * Alt uses 56.  Space uses 57.  CapsLock uses 58.  NumLock uses 69.
 * ScrollLock uses 70.  The "keypad" keys which use scan codes 71-83
 * are ordered KP7,KP8,KP9,KP-,KP4,KP5,KP6,KP+,KP1,KP2,KP3,INS,DEL.
 *
 * Using "bioskey(0x10)" instead of "bioskey(0)" apparently provides more
 * information, including better access to the keypad keys in combination
 * with various modifiers, but only works on "PC's after 6/1/86", and there
 * is no way to determine if the function is provided on a machine.  I have
 * been told that without it you cannot detect, for example, control-left.
 * The basic scan code + ascii value pairs returned by the keypad follow,
 * with values in parentheses only available to "bioskey(0x10)".
 *
 *         /      *      -      +      1      2      3      4
 * Norm:  352f   372a   4a2d   4e2b   4f00   5000   5100   4b00
 * Shft:  352f   372a   4a2d   4e2b   4f31   5032   5133   4b34
 * Ctrl: (9500) (9600) (8e00) (9000)  7500  (9100)  7600   7300
 *
 *         5      6      7      8      9      0      .     Enter
 * Norm: (4c00)  4d00   4700   4800   4900   5200   5300  (e00d)
 * Shft:  4c35   4d36   4737   4838   4939   5230   532e  (e00d)
 * Ctrl: (8f00)  7400   7700  (8d00)  8400  (9200) (9300) (e00a)
 *
 * See "pref-dos.prf" for the "standard" macros for various keys.
 *
 * Certain "bizarre" keypad keys (such as "enter") return a "scan code"
 * of "0xE0", and a "usable" ascii value.  These keys should be treated
 * like the normal keys, see below.  XXX XXX XXX Note that these "special"
 * keys could be prefixed with an optional "ctrl-^" which would allow them
 * to be used in macros without hurting their use in normal situations.
 */
static errr Term_xtra_dos_event(int v)
{
	int i, k, s;

	bool mc = FALSE;
	bool ms = FALSE;
	bool ma = FALSE;

	/* Hack -- Check for a keypress */
	if (!v && !bioskey(1)) return (1);

	/* Wait for a keypress */
	k = bioskey(0x10);

	/* Access the "modifiers" */
	i = bioskey(2);

	/* Extract the "scan code" */
	s = ((k >> 8) & 0xFF);

	/* Extract the "ascii value" */
	k = (k & 0xFF);

	/* Process "normal" keys */
	if (((s <= 58) || (s == 0xE0)) && k)
	{
		/* Enqueue it */
		if (k) Term_keypress(k);

		/* Success */
		return (0);
	}

	/* Extract the modifier flags */
	if (i & (1 << K_CTRL)) mc = TRUE;
	if (i & (1 << K_LSHIFT)) ms = TRUE;
	if (i & (1 << K_RSHIFT)) ms = TRUE;
	if (i & (1 << K_ALT)) ma = TRUE;

	/* Dump the screen with "Ctrl-Print" */
	if ((s == 0x72) && mc)
	{
		/* Dump the screen */
		dos_dump_screen();

		/* Success */
		return (0);
	}

	/* Begin a "macro trigger" */
	Term_keypress(31);

	/* Hack -- Send the modifiers */
	if (mc) Term_keypress('C');
	if (ms) Term_keypress('S');
	if (ma) Term_keypress('A');

	/* Introduce the hexidecimal scan code */
	Term_keypress('x');

	/* Encode the hexidecimal scan code */
	Term_keypress(hexsym[s/16]);
	Term_keypress(hexsym[s%16]);

	/* End the "macro trigger" */
	Term_keypress(13);

	/* Success */
	return (0);
}


/*
 * Set the Angband pallete
 */
void Term_xtra_dos_react(void)
{
	int i;

	/* Set the Angband colors */
	for (i = 0; i < 16; i++)
	{
		/* Extract desired values */
		char rv = angband_color_table[i][1] >> 2;
		char gv = angband_color_table[i][2] >> 2;
		char bv = angband_color_table[i][3] >> 2;

		RGB color = { rv,  gv,  bv  };
		set_color(angband_colors+i, &color);
	}
}


/*
 * Clear a terminal
 */
void Term_xtra_dos_clear(void)
{
	term_data *td = (term_data*)(Term->data);

	int dest_x = td->x;
	int dest_y = td->y;
	int dest_w = td->w * td->cols;
	int dest_h = td->h * td->rows;

#ifdef USE_BACKGROUND

	if (td->background)
	{
		/* Draw the background */
		stretch_blit(td->background, screen, 0, 0, td->background->w, td->background->h,
			dest_x, dest_y, dest_w, dest_h);
	}
	else

#endif /* USE_BACKGROUND */

	{
		/* Draw the Term black */
		rectfill(screen, dest_x, dest_y, dest_x + dest_w, dest_y + dest_h, angband_colors + TERM_DARK);
	}
}


/*
 * Handle a "special request"
 *
 * The given parameters are "valid".
 */
static errr Term_xtra_dos(int n, int v)
{
	/* Analyze the request */
	switch (n)
	{
		/* Make a "bell" noise */
		case TERM_XTRA_NOISE:
		{
			/* Make a bell noise */
			(void)write(1, "\007", 1);

			/* Success */
			return (0);
		}

		/* Clear the screen */
		case TERM_XTRA_CLEAR:
		{
			Term_xtra_dos_clear();

			/* Success */
			return (0);
		}

		/* Process events */
		case TERM_XTRA_EVENT:
		{
			/* Process one event */
			return (Term_xtra_dos_event(v));
		}

		/* Flush events */
		case TERM_XTRA_FLUSH:
		{
			/* Strip events */
			while (!Term_xtra_dos_event(FALSE));

			/* Success */
			return (0);
		}

		/* React to global changes */
		case TERM_XTRA_REACT:
		{
			/* Change the colors */
			Term_xtra_dos_react();

			/* Success */
			return (0);
		}

		/* Delay for some milliseconds */
		case TERM_XTRA_DELAY:
		{
			/* Delay if needed */
			if (v > 0) delay(v);

			/* Success */
			return (0);
		}
	}

	/* Unknown request */
	return (1);
}



/*
 * Move the cursor
 *
 * The given parameters are "valid".
 */
static errr Term_curs_dos(int x, int y)
{
	term_data *td = (term_data*)(Term->data);

	x = x * td->w + td->x;
	y = y * td->h + td->y;

	/* Draw the cursor */
	draw_sprite(screen, cursor, x, y);

	/* Success */
	return (0);
}


/*
 * Erase a block of the screen
 *
 * The given parameters are "valid".
 */
static errr Term_wipe_dos(int x, int y, int n)
{
	term_data *td = (term_data*)(Term->data);

	int dest_x = x * td->w + td->x;
	int dest_y = y * td->h + td->y;
	int dest_w = n * td->w;
	int dest_h = td->h;

#ifdef USE_BACKGROUND

	if (td->background)
	{
		int source_x = x * td->background->w / td->cols;
		int source_y = y * td->background->h / td->rows;
		int source_w = n * td->background->w / td->cols;
		int source_h = td->background->h / td->rows;

		/* Draw the background */
		stretch_blit(td->background, screen, source_x, source_y, source_w, source_h,
			dest_x, dest_y, dest_w, dest_h);
	}
	else

#endif /* USE_BACKGROUND */

	{
		/* Draw a black block */
		rectfill(screen, dest_x, dest_y, dest_x + dest_w,
			dest_y + dest_h, angband_colors + TERM_DARK );
	}

	/* Success */
	return (0);
}


/*
 * Place some text on the screen using an attribute
 *
 * The given parameters are "valid".  Be careful with "a".
 *
 * The string "cp" has length "n" and is NOT null-terminated.
 */
static errr Term_text_dos(int x, int y, int n, byte a, const char *cp)
{
	register int i;

	term_data *td = (term_data*)(Term->data);

	char text[2];

	/* Erase old contents */
	Term_wipe_dos(x, y, n);

	/* Terminate "text" for "textout()" below */
	text[1] = 0;

	/* Write the chars to the screen */
	for (i = 0; i < n; i++)
	{
		int x1 = (x + i) * td->w + td->x;
		int y1 = y * td->h + td->y;

		/* Build a one character string */
		text[0] = cp[i];

		/* Dump some text */
		textout(screen, td->font, text, x1, y1,
			angband_colors + (a & 0x0F));
	}

	/* Success */
	return (0);
}


/*
 * Place some attr/char pairs on the screen
 *
 * The given parameters are "valid".
 */
static errr Term_pict_dos(int x, int y, int n, const byte *ap, const char *cp)
{
	register int i;

	term_data *td = (term_data*)(Term->data);

	/* Dump the tiles */
	for (i = 0; i < n; i++)
	{
		int x1 = (x + i) * td->w + td->x;
		int y1 = y * td->h + td->y;

		/* Extract tile position */
		int row = (ap[i] & 0x7F);
		int col = (cp[i] & 0x7F);

		/* Player - Remap for race and class */
		if ((row == 12) && (col == 0))
		{

#ifdef EXTENDED_GRAPHICS

			/* Use the enhanced bitmap from ZAngband */
			row = p_ptr->pclass + 36;
			col = p_ptr->prace;

#else /* EXTENDED_GRAPHICS */

			/* Use the standard bitmap from Angband */
			row = (( p_ptr->pclass * 10 + p_ptr->prace) >> 5 ) + 12;
			col = (( p_ptr->pclass * 10 + p_ptr->prace) & 0x1f );

#endif /* EXTENDED_GRAPHICS */

		}

		/* Stretch */
		if (td->stretch)
		{
			/* Blit the tile to the screen and stretch it */
			stretch_blit(tiles, screen,
				col * bitmap_tile_width, row * bitmap_tile_height,
				bitmap_tile_width, bitmap_tile_height,
				x1, y1, td->w, td->h);
		}

		/* Blit */
		else
		{
			/* Blit the tile to the screen */
			blit(tiles, screen,
				col * bitmap_tile_width, row * bitmap_tile_height,
				x1, y1, td->w, td->h);
		}
	}

	/* Success */
	return (0);
}


/*
 * Init a Term
 */
static void Term_init_dos(term *t)
{
	/* XXX Nothing */
}


/*
 * Nuke a Term
 */
static void Term_nuke_dos(term *t)
{
	term_data *td = (term_data*)(t->data);

#ifdef USE_BACKGROUND

	/* Free the background bitmap */
	if (td->background) destroy_bitmap(td->background);

#endif /* USE_BACKGROUND */

	/* Free the terminal font */
	if (td->font) destroy_font(td->font);
}




/*
 * Parse the angband.ini file and return a string
 */
void parse_ini_line_string(FILE *fff, char *buf, int len)
{
	while (TRUE)
	{
		/* Get a line from the file */
		if (my_fgets(fff, buf, len))
		{
			quit("Error in 'angband.ini' file.");
		}

		/* Skip "empty" lines */
		if (!buf[0]) continue;

		/* Skip "blank" lines */
		if (isspace(buf[0])) continue;

		/* Skip comments */
		if (buf[0] == '#') continue;
		if (buf[0] == ';') continue;

		/* Exit with the new value */
		break;
	}
}


/*
 * Parse the angband.ini file and return an integer
 */
int parse_ini_line_int(FILE *fff)
{
	char buf[256];

	parse_ini_line_string(fff, buf, 256);

	return (atoi(buf));
}



/*
 * Instantiate a "term_data" structure
 */
static void term_data_link(term_data *td)
{
	term *t = &td->t;

	/* Initialize the term */
	term_init(t, td->cols, td->rows, 255);

	/* Use a "software" cursor */
	t->soft_cursor = TRUE;

	/* Use "Term_pict" for "graphic" data */
	t->higher_pict = TRUE;

	/* Ignore the "TERM_XTRA_BORED" action */
	t->never_bored = TRUE;

	/* Ignore the "TERM_XTRA_FROSH" action */
	t->never_frosh = TRUE;

	/* Erase with "white space" */
	t->attr_blank = TERM_WHITE;
	t->char_blank = ' ';

	/* Prepare the init/nuke hooks */
	t->init_hook = Term_init_dos;
	t->nuke_hook = Term_nuke_dos;

	/* Prepare the template hooks */
	t->xtra_hook = Term_xtra_dos;
	t->curs_hook = Term_curs_dos;
	t->wipe_hook = Term_wipe_dos;
	t->text_hook = Term_text_dos;
	t->pict_hook = Term_pict_dos;

	/* Remember where we came from */
	t->data = (vptr)(td);
}


/*
 * Quit Angband
 */
static void hook_quit(cptr str)
{
	int i;

	/* Close sub-windows */
	for (i = MAX_TERM_DATA - 1; i >= 1; i--)
	{
		/* Unused */
		if (!angband_term[i]) continue;

		/* Nuke it */
		term_nuke(angband_term[i]);
	}


	/* Free all resources */
	if (tiles) destroy_bitmap(tiles);
	if (cursor) destroy_bitmap(cursor);

	/* Shut down Allegro */
	allegro_exit();

	/* Print the error description */
	if (str) plog(str);

	exit(0);
}


/*
 * Display text on the tomb-stone
 */
void tombtext(int x, int y, char *text)
{
	term_data *td = (term_data*)(Term->data);

	/* Print the "embossed" text */
	textout_centre(screen, td->font, text,
		td->w * x + td->x + 1, td->h * y + td->y + 1, angband_colors + TERM_DARK);

	/* Print the text */
	textout_centre(screen, td->font, text,
		td->w * x + td->x, td->h * y + td->y, angband_colors + TERM_WHITE);
}



/*
 * Display a "tomb-stone"
 */
bool dos_tomb(void)
{
	term_data *td = (term_data*)(Term->data);

	/* Filename */
	char tmp[1024];

	/* Tomb bitmap and pallete */
	BITMAP *tomb_bmp;
	PALLETE tomb_pallete;

	/* Get the current time and date */
	time_t ct = time((time_t)0);

	/* Clear screen */
	Term_clear();

	/* Build the filename for the tomb-bitmap */
	path_build(tmp, 1024, ANGBAND_DIR_XTRA, "tomb.bmp");

	/* Open the bitmap file */
	if (!(tomb_bmp = load_bitmap(tmp, tomb_pallete)))
		return FALSE;

	/* Draw the bitmap */
	stretch_blit(tomb_bmp, screen, 0, 0, tomb_bmp->w, tomb_bmp->h,
		td->x, td->y, td->w * td->cols, td->h * td->rows);

#ifdef ANGBAND_2_8_1
	tombtext(40, 6, player_name);
#else /* ANGBAND_2_8_1 */
	tombtext(40, 6, op_ptr->full_name);
#endif /* ANGBAND_2_8_1 */

	tombtext(40, 7, "the");

	/* King or Queen */
#ifdef ANGBAND_2_8_1
	if (total_winner || (p_ptr->lev > PY_MAX_LEVEL))
#else /* ANGBAND_2_8_1 */
	if (p_ptr->total_winner || (p_ptr->lev > PY_MAX_LEVEL))
#endif /* ANGBAND_2_8_1 */

	{
		(void)sprintf(tmp, "Magnificent");
	}

	/* Normal */
	else
	{
		(void)sprintf(tmp, "%s", player_title[p_ptr->pclass][(p_ptr->lev-1)/5]);
	}

	tombtext(40, 8, tmp);
	(void)sprintf(tmp, "%s", cp_ptr->title);
	tombtext(40, 10, tmp);
	(void)sprintf(tmp, "Level: %d", (int)p_ptr->lev);
	tombtext(40, 11, tmp);
	(void)sprintf(tmp, "Exp: %ld", (long)p_ptr->exp);
	tombtext(40, 12, tmp);
	(void)sprintf(tmp, "AU: %ld", (long)p_ptr->au);
	tombtext(40, 13, tmp);

#ifdef ANGBAND_2_8_1
	(void)sprintf(tmp, "Killed on Level %d", dun_level);
#else /* ANGBAND_2_8_1 */
	(void)sprintf(tmp, "Killed on Level %d", p_ptr->depth);
#endif /* ANGBAND_2_8_1 */

	tombtext(40, 14, tmp);

#ifdef ANGBAND_2_8_1
	(void)sprintf(tmp, "by %s.", died_from);
#else /* ANGBAND_2_8_1 */
	(void)sprintf(tmp, "by %s.", p_ptr->died_from);
#endif /* ANGBAND_2_8_1 */

	tombtext(40, 15, tmp);
	(void)sprintf(tmp, "%-.24s", ctime(&ct));
	tombtext(40, 17, tmp);

	/* Wait for keypress */
	while (1)
	{
		int a = getkey();
		if ((a == K_Escape) || (a == 'q') || (a == 'Q')) break;
	}

	/* Free up some memory */
	if (tomb_bmp) destroy_bitmap(tomb_bmp);

	/* Success */
	return TRUE;
}


/* GRX font file reader by Mark Wodrich.
 *
 * GRX FNT files consist of the header data (see struct below). If the font
 * is proportional, followed by a table of widths per character (unsigned 
 * shorts). Then, the data for each character follows. 1 bit/pixel is used,
 * with each line of the character stored in contiguous bytes. High bit of
 * first byte is leftmost pixel of line.
 *
 * Note : FNT files can have a variable number of characters, so we must
 *        check that the chars 32..127 exist.
 */


#define FONTMAGIC       0x19590214L


/* .FNT file header */
typedef struct {
   unsigned long  magic;
   unsigned long  bmpsize;
   unsigned short width;
   unsigned short height;
   unsigned short minchar;
   unsigned short maxchar;
   unsigned short isfixed;
   unsigned short reserved;
   unsigned short baseline;
   unsigned short undwidth;
   char           fname[16];
   char           family[16];
} FNTfile_header;


/* all we need is an array of bytes for each character */
typedef unsigned char * GRX_BITMAP;


#define GRX_TMP_SIZE    4096



void convert_grx_bitmap(int width, int height, GRX_BITMAP src, GRX_BITMAP dest) 
{
   unsigned short x, y, bytes_per_line;
   unsigned char bitpos, bitset;

   bytes_per_line = (width+7) >> 3;

   for (y=0; y<height; y++) {
      for (x=0; x<width; x++) {
	 bitpos = 7-(x&7);
	 bitset = !!(src[(bytes_per_line*y) + (x>>3)] & (1<<bitpos));
	 dest[y*width+x] = bitset;
      }
   }
}



GRX_BITMAP *load_grx_bmps(PACKFILE *f, FNTfile_header *hdr, int numchar, unsigned short *wtable) 
{
   int t, width, bmp_size;
   GRX_BITMAP temp;
   GRX_BITMAP *bmp;

   /* alloc array of bitmap pointers */
   bmp = malloc(sizeof(GRX_BITMAP) * numchar);

   /* assume it's fixed width for now */
   width = hdr->width;

   /* temporary working area to store FNT bitmap */
   temp = malloc(GRX_TMP_SIZE);

   for (t=0; t<numchar; t++) {
      /* if prop. get character width */
      if (!hdr->isfixed) 
	 width = wtable[t];

      /* work out how many bytes to read */
      bmp_size = ((width+7) >> 3) * hdr->height;

      /* oops, out of space! */
      if (bmp_size > GRX_TMP_SIZE) {
	 free(temp);
	 for (t--; t>=0; t--)
	    free(bmp[t]);
	 free(bmp);
	 return NULL;
      }

      /* alloc space for converted bitmap */
      bmp[t] = malloc(width*hdr->height);

      /* read data */
      pack_fread(temp, bmp_size, f);

      /* convert to 1 byte/pixel */
      convert_grx_bitmap(width, hdr->height, temp, bmp[t]);
   }

   free(temp);
   return bmp;
}


FONT *import_grx_font(char *fname)
{
   PACKFILE *f;
   FNTfile_header hdr;              /* GRX font header */
   int numchar;                     /* number of characters in the font */
   unsigned short *wtable = NULL;   /* table of widths for each character */
   GRX_BITMAP *bmp;                 /* array of font bitmaps */
   FONT *font = NULL;               /* the Allegro font */
   FONT_PROP *font_prop;
   int c, c2, start, width;

   f = pack_fopen(fname, F_READ);
   if (!f)
      return NULL;

   pack_fread(&hdr, sizeof(hdr), f);      /* read the header structure */

   if (hdr.magic != FONTMAGIC) {          /* check magic number */
      pack_fclose(f);
      return NULL;
   }

   numchar = hdr.maxchar-hdr.minchar+1;

   if (!hdr.isfixed) {                    /* proportional font */
      wtable = malloc(sizeof(unsigned short) * numchar);
      pack_fread(wtable, sizeof(unsigned short) * numchar, f);
   }

   bmp = load_grx_bmps(f, &hdr, numchar, wtable);
   if (!bmp)
      goto get_out;

   if (pack_ferror(f))
      goto get_out;

   font = malloc(sizeof(FONT));
   font->height = -1;
   font->dat.dat_prop = font_prop = malloc(sizeof(FONT_PROP));

   start = 32 - hdr.minchar;
   width = hdr.width;

   for (c=0; c<FONT_SIZE; c++) {
      c2 = c+start;

      if ((c2 >= 0) && (c2 < numchar)) {
	 if (!hdr.isfixed)
	    width = wtable[c2];

	 font_prop->dat[c] = create_bitmap_ex(8, width, hdr.height);
	 memcpy(font_prop->dat[c]->dat, bmp[c2], width*hdr.height);
      }
      else {
	 font_prop->dat[c] = create_bitmap_ex(8, 8, hdr.height);
	 clear(font_prop->dat[c]);
      }
   }

   get_out:

   pack_fclose(f);

   if (wtable)
      free(wtable);

   if (bmp) {
      for (c=0; c<numchar; c++)
	 free(bmp[c]);
      free(bmp);
   }

   return font;
}


/*
 * Attempt to initialize this file
 *
 * Hack -- we assume that "blank space" should be "white space"
 * (and not "black space" which might make more sense).
 *
 * Note the use of "((x << 2) | (x >> 4))" to "expand" a 6 bit value
 * into an 8 bit value, without losing much precision, by using the 2
 * most significant bits as the least significant bits in the new value.
 */
errr init_dos(void)
{
	int i, n;

	term_data *td;

	int screen_width;
	int screen_height;

	char name_tiles[128];

	char name_fonts[8][128];

	char filename[1024];

	FILE *ini_file;

	PALLETE tiles_pallete;

#ifdef USE_BACKGROUND

	PALLETE background_pallete;

	char name_backgrounds[8][128];

#endif /* USE_BACKGROUND */

	/* Initialize the Allegro library */
	allegro_init();

	/* Build the filename for "angband.ini" */
	path_build(filename, 1024, ANGBAND_DIR_XTRA, "angband.ini");

	/* Open the "angband.ini" file */
	if (!(ini_file = my_fopen(filename, "r")))
	{
		/* Comment XXX XXX XXX */
		plog_fmt("\nCannot open file '%s'\n", filename);

		/* Shut down Allegro */
		allegro_exit();

		/* Failure */
		return (-1);
	}

	/* Get screen size */
	screen_width = parse_ini_line_int(ini_file);
	screen_height = parse_ini_line_int(ini_file);

	/* Get bitmap filename */
	parse_ini_line_string(ini_file, name_tiles, 80);

	/* Get bitmap tile size */
	bitmap_tile_width = parse_ini_line_int(ini_file);
	bitmap_tile_height = parse_ini_line_int(ini_file);

	/* Get term count */
	n = parse_ini_line_int(ini_file);

	/* Paranoia */
	if (n > 8) n = 8;

	/* Init the terms */
	for (i = 0; i < n; i++)
	{
#ifndef USE_BACKGROUND
		char tmp[128];
#endif /* USE_BACKGROUND */

		td = &data[i];
		WIPE(td, term_data);

		/* Coordinates of left top corner */
		td->x = parse_ini_line_int(ini_file);
		td->y = parse_ini_line_int(ini_file);

		/* Rows and cols of term */
		td->rows = parse_ini_line_int(ini_file);
		td->cols = parse_ini_line_int(ini_file);

		/* Tile size */
		td->w = parse_ini_line_int(ini_file);
		td->h = parse_ini_line_int(ini_file);

		/* Extract stretch */
		td->stretch = FALSE;
		if (td->w != bitmap_tile_width) td->stretch = TRUE;
		if (td->h != bitmap_tile_height) td->stretch = TRUE;

		/* Get font filename */
		parse_ini_line_string(ini_file, name_fonts[i], 80);

#ifdef USE_BACKGROUND
		/* Get background filename */
		parse_ini_line_string(ini_file, name_backgrounds[i], 80);
#else /* USE_BACKGROUND */
		/* Ignore background filename */
		parse_ini_line_string(ini_file, tmp, 80);
#endif /* USE_BACKGROUND */

	}

	/* Close angband.ini */
	my_fclose(ini_file);

	/* Set the color depth */
	set_color_depth(8);

	/* Auto-detect, and instantiate, the appropriate graphics mode */
	if ((set_gfx_mode(GFX_AUTODETECT, screen_width, screen_height, 0, 0)) < 0)
	{
		/* Get the Allegro error description */
		char error_text[1024];
		(void)sprintf(error_text, "Error selecting screen mode\n%s\n", allegro_error);

		/* Shut down Allegro */
		allegro_exit();

		/* Print the error description */
		plog(error_text);

		/* Failure */
		return (-1);
	}

	/* Hook in function hooks */
	quit_aux = hook_quit;
	tombstone_aux = dos_tomb;
	screendump_aux = dos_dump_screen;

	/* Build the filename for the tiles-bitmap */
	path_build(filename, 1024, ANGBAND_DIR_XTRA, name_tiles);

	/* Open the bitmap file */
	if ((tiles = load_bitmap(filename, tiles_pallete)))
	{
		/* Select the bitmap pallete */
		set_pallete(tiles_pallete);

		/* Use graphics */
		use_graphics = TRUE;
		arg_graphics = TRUE;
	}
	else
	{
		/* Don't use graphics */
		use_graphics = FALSE;
		arg_graphics = FALSE;
	}

	/* Set the Angband colors */
	Term_xtra_dos_react();

	/* Init the terms */
	for (i = 0; i < n; i++)
	{
		td = &data[i];

		/* Load the font */
		path_build(filename, 1024, ANGBAND_DIR_XTRA, name_fonts[i]);

		if (!(td->font = import_grx_font(filename)))
		{
			quit("Error reading font file");
		}

#ifdef USE_BACKGROUND

		/* Build the filename for the background-bitmap */
		path_build(filename, 1024, ANGBAND_DIR_XTRA, name_backgrounds[i]);

		/* Try to open the bitmap file */
		td->background = load_bitmap(filename, background_pallete);

		/* Try to set the palette for the background */
		if (td->background)
		{
			/* We only use the 16 colors from 223 to 239 */
			set_palette_range(background_pallete, 223, 239, 0);
		}

#endif /* USE_BACKGROUND */

		/* Link the term */
		term_data_link(td);
		angband_term[i] = &td->t;

		/* Activate the term */
		Term_activate(angband_term[i]);

		/* Redraw the term */
		Term_redraw();
	}

	/* Build a cursor bitmap */
	cursor = create_bitmap(data[0].w, data[0].h);

	/* Erase the cursor sprite */
	clear(cursor);

	/* Draw the cursor sprite (yellow rectangle) */
	rect(cursor, 0, 0, data[0].w - 1, data[0].h - 1,
	     angband_colors + TERM_YELLOW);

#ifdef USE_BACKGROUND

	/* Use transparent text */
	text_mode(-1);

#endif /* USE_BACKGROUND */

	/* Activate the main term */
	Term_activate(angband_term[0]);

	/* Erase the screen */
	Term_xtra_dos(TERM_XTRA_CLEAR, 0);

	/* Place the cursor */
	Term_curs_dos(0, 0);

	/* Success */
	return 0;
}

#endif /* USE_DOS */

