/* 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 #include #if __DJGPP__ > 1 # include # include #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 */ /* * Include the keyboard header file */ #include "keys.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; }; /* * Maximum number of terminals */ #define MAX_TERM_DATA 8 /* * An array of term_data's */ static term_data data[MAX_TERM_DATA]; /* * 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 */ /* * 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 x1 = td->x; int y1 = td->y; int w1 = td->w * td->cols; int h1 = td->h * td->rows; /* Draw the Term black */ rectfill(screen, x1, y1, x1 + w1, y1 + h1, 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; /* Draw a black block */ rectfill(screen, x1, y1, x1 + w1, y1 + h1, 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); /* 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); } /* * 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 that GRX FNT files can have a variable number of characters, so you * must verify that any "necessary" characters exist before using them. * * The GRX FNT files were developed by ???. */ /* * Magic value */ #define FONTMAGIC 0x19590214L /* * Forward declare */ typedef struct FNTfile_header FNTfile_header; /* * .FNT file header */ struct FNTfile_header { 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]; }; /* * A "bitmap" is simply an array of bytes */ typedef byte *GRX_BITMAP; /* * Temporary space to store font 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>3)] & (1<width; /* temporary working area to store FNT bitmap */ temp = malloc(GRX_TMP_SIZE); for (t=0; tisfixed) 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; /* GRX font header */ FNTfile_header hdr; /* number of characters in the font */ int numchar; /* table of widths for each character */ unsigned short *wtable = NULL; /* array of font bitmaps */ GRX_BITMAP *bmp; /* the Allegro font */ FONT *font = NULL; FONT_PROP *font_prop; int c, c2, start, width; f = pack_fopen(fname, F_READ); if (!f) return NULL; /* read the header structure */ pack_fread(&hdr, sizeof(hdr), f); /* check magic number */ if (hdr.magic != FONTMAGIC) { pack_fclose(f); return NULL; } numchar = hdr.maxchar-hdr.minchar+1; /* proportional font */ if (!hdr.isfixed) { 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= 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> 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; /* 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++) { 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); } /* 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; /* 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"); } /* 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); /* 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 */