4 * Copyright (c) 1997 Ben Harrison, Robert Ruehlmann, and others
6 * This software may be copied and distributed for educational, research,
7 * and not for profit purposes provided that this copyright and statement
8 * are included in all such copies.
13 * This file helps Angband work with DOS computers.
15 * Adapted from "main-ibm.c".
17 * Author: Robert Ruehlmann (rr9@angband.org).
18 * See "http://thangorodrim.angband.org/".
20 * Initial framework (and some code) by Ben Harrison (benh@phial.com).
22 * This file requires the (free) DJGPP compiler (based on "gcc").
23 * See "http://www.delorie.com/djgpp/".
25 * This file uses the (free) "Allegro" library (SVGA graphics library).
26 * See "http://www.talula.demon.co.uk/allegro/".
28 * To compile this file, use "Makefile.dos", which defines "USE_DOS".
30 * See also "main-ibm.c" and "main-win.c".
33 * The "lib/user/pref.prf" file contains macro definitions and possible
34 * alternative color set definitions.
36 * The "lib/user/font.prf" contains attr/char mappings for use with the
37 * special fonts in the "lib/xtra/font/" directory.
39 * The "lib/user/graf.prf" contains attr/char mappings for use with the
40 * special bitmaps in the "lib/xtra/graf/" directory.
43 * Both "shift" keys are treated as "identical", and all the modifier keys
44 * (control, shift, alt) are ignored when used with "normal" keys, unless
45 * they modify the underlying "ascii" value of the key. You must use the
46 * new "user pref files" to be able to interact with the keypad and such.
48 * Note that "Term_xtra_dos_react()" allows runtime color, graphics,
49 * screen resolution, and sound modification.
52 * The sound code uses *.wav files placed in the "lib/xtra/sound" folder.
53 * Every sound-event can have several samples assigned to it and a random
54 * one will be played when the event occures. Look at the
55 * "lib/xtra/sound/sound.cfg" configuration file for more informations.
57 * The background music uses midi-files (and mod files) from the
58 * "lib/xtra/music" folder.
61 * Comment by Ben Harrison (benh@phial.com):
63 * On my Windows NT box, modes "VESA1" and "VESA2B" seem to work, but
64 * result in the game running with no visible output. Mode "VESA2L"
65 * clears the screen and then fails silently. All other modes fail
66 * instantly. To recover from such "invisible" modes, you can try
67 * typing escape, plus control-x, plus escape. XXX XXX XXX
79 #endif /* USE_MOD_FILES */
88 * Index of the first standard Angband color.
90 * All colors below this index are defined by
91 * the palette of the tiles-bitmap.
93 #define COLOR_OFFSET 240
97 * Maximum number of terminals
99 #define MAX_TERM_DATA 8
105 typedef struct term_data term_data;
135 #endif /* USE_GRAPHICS */
137 #ifdef USE_BACKGROUND
141 #endif /* USE_BACKGROUND */
146 * The current screen resolution
148 static int resolution;
150 #ifdef USE_BACKGROUND
153 * The background images
155 BITMAP *background[17];
157 #endif /* USE_BACKGROUND */
161 * An array of term_data's
163 static term_data data[MAX_TERM_DATA];
169 * Are graphics already initialized ?
171 static bool graphics_initialized = FALSE;
173 #endif /* USE_GRAPHICS */
177 * Small bitmap for the cursor
179 static BITMAP *cursor;
185 * Is the sound already initialized ?
187 static bool sound_initialized = FALSE;
189 # ifdef USE_MOD_FILES
191 * Is the mod-file support already initialized ?
193 static bool mod_file_initialized = FALSE;
195 # endif /* USE_MOD_FILES */
200 static int digi_volume;
201 static int midi_volume;
204 * The currently playing song
206 static MIDI *midi_song = NULL;
208 # ifdef USE_MOD_FILES
210 static JGMOD *mod_song = NULL;
212 # endif /* USE_MOD_FILES */
214 static int current_song;
217 * The number of available songs
219 static int song_number;
222 * The maximum number of available songs
224 #define MAX_SONGS 255
226 static char music_files[MAX_SONGS][16];
229 * The maximum number of samples per sound-event
231 #define SAMPLE_MAX 10
234 * An array of sound files
236 static SAMPLE* samples[SOUND_MAX][SAMPLE_MAX];
239 * The number of available samples for every event
241 static int sample_count[SOUND_MAX];
243 #endif /* USE_SOUND */
249 static char xtra_font_dir[1024];
250 static char xtra_graf_dir[1024];
251 static char xtra_sound_dir[1024];
252 static char xtra_music_dir[1024];
255 * List of used videomodes to reduce executable size
257 BEGIN_GFX_DRIVER_LIST
268 * List of used color depths to reduce executeable size
270 BEGIN_COLOR_DEPTH_LIST
276 * Keypress input modifier flags (hard-coded by DOS)
278 #define K_RSHIFT 0 /* Right shift key down */
279 #define K_LSHIFT 1 /* Left shift key down */
280 #define K_CTRL 2 /* Ctrl key down */
281 #define K_ALT 3 /* Alt key down */
282 #define K_SCROLL 4 /* Scroll lock on */
283 #define K_NUM 5 /* Num lock on */
284 #define K_CAPS 6 /* Caps lock on */
285 #define K_INSERT 7 /* Insert on */
291 static errr Term_xtra_dos_event(int v);
292 static void Term_xtra_dos_react(void);
293 static void Term_xtra_dos_clear(void);
294 static errr Term_xtra_dos(int n, int v);
295 static errr Term_user_dos(int n);
296 static errr Term_curs_dos(int x, int y);
297 static errr Term_wipe_dos(int x, int y, int n);
298 static errr Term_text_dos(int x, int y, int n, byte a, const char *cp);
299 static void Term_init_dos(term *t);
300 static void Term_nuke_dos(term *t);
301 static void term_data_link(term_data *td);
302 static void dos_dump_screen(void);
303 static void dos_quit_hook(cptr str);
304 static bool init_windows(void);
307 static bool init_sound(void);
308 static errr Term_xtra_dos_sound(int v);
309 static void play_song(void);
310 #endif /* USE_SOUND */
312 static bool init_graphics(void);
313 # ifdef USE_TRANSPARENCY
314 static errr Term_pict_dos(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp);
315 # else /* USE_TRANSPARENCY */
316 static errr Term_pict_dos(int x, int y, int n, const byte *ap, const char *cp);
317 # endif /* USE_TRANSPARENCY */
318 #endif /* USE_GRAPHICS */
322 * Process an event (check for a keypress)
324 * The keypress processing code is often the most system dependant part
325 * of Angband, since sometimes even the choice of compiler is important.
327 * For this file, we divide all keypresses into two catagories, first, the
328 * "normal" keys, including all keys required to play Angband, and second,
329 * the "special" keys, such as keypad keys, function keys, and various keys
330 * used in combination with various modifier keys.
332 * To simplify this file, we use Angband's "macro processing" ability, in
333 * combination with the "lib/user/pref.prf" file, to handle most of the
334 * "special" keys, instead of attempting to fully analyze them here. This
335 * file only has to determine when a "special" key has been pressed, and
336 * translate it into a simple string which signals the use of a "special"
337 * key, the set of modifiers used, if any, and the hardware scan code of
338 * the actual key which was pressed. To simplify life for the user, we
339 * treat both "shift" keys as identical modifiers.
341 * The final encoding is "^_MMMxSS\r", where "MMM" encodes the modifiers
342 * ("C" for control, "S" for shift, "A" for alt, or any ordered combination),
343 * and "SS" encodes the keypress (as the two "digit" hexidecimal encoding of
344 * the scan code of the key that was pressed), and the "^_" and "x" and "\r"
345 * delimit the encoding for recognition by the macro processing code.
347 * Some important facts about scan codes follow. All "normal" keys use
348 * scan codes from 1-58. The "function" keys use 59-68 (and 133-134).
349 * The "keypad" keys use 69-83. Escape uses 1. Enter uses 28. Control
350 * uses 29. Left Shift uses 42. Right Shift uses 54. PrtScrn uses 55.
351 * Alt uses 56. Space uses 57. CapsLock uses 58. NumLock uses 69.
352 * ScrollLock uses 70. The "keypad" keys which use scan codes 71-83
353 * are ordered KP7,KP8,KP9,KP-,KP4,KP5,KP6,KP+,KP1,KP2,KP3,INS,DEL.
355 * Using "bioskey(0x10)" instead of "bioskey(0)" apparently provides more
356 * information, including better access to the keypad keys in combination
357 * with various modifiers, but only works on "PC's after 6/1/86", and there
358 * is no way to determine if the function is provided on a machine. I have
359 * been told that without it you cannot detect, for example, control-left.
360 * The basic scan code + ascii value pairs returned by the keypad follow,
361 * with values in parentheses only available to "bioskey(0x10)".
364 * Norm: 352f 372a 4a2d 4e2b 4f00 5000 5100 4b00
365 * Shft: 352f 372a 4a2d 4e2b 4f31 5032 5133 4b34
366 * Ctrl: (9500) (9600) (8e00) (9000) 7500 (9100) 7600 7300
368 * 5 6 7 8 9 0 . Enter
369 * Norm: (4c00) 4d00 4700 4800 4900 5200 5300 (e00d)
370 * Shft: 4c35 4d36 4737 4838 4939 5230 532e (e00d)
371 * Ctrl: (8f00) 7400 7700 (8d00) 8400 (9200) (9300) (e00a)
373 * See "lib/user/pref-win.prf" for the "standard" macros for various keys.
375 * Certain "bizarre" keypad keys (such as "enter") return a "scan code"
376 * of "0xE0", and a "usable" ascii value. These keys should be treated
377 * like the normal keys, see below. XXX XXX XXX Note that these "special"
378 * keys could be prefixed with an optional "ctrl-^" which would allow them
379 * to be used in macros without hurting their use in normal situations.
381 * This function also appears in "main-ibm.c". XXX XXX XXX
383 * Addition for the DOS version: Dump-screen function with the
384 * "Ctrl-Print" key saves a bitmap with the screen contents to
385 * "lib/user/dump.bmp".
387 static errr Term_xtra_dos_event(int v)
395 /* Hack -- Check for a keypress */
396 if (!v && !bioskey(1)) return (1);
398 /* Wait for a keypress */
401 /* Access the "modifiers" */
404 /* Extract the "scan code" */
405 s = ((k >> 8) & 0xFF);
407 /* Extract the "ascii value" */
410 /* Process "normal" keys */
411 if ((s <= 58) || (s == 0xE0))
414 if (k) Term_keypress(k);
420 /* Extract the modifier flags */
421 if (i & (1 << K_CTRL)) mc = TRUE;
422 if (i & (1 << K_LSHIFT)) ms = TRUE;
423 if (i & (1 << K_RSHIFT)) ms = TRUE;
424 if (i & (1 << K_ALT)) ma = TRUE;
426 /* Dump the screen with "Ctrl-Print" */
427 if ((s == 0x72) && mc)
429 /* Dump the screen */
436 /* Begin a "macro trigger" */
439 /* Hack -- Send the modifiers */
440 if (mc) Term_keypress('C');
441 if (ms) Term_keypress('S');
442 if (ma) Term_keypress('A');
444 /* Introduce the hexidecimal scan code */
447 /* Encode the hexidecimal scan code */
448 Term_keypress(hexsym[s/16]);
449 Term_keypress(hexsym[s%16]);
451 /* End the "macro trigger" */
460 * React to global changes in the colors, graphics, and sound settings.
462 static void Term_xtra_dos_react(void)
466 #ifdef USE_SPECIAL_BACKGROUND
472 #endif /* USE_SPECIAL_BACKGROUND */
475 * Set the Angband colors
477 for (i = 0; i < 16; i++)
479 /* Extract desired values */
480 char rv = angband_color_table[i][1] >> 2;
481 char gv = angband_color_table[i][2] >> 2;
482 char bv = angband_color_table[i][3] >> 2;
484 RGB color = { rv, gv, bv };
486 set_color(COLOR_OFFSET + i, &color);
492 * Handle "arg_graphics"
494 if (use_graphics != arg_graphics)
496 /* Initialize (if needed) */
497 if (arg_graphics && !init_graphics())
500 plog("Cannot initialize graphics!");
503 arg_graphics = GRAPHICS_NONE;
507 use_graphics = arg_graphics;
510 #endif /* USE_GRAPHICS */
517 if (use_sound != arg_sound)
519 /* Clear the old song */
520 if (midi_song) destroy_midi(midi_song);
524 if (mod_file_initialized)
527 destroy_mod(mod_song);
529 #endif /* USE_MOD_FILES */
531 /* Initialize (if needed) */
532 if (arg_sound && !init_sound())
535 plog("Cannot initialize sound!");
542 use_sound = arg_sound;
545 #endif /* USE_SOUND */
547 #ifdef USE_SPECIAL_BACKGROUND
550 * Initialize the window backgrounds
552 for (i = 0; i < 8; i++)
557 for (j = 0; j < 16; j++)
559 if (op_ptr->window_flag[i] & (1L << j))
561 if (background[j + 1])
563 td->window_type = j + 1;
572 #endif /* USE_SPECIAL_BACKGROUND */
579 * Fills the terminal area with black color or with
580 * the background image
582 static void Term_xtra_dos_clear(void)
584 term_data *td = (term_data*)(Term->data);
586 #ifdef USE_BACKGROUND
588 #endif /* USE_BACKGROUND */
598 w1 = td->tile_wid * td->cols;
599 h1 = td->tile_hgt * td->rows;
601 #ifdef USE_BACKGROUND
603 bgrnd = td->window_type;
605 if (background[bgrnd])
607 /* Draw the background */
608 stretch_blit(background[bgrnd], screen,
609 0, 0, background[bgrnd]->w, background[bgrnd]->h,
614 #endif /* USE_BACKGROUND */
617 /* Draw the Term black */
619 x1, y1, x1 + w1 - 1, y1 + h1 - 1,
620 COLOR_OFFSET + TERM_DARK);
626 * Handle a "special request"
628 * The given parameters are "valid".
630 static errr Term_xtra_dos(int n, int v)
632 /* Analyze the request */
635 /* Make a "bell" noise */
636 case TERM_XTRA_NOISE:
638 /* Make a bell noise */
639 (void)write(1, "\007", 1);
645 /* Clear the screen */
646 case TERM_XTRA_CLEAR:
648 /* Clear the screen */
649 Term_xtra_dos_clear();
656 case TERM_XTRA_EVENT:
658 /* Process one event */
659 return (Term_xtra_dos_event(v));
663 case TERM_XTRA_FLUSH:
666 while (!Term_xtra_dos_event(FALSE));
672 /* Do something useful if bored */
673 case TERM_XTRA_BORED:
677 * Check for end of song and start a new one
679 if (!use_sound) return (0);
682 if (song_number && (midi_pos == -1) && !is_mod_playing())
683 #else /* USE_MOD_FILES */
684 if (song_number && (midi_pos == -1))
685 #endif /* USE_MOD_FILES */
689 /* Get a *new* song at random */
692 n = randint(song_number);
693 if (n != current_song) break;
699 /* We only have one song, so loop it */
707 #endif /* USE_SOUND */
713 /* React to global changes */
714 case TERM_XTRA_REACT:
716 /* Change the colors */
717 Term_xtra_dos_react();
723 /* Delay for some milliseconds */
724 case TERM_XTRA_DELAY:
726 /* Delay if needed */
736 case TERM_XTRA_SOUND:
738 return (Term_xtra_dos_sound(v));
741 #endif /* USE_SOUND */
745 /* Unknown request */
751 * Do a "user action" on the current "term"
753 static errr Term_user_dos(int n)
767 /* Print date and time of compilation */
768 prt(format("Compiled: %s %s\n", __TIME__, __DATE__), 1, 45);
770 /* Why are we here */
771 prt("DOS options", 2, 0);
773 /* Give some choices */
775 prt("(V) Sound Volume", 4, 5);
776 prt("(M) Music Volume", 5, 5);
777 #endif /* USE_SOUND */
783 strcpy(status, "On");
787 strcpy(status, "Off");
789 prt(format("(G) Graphics : %s", status), 7, 5);
791 #endif /* USE_GRAPHICS */
797 strcpy(status, "On");
801 strcpy(status, "Off");
803 prt(format("(S) Sound/Music : %s", status), 8, 5);
805 #endif /* USE_SOUND */
807 prt("(R) Screen resolution", 12, 5);
809 prt("(W) Save current options", 14, 5);
812 prt("Command: ", 18, 0);
818 if (k == ESCAPE) break;
829 prt("Command: Sound Volume", 18, 0);
831 /* Get a new value */
834 prt(format("Current Volume: %d", digi_volume), 22, 0);
835 prt("Change Volume (+, - or ESC to accept): ", 20, 0);
837 if (k == ESCAPE) break;
843 if (digi_volume > 255) digi_volume = 255;
849 if (digi_volume < 0) digi_volume = 0;
858 set_volume(digi_volume, -1);
868 prt("Command: Music Volume", 18, 0);
870 /* Get a new value */
873 prt(format("Current Volume: %d", midi_volume), 22, 0);
874 prt("Change Volume (+, - or ESC to accept): ", 20, 0);
876 if (k == ESCAPE) break;
882 if (midi_volume > 255) midi_volume = 255;
888 if (midi_volume < 0) midi_volume = 0;
897 set_volume(-1, midi_volume);
902 #endif /* USE_SOUND */
906 /* Switch graphics on/off */
910 /* Toggle "arg_graphics" */
911 arg_graphics = !arg_graphics;
913 /* React to changes */
914 Term_xtra_dos_react();
919 #else /* ANGBAND_2_8_1 */
921 #endif /* ANGBAND_2_8_1 */
925 #endif /* USE_GRAPHICS */
929 /* Sound/Music On/Off */
933 /* Toggle "arg_sound" */
934 arg_sound = !arg_sound;
936 /* React to changes */
937 Term_xtra_dos_react();
942 #endif /* USE_SOUND */
944 /* Screen Resolution */
955 prt("Command: Screen Resolution", 1, 0);
956 prt("Restart Angband to get the new screenmode.", 3, 0);
958 /* Get a list of the available presets */
962 sprintf(section, "Mode-%d", i);
964 /* Get new values or end the list */
965 if (!(w = get_config_int(section, "screen_wid", 0)) || (i == 16)) break;
966 h = get_config_int(section, "screen_hgt", 0);
968 /* Get a extra description of the resolution */
969 descr = get_config_string(section, "Description", "");
972 prt(format("(%d) %d x %d %s", i, w, h, descr), 4 + i, 0);
978 /* Get a new resolution */
979 prt(format("Screen Resolution : %d", resolution), 20, 0);
981 if (k == ESCAPE) break;
982 if (isdigit(k)) resolution = D2I(k);
984 /* Check for min, max value */
985 if ((resolution < 1) || (resolution >= i)) resolution = 1;
987 /* Save the resolution */
988 set_config_int("Angband", "Resolution", resolution);
995 /* Save current option */
999 prt("Saving current options", 18, 0);
1002 set_config_int("sound", "digi_volume", digi_volume);
1003 set_config_int("sound", "midi_volume", midi_volume);
1004 #endif /* USE_SOUND */
1005 set_config_int("Angband", "Graphics", arg_graphics);
1006 set_config_int("Angband", "Sound", arg_sound);
1011 /* Unknown option */
1018 /* Flush messages */
1023 Term_key_push(KTRL('R'));
1033 * The given parameters are "valid".
1035 static errr Term_curs_dos(int x, int y)
1037 term_data *td = (term_data*)(Term->data);
1042 x1 = x * td->tile_wid + td->x;
1043 y1 = y * td->tile_hgt + td->y;
1045 /* Draw the cursor */
1046 draw_sprite(screen, cursor, x1, y1);
1054 * Erase a block of the screen
1056 * The given parameters are "valid".
1058 static errr Term_wipe_dos(int x, int y, int n)
1060 term_data *td = (term_data*)(Term->data);
1062 #ifdef USE_BACKGROUND
1064 #endif /* USE_BACKGROUND */
1070 x1 = x * td->tile_wid + td->x;
1071 y1 = y * td->tile_hgt + td->y;
1074 w1 = n * td->tile_wid;
1077 #ifdef USE_BACKGROUND
1079 bgrnd = td->window_type;
1081 if (background[bgrnd])
1083 int source_x = x * background[bgrnd]->w / td->cols;
1084 int source_y = y * background[bgrnd]->h / td->rows;
1085 int source_w = n * background[bgrnd]->w / td->cols;
1086 int source_h = background[bgrnd]->h / td->rows;
1088 /* Draw the background */
1089 stretch_blit(background[bgrnd], screen,
1090 source_x, source_y, source_w, source_h,
1095 #endif /* USE_BACKGROUND */
1098 /* Draw a black block */
1099 rectfill(screen, x1, y1, x1 + w1 - 1, y1 + h1 - 1,
1100 COLOR_OFFSET + TERM_DARK);
1109 * Place some text on the screen using an attribute
1111 * The given parameters are "valid". Be careful with "a".
1113 * The string "cp" has length "n" and is NOT null-terminated.
1115 static errr Term_text_dos(int x, int y, int n, byte a, const char *cp)
1117 term_data *td = (term_data*)(Term->data);
1126 x1 = x * td->tile_wid + td->x;
1127 y1 = y * td->tile_hgt + td->y;
1129 /* Erase old contents */
1130 Term_wipe_dos(x, y, n);
1132 #ifdef USE_SPECIAL_BACKGROUND
1134 /* Show text in black in the message window */
1135 if (op_ptr->window_flag[td->number] & (PW_MESSAGE)) a = 0;
1137 #endif /* USE_SPECIAL_BACKGROUND */
1139 /* No stretch needed */
1140 if (td->font_wid == td->tile_wid)
1142 /* Copy the string */
1143 for (i = 0; i < n; ++i) text[i] = cp[i];
1149 textout(screen, td->font, text, x1, y1,
1150 COLOR_OFFSET + (a & 0x0F));
1152 /* Stretch needed */
1158 /* Write the chars to the screen */
1159 for (i = 0; i < n; ++i)
1161 /* Build a one character string */
1164 /* Dump some text */
1165 textout(screen, td->font, text, x1, y1,
1166 COLOR_OFFSET + (a & 0x0F));
1181 * Place some attr/char pairs on the screen
1183 * The given parameters are "valid".
1185 * To prevent crashes, we must not only remove the high bits of the
1186 * "ap[i]" and "cp[i]" values, but we must map the resulting value
1187 * onto the legal bitmap size, which is normally 32x32. XXX XXX XXX
1189 #ifdef USE_TRANSPARENCY
1190 static errr Term_pict_dos(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp)
1191 #else /* USE_TRANSPARENCY */
1192 static errr Term_pict_dos(int x, int y, int n, const byte *ap, const char *cp)
1193 #endif /* USE_TRANSPARENCY */
1195 term_data *td = (term_data*)(Term->data);
1204 # ifdef USE_TRANSPARENCY
1208 # endif /* USE_TRANSPARENCY */
1214 /* Location (window) */
1218 /* Dump the tiles */
1219 for (i = 0; i < n; i++)
1221 /* Location (bitmap) */
1222 x2 = (cp[i] & 0x7F) * w;
1223 y2 = (ap[i] & 0x7F) * h;
1225 # ifdef USE_TRANSPARENCY
1226 x3 = (tcp[i] & 0x7F) * w;
1227 y3 = (tap[i] & 0x7F) * h;
1229 /* Blit the tile to the screen */
1230 blit(td->tiles, screen, x3, y3, x1, y1, w, h);
1232 /* Blit the tile to the screen */
1233 masked_blit(td->tiles, screen, x2, y2, x1, y1, w, h);
1235 # else /* USE_TRANSPARENCY */
1237 /* Blit the tile to the screen */
1238 blit(td->tiles, screen, x2, y2, x1, y1, w, h);
1240 # endif /* USE_TRANSPARENCY */
1242 /* Advance (window) */
1250 #endif /* USE_GRAPHICS */
1256 static void Term_init_dos(term *t)
1265 static void Term_nuke_dos(term *t)
1267 term_data *td = (term_data*)(t->data);
1269 /* Free the terminal font */
1270 if (td->font) destroy_font(td->font);
1274 /* Free the terminal bitmap */
1275 if (td->tiles) destroy_bitmap(td->tiles);
1277 #endif /* USE_GRAPHICS */
1283 * Instantiate a "term_data" structure
1285 static void term_data_link(term_data *td)
1289 /* Initialize the term */
1290 term_init(t, td->cols, td->rows, 255);
1292 /* Use a "software" cursor */
1293 t->soft_cursor = TRUE;
1295 /* Ignore the "TERM_XTRA_BORED" action */
1296 t->never_bored = FALSE;
1298 /* Ignore the "TERM_XTRA_FROSH" action */
1299 t->never_frosh = TRUE;
1301 /* Erase with "white space" */
1302 t->attr_blank = TERM_WHITE;
1303 t->char_blank = ' ';
1305 /* Prepare the init/nuke hooks */
1306 t->init_hook = Term_init_dos;
1307 t->nuke_hook = Term_nuke_dos;
1309 /* Prepare the template hooks */
1310 t->xtra_hook = Term_xtra_dos;
1311 t->curs_hook = Term_curs_dos;
1312 t->wipe_hook = Term_wipe_dos;
1313 t->user_hook = Term_user_dos;
1314 t->text_hook = Term_text_dos;
1318 /* Prepare the graphics hook */
1319 t->pict_hook = Term_pict_dos;
1321 /* Use "Term_pict" for "graphic" data */
1322 t->higher_pict = TRUE;
1324 #endif /* USE_GRAPHICS */
1326 /* Remember where we came from */
1327 t->data = (vptr)(td);
1332 * Shut down visual system, then fall back into standard "quit()"
1334 static void dos_quit_hook(cptr str)
1338 /* Destroy sub-windows */
1339 for (i = MAX_TERM_DATA - 1; i >= 1; i--)
1342 if (!angband_term[i]) continue;
1345 term_nuke(angband_term[i]);
1349 /* Free all resources */
1350 if (cursor) destroy_bitmap(cursor);
1352 #ifdef USE_BACKGROUND
1354 /* Free the background bitmaps */
1355 for (i = 0; i < 17; i++)
1357 if (background[i]) destroy_bitmap(background[i]);
1360 #endif /* USE_BACKGROUND */
1365 if (sound_initialized)
1367 /* Destroy samples */
1368 for (i = 1; i < SOUND_MAX; i++)
1372 for (j = 0; j < sample_count[i]; j++)
1374 if (samples[i][j]) destroy_sample(samples[i][j]);
1379 /* Clear the old song */
1380 if (midi_song) destroy_midi(midi_song);
1382 # ifdef USE_MOD_FILES
1383 if (mod_file_initialized)
1386 destroy_mod(mod_song);
1388 # endif /* USE_MOD_FILES */
1390 #endif /* USE_SOUND */
1392 /* Shut down Allegro */
1398 * Dump the screen to "lib/user/dump.bmp"
1400 static void dos_dump_screen(void)
1402 /* Bitmap and palette of the screen */
1407 char filename[1024];
1409 /* Get bitmap and palette of the screen */
1410 bmp = create_sub_bitmap(screen, 0, 0, SCREEN_W, SCREEN_H);
1413 /* Build the filename for the screen-dump */
1414 path_build(filename, 1024, ANGBAND_DIR_USER, "dump.bmp");
1417 save_bmp(filename, bmp, pal);
1419 /* Free up the memory */
1420 if (bmp) destroy_bitmap(bmp);
1422 /* Success message */
1423 msg_print("Screen dump saved.");
1428 /* GRX font file reader by Mark Wodrich.
1430 * GRX FNT files consist of the header data (see struct below). If the font
1431 * is proportional, followed by a table of widths per character (unsigned
1432 * shorts). Then, the data for each character follows. 1 bit/pixel is used,
1433 * with each line of the character stored in contiguous bytes. High bit of
1434 * first byte is leftmost pixel of line.
1436 * Note : FNT files can have a variable number of characters, so we must
1437 * check that the chars 32..127 exist.
1440 #define FONTMAGIC 0x19590214L
1443 /* .FNT file header */
1446 unsigned long magic;
1447 unsigned long bmpsize;
1448 unsigned short width;
1449 unsigned short height;
1450 unsigned short minchar;
1451 unsigned short maxchar;
1452 unsigned short isfixed;
1453 unsigned short reserved;
1454 unsigned short baseline;
1455 unsigned short undwidth;
1461 #define GRX_TMP_SIZE 4096
1465 /* converts images from bit to byte format */
1466 static void convert_grx_bitmap(int width, int height, unsigned char *src, unsigned char *dest)
1468 unsigned short x, y, bytes_per_line;
1469 unsigned char bitpos, bitset;
1471 bytes_per_line = (width + 7) >> 3;
1473 for (y = 0; y < height; y++)
1475 for (x = 0; x < width; x++)
1478 bitset = !!(src[(bytes_per_line * y) + (x >> 3)] & (1 << bitpos));
1479 dest[y * width + x] = bitset;
1486 /* reads GRX format images from disk */
1487 static unsigned char **load_grx_bmps(PACKFILE *f, FNTfile_header *hdr, int numchar, unsigned short *wtable)
1489 int t, width, bmp_size;
1490 unsigned char *temp;
1491 unsigned char **bmp;
1493 /* alloc array of bitmap pointers */
1494 bmp = malloc(sizeof(unsigned char *) * numchar);
1496 /* assume it's fixed width for now */
1499 /* temporary working area to store FNT bitmap */
1500 temp = malloc(GRX_TMP_SIZE);
1502 for (t = 0; t < numchar; t++)
1504 /* if prop. get character width */
1508 /* work out how many bytes to read */
1509 bmp_size = ((width + 7) >> 3) * hdr->height;
1511 /* oops, out of space! */
1512 if (bmp_size > GRX_TMP_SIZE)
1515 for (t--; t >= 0; t--)
1521 /* alloc space for converted bitmap */
1522 bmp[t] = malloc(width * hdr->height);
1525 pack_fread(temp, bmp_size, f);
1527 /* convert to 1 byte/pixel */
1528 convert_grx_bitmap(width, hdr->height, temp, bmp[t]);
1537 /* main import routine for the GRX font format */
1538 static FONT *import_grx_font(char *fname)
1541 FNTfile_header hdr; /* GRX font header */
1542 int numchar; /* number of characters in the font */
1543 unsigned short *wtable = NULL; /* table of widths for each character */
1544 unsigned char **bmp; /* array of font bitmaps */
1545 FONT *font = NULL; /* the Allegro font */
1546 FONT_PROP *font_prop;
1547 int c, c2, start, width;
1549 f = pack_fopen(fname, F_READ);
1553 pack_fread(&hdr, sizeof(hdr), f); /* read the header structure */
1555 if (hdr.magic != FONTMAGIC) /* check magic number */
1561 numchar = hdr.maxchar - hdr.minchar + 1;
1563 if (!hdr.isfixed) /* proportional font */
1565 wtable = malloc(sizeof(unsigned short) * numchar);
1566 pack_fread(wtable, sizeof(unsigned short) * numchar, f);
1569 bmp = load_grx_bmps(f, &hdr, numchar, wtable);
1576 font = malloc(sizeof(FONT));
1578 font->dat.dat_prop = font_prop = malloc(sizeof(FONT_PROP));
1579 font_prop->render = NULL;
1581 start = 32 - hdr.minchar;
1584 for (c = 0; c <FONT_SIZE; c++)
1588 if ((c2 >= 0) && (c2 < numchar))
1593 font_prop->dat[c] = create_bitmap_ex(8, width, hdr.height);
1594 memcpy(font_prop->dat[c]->dat, bmp[c2], width * hdr.height);
1598 font_prop->dat[c] = create_bitmap_ex(8, 8, hdr.height);
1599 clear(font_prop->dat[c]);
1612 for (c = 0; c < numchar; c++)
1623 * Initialize the terminal windows
1625 static bool init_windows(void)
1633 char filename[1024];
1638 sprintf(section, "Mode-%d", resolution);
1640 /* Get number of windows */
1641 num_windows = get_config_int(section, "num_windows", 1);
1644 if (num_windows > 8) num_windows = 8;
1646 /* Init the terms */
1647 for (i = 0; i < num_windows; i++)
1650 WIPE(td, term_data);
1653 sprintf(section, "Term-%d-%d", resolution, i);
1658 /* Coordinates of left top corner */
1659 td->x = get_config_int(section, "x", 0);
1660 td->y = get_config_int(section, "y", 0);
1662 /* Rows and cols of term */
1663 td->rows = get_config_int(section, "rows", 24);
1664 td->cols = get_config_int(section, "cols", 80);
1667 td->tile_wid = get_config_int(section, "tile_wid", 8);
1668 td->tile_hgt = get_config_int(section, "tile_hgt", 13);
1671 td->font_wid = get_config_int(section, "tile_wid", 8);
1672 td->font_hgt = get_config_int(section, "tile_hgt", 13);
1674 /* Get font filename */
1675 strcpy(buf, get_config_string(section, "font_file", "xm8x13.fnt"));
1677 /* Build the name of the font file */
1678 path_build(filename, 1024, xtra_font_dir, buf);
1680 /* Load a "*.fnt" file */
1681 if (suffix(filename, ".fnt"))
1683 /* Load the font file */
1684 if (!(td->font = import_grx_font(filename)))
1686 quit_fmt("Error reading font file '%s'", filename);
1690 /* Load a "*.dat" file */
1691 else if (suffix(filename, ".dat"))
1695 /* Load the font file */
1696 if (!(fontdata = load_datafile(filename)))
1698 quit_fmt("Error reading font file '%s'", filename);
1701 /* Save the font data */
1702 td->font = fontdata[1].dat;
1704 /* Unload the font file */
1705 unload_datafile_object(fontdata);
1711 quit_fmt("Unknown suffix in font file '%s'", filename);
1716 angband_term[i] = &td->t;
1724 #ifdef USE_BACKGROUND
1727 * Initialize the window backgrounds
1729 static void init_background(void)
1733 char filename[1024];
1737 PALLETE background_pallete;
1739 /* Get the backgrounds */
1740 for (i = 0; i < 16; i++)
1742 /* Get background filename */
1743 strcpy(buf, get_config_string("Background", format("Background-%d", i), ""));
1745 /* Build the filename for the background-bitmap */
1746 path_build(filename, 1024, xtra_graf_dir, buf);
1748 /* Try to open the bitmap file */
1749 background[i] = load_bitmap(filename, background_pallete);
1752 #ifndef USE_SPECIAL_BACKGROUND
1754 * Set the palette for the background
1758 set_palette_range(background_pallete, 0, COLOR_OFFSET - 1, 0);
1760 #endif /* USE_SPECIAL_BACKGROUND */
1763 #endif /* USE_BACKGROUND */
1769 * Initialize graphics
1771 static bool init_graphics(void)
1773 char filename[1024];
1775 char name_tiles[128];
1777 /* Large bitmap for the tiles */
1778 BITMAP *tiles = NULL;
1779 PALLETE tiles_pallete;
1781 /* Size of each bitmap tile */
1787 if (!graphics_initialized)
1790 sprintf(section, "Mode-%d", resolution);
1792 /* Get bitmap tile size */
1793 bitmap_wid = get_config_int(section, "bitmap_wid", 8);
1794 bitmap_hgt = get_config_int(section, "bitmap_hgt", 8);
1796 /* Get bitmap filename */
1797 strcpy(name_tiles, get_config_string(section, "bitmap_file", "8x8.bmp"));
1799 /* Get number of windows */
1800 num_windows = get_config_int(section, "num_windows", 1);
1802 /* Build the name of the bitmap file */
1803 path_build(filename, 1024, xtra_graf_dir, name_tiles);
1805 /* Open the bitmap file */
1806 if ((tiles = load_bitmap(filename, tiles_pallete)) != NULL)
1811 * Set the graphics mode to "new" if Adam Bolt's
1812 * new 16x16 tiles are used.
1814 ANGBAND_GRAF = get_config_string(section, "graf-mode", "old");
1816 /* Use transparent blits */
1817 if (streq(ANGBAND_GRAF, "new"))
1818 use_transparency = TRUE;
1820 /* Select the bitmap pallete */
1821 set_palette_range(tiles_pallete, 0, COLOR_OFFSET - 1, 0);
1823 /* Prepare the graphics */
1824 for (i = 0; i < num_windows; i++)
1836 cols = tiles->w / bitmap_wid;
1837 rows = tiles->h / bitmap_hgt;
1839 width = td->tile_wid * cols;
1840 height = td->tile_hgt * rows;
1842 /* Initialize the tile graphics */
1843 td->tiles = create_bitmap(width, height);
1845 for (row = 0; row < rows; ++row)
1847 src_y = row * bitmap_hgt;
1848 tgt_y = row * td->tile_hgt;
1850 for (col = 0; col < cols; ++col)
1852 src_x = col * bitmap_wid;
1853 tgt_x = col * td->tile_wid;
1855 stretch_blit(tiles, td->tiles,
1857 bitmap_wid, bitmap_hgt,
1859 td->tile_wid, td->tile_hgt);
1864 /* Free the old tiles bitmap */
1865 if (tiles) destroy_bitmap(tiles);
1867 graphics_initialized = TRUE;
1881 #endif /* USE_GRAPHICS */
1887 * We try to get a list of the available sound-files from "lib/xtra/sound/sound.cfg"
1888 * and then preload the samples. Every Angband-sound-event can have several samples
1889 * assigned. Angband will randomly select which is played. This makes it easy to
1890 * create "sound-packs", just copy wav-files into the "lib/xtra/sound/" folder and
1891 * add the filenames to "sound.cfg" in the same folder.
1893 static bool init_sound(void)
1898 char filename[1024];
1903 if (sound_initialized) return (TRUE);
1905 reserve_voices(16, -1);
1907 /* Initialize Allegro sound */
1908 if (!install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, NULL))
1910 #ifdef USE_MOD_FILES
1912 * Try to enable support for MOD-, and S3M-files
1913 * The parameter for install_mod() is the number
1914 * of channels reserved for the MOD/S3M-file.
1916 if (install_mod(8) > 0) mod_file_initialized = TRUE;
1917 #endif /* USE_MOD_FILES */
1919 /* Access the new sample */
1920 path_build(filename, 1024, xtra_sound_dir, "sound.cfg");
1922 /* Read config info from "lib/xtra/sound/sound.cfg" */
1923 override_config_file(filename);
1926 strcpy(section, "Sound");
1928 /* Prepare the sounds */
1929 for (i = 1; i < SOUND_MAX; i++)
1931 /* Get the sample names */
1932 argv = get_config_argv(section, angband_sound_name[i], &sample_count[i]);
1934 /* Limit the number of samples */
1935 if (sample_count[i] > SAMPLE_MAX) sample_count[i] = SAMPLE_MAX;
1937 for (j = 0; j < sample_count[i]; j++)
1939 /* Access the new sample */
1940 path_build(filename, 1024, xtra_sound_dir, argv[j]);
1942 /* Load the sample */
1943 samples[i][j] = load_sample(filename);
1948 * Get a list of music files
1950 #ifdef USE_MOD_FILES
1951 if (mod_file_initialized)
1953 done = findfirst(format("%s/*.*", xtra_music_dir), &f, FA_ARCH|FA_RDONLY);
1956 #endif /* USE_MOD_FILES */
1957 done = findfirst(format("%s/*.mid", xtra_music_dir), &f, FA_ARCH|FA_RDONLY);
1960 while (!done && (song_number <= MAX_SONGS))
1962 /* Add music files */
1964 strcpy(music_files[song_number], f.ff_name);
1968 done = findnext(&f);
1971 /* Use "angdos.cfg" */
1972 override_config_file("angdos.cfg");
1975 strcpy(section, "Sound");
1977 /* Get the volume setting */
1978 digi_volume = get_config_int(section, "digi_volume", 255);
1979 midi_volume = get_config_int(section, "midi_volume", 255);
1981 /* Set the volume */
1982 set_volume(digi_volume, midi_volume);
1996 static errr Term_xtra_dos_sound(int v)
2000 /* Sound disabled */
2001 if (!use_sound) return (1);
2004 if ((v < 0) || (v >= SOUND_MAX)) return (1);
2006 /* Get a random sample from the available ones */
2007 n = rand_int(sample_count[v]);
2009 /* Play the sound, catch errors */
2012 return (play_sample(samples[v][n], 255, 128, 1000, 0) == 0);
2023 static void play_song(void)
2027 /* Clear the old song */
2028 if (midi_song) destroy_midi(midi_song);
2031 #ifdef USE_MOD_FILES
2032 if (mod_file_initialized)
2035 destroy_mod(mod_song);
2037 #endif /* USE_MOD_FILES */
2039 /* Access the new song */
2040 path_build(filename, 1024, xtra_music_dir, music_files[current_song - 1]);
2042 /* Load and play the new song */
2043 midi_song = load_midi(filename);
2047 play_midi(midi_song, 0);
2049 #ifdef USE_MOD_FILES
2050 else if (mod_file_initialized)
2052 mod_song = load_mod(filename);
2054 if (mod_song) play_mod(mod_song, FALSE);
2056 #endif /* USE_MOD_FILES */
2059 #endif /* USE_SOUND */
2063 * Attempt to initialize this file
2065 * Hack -- we assume that "blank space" should be "white space"
2066 * (and not "black space" which might make more sense).
2068 * Note the use of "((x << 2) | (x >> 4))" to "expand" a 6 bit value
2069 * into an 8 bit value, without losing much precision, by using the 2
2070 * most significant bits as the least significant bits in the new value.
2072 * We should attempt to "share" bitmaps (and fonts) between windows
2073 * with the same "tile" size. XXX XXX XXX
2084 /* Initialize the Allegro library (never fails) */
2085 (void)allegro_init();
2087 /* Install timer support for music and sound */
2090 /* Read config info from filename */
2091 set_config_file("angdos.cfg");
2094 strcpy(section, "Angband");
2096 /* Get screen size */
2097 resolution = get_config_int(section, "Resolution", 1);
2100 sprintf(section, "Mode-%d", resolution);
2102 /* Get the screen dimensions */
2103 screen_wid = get_config_int(section, "screen_wid", 640);
2104 screen_hgt = get_config_int(section, "screen_hgt", 480);
2106 /* Set the color depth */
2109 /* Auto-detect, and instantiate, the appropriate graphics mode */
2110 if ((set_gfx_mode(GFX_AUTODETECT, screen_wid, screen_hgt, 0, 0)) < 0)
2113 * Requested graphics mode is not available
2114 * We retry with the basic 640x480 mode
2118 if ((set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0)) < 0)
2120 char error_text[1024];
2122 /* Save the Allegro error description */
2123 strcpy(error_text, allegro_error);
2125 /* Shut down Allegro */
2128 /* Print the error description */
2129 plog_fmt("Error selecting screen mode: %s", error_text);
2136 /* Hook in "z-util.c" hook */
2137 quit_aux = dos_quit_hook;
2139 /* Build the "graf" path */
2140 path_build(xtra_graf_dir, 1024, ANGBAND_DIR_XTRA, "graf");
2142 /* Build the "font" path */
2143 path_build(xtra_font_dir, 1024, ANGBAND_DIR_XTRA, "font");
2145 /* Build the "sound" path */
2146 path_build(xtra_sound_dir, 1024, ANGBAND_DIR_XTRA, "sound");
2148 /* Build the "music" path */
2149 path_build(xtra_music_dir, 1024, ANGBAND_DIR_XTRA, "music");
2151 /* Initialize the windows */
2156 /* Look for the sound preferences in "angdos.cfg" */
2159 arg_sound = get_config_int("Angband", "Sound", TRUE);
2162 #endif /* USE_SOUND */
2166 /* Look for the graphic preferences in "angdos.cfg" */
2169 arg_graphics = get_config_int("Angband", "Graphics", GRAPHICS_ORIGINAL);
2172 #endif /* USE_GRAPHICS */
2174 /* Initialize the "complex" RNG for the midi-shuffle function */
2176 Rand_state_init(time(NULL));
2178 /* Set the Angband colors/graphics/sound mode */
2179 Term_xtra_dos_react();
2181 #ifdef USE_BACKGROUND
2183 /* Initialize the background graphics */
2186 #endif /* USE_BACKGROUND */
2188 /* Clear the screen */
2189 clear_to_color(screen, COLOR_OFFSET + TERM_DARK);
2194 /* Build a cursor bitmap */
2195 cursor = create_bitmap(td->tile_wid, td->tile_hgt);
2197 /* Erase the cursor sprite */
2200 /* Draw the cursor sprite (yellow rectangle) */
2201 rect(cursor, 0, 0, td->tile_wid - 1, td->tile_hgt - 1,
2202 COLOR_OFFSET + TERM_YELLOW);
2204 /* Activate the main term */
2205 Term_activate(angband_term[0]);
2207 /* Place the cursor */
2208 Term_curs_dos(0, 0);
2210 #ifdef USE_BACKGROUND
2212 /* Use transparent text */
2215 #endif /* USE_BACKGROUND */
2221 #endif /* USE_DOS */