1 /* File: main-dos.c */
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 static errr Term_pict_dos(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp);
314 #endif /* USE_GRAPHICS */
318 * Process an event (check for a keypress)
320 * The keypress processing code is often the most system dependant part
321 * of Angband, since sometimes even the choice of compiler is important.
323 * For this file, we divide all keypresses into two catagories, first, the
324 * "normal" keys, including all keys required to play Angband, and second,
325 * the "special" keys, such as keypad keys, function keys, and various keys
326 * used in combination with various modifier keys.
328 * To simplify this file, we use Angband's "macro processing" ability, in
329 * combination with the "lib/user/pref.prf" file, to handle most of the
330 * "special" keys, instead of attempting to fully analyze them here. This
331 * file only has to determine when a "special" key has been pressed, and
332 * translate it into a simple string which signals the use of a "special"
333 * key, the set of modifiers used, if any, and the hardware scan code of
334 * the actual key which was pressed. To simplify life for the user, we
335 * treat both "shift" keys as identical modifiers.
337 * The final encoding is "^_MMMxSS\r", where "MMM" encodes the modifiers
338 * ("C" for control, "S" for shift, "A" for alt, or any ordered combination),
339 * and "SS" encodes the keypress (as the two "digit" hexidecimal encoding of
340 * the scan code of the key that was pressed), and the "^_" and "x" and "\r"
341 * delimit the encoding for recognition by the macro processing code.
343 * Some important facts about scan codes follow. All "normal" keys use
344 * scan codes from 1-58. The "function" keys use 59-68 (and 133-134).
345 * The "keypad" keys use 69-83. Escape uses 1. Enter uses 28. Control
346 * uses 29. Left Shift uses 42. Right Shift uses 54. PrtScrn uses 55.
347 * Alt uses 56. Space uses 57. CapsLock uses 58. NumLock uses 69.
348 * ScrollLock uses 70. The "keypad" keys which use scan codes 71-83
349 * are ordered KP7,KP8,KP9,KP-,KP4,KP5,KP6,KP+,KP1,KP2,KP3,INS,DEL.
351 * Using "bioskey(0x10)" instead of "bioskey(0)" apparently provides more
352 * information, including better access to the keypad keys in combination
353 * with various modifiers, but only works on "PC's after 6/1/86", and there
354 * is no way to determine if the function is provided on a machine. I have
355 * been told that without it you cannot detect, for example, control-left.
356 * The basic scan code + ascii value pairs returned by the keypad follow,
357 * with values in parentheses only available to "bioskey(0x10)".
360 * Norm: 352f 372a 4a2d 4e2b 4f00 5000 5100 4b00
361 * Shft: 352f 372a 4a2d 4e2b 4f31 5032 5133 4b34
362 * Ctrl: (9500) (9600) (8e00) (9000) 7500 (9100) 7600 7300
364 * 5 6 7 8 9 0 . Enter
365 * Norm: (4c00) 4d00 4700 4800 4900 5200 5300 (e00d)
366 * Shft: 4c35 4d36 4737 4838 4939 5230 532e (e00d)
367 * Ctrl: (8f00) 7400 7700 (8d00) 8400 (9200) (9300) (e00a)
369 * See "lib/user/pref-win.prf" for the "standard" macros for various keys.
371 * Certain "bizarre" keypad keys (such as "enter") return a "scan code"
372 * of "0xE0", and a "usable" ascii value. These keys should be treated
373 * like the normal keys, see below. XXX XXX XXX Note that these "special"
374 * keys could be prefixed with an optional "ctrl-^" which would allow them
375 * to be used in macros without hurting their use in normal situations.
377 * This function also appears in "main-ibm.c". XXX XXX XXX
379 * Addition for the DOS version: Dump-screen function with the
380 * "Ctrl-Print" key saves a bitmap with the screen contents to
381 * "lib/user/dump.bmp".
383 static errr Term_xtra_dos_event(int v)
391 /* Hack -- Check for a keypress */
392 if (!v && !bioskey(1)) return (1);
394 /* Wait for a keypress */
397 /* Access the "modifiers" */
400 /* Extract the "scan code" */
401 s = ((k >> 8) & 0xFF);
403 /* Extract the "ascii value" */
406 /* Process "normal" keys */
407 if ((s <= 58) || (s == 0xE0))
410 if (k) Term_keypress(k);
416 /* Extract the modifier flags */
417 if (i & (1 << K_CTRL)) mc = TRUE;
418 if (i & (1 << K_LSHIFT)) ms = TRUE;
419 if (i & (1 << K_RSHIFT)) ms = TRUE;
420 if (i & (1 << K_ALT)) ma = TRUE;
422 /* Dump the screen with "Ctrl-Print" */
423 if ((s == 0x72) && mc)
425 /* Dump the screen */
432 /* Begin a "macro trigger" */
435 /* Hack -- Send the modifiers */
436 if (mc) Term_keypress('C');
437 if (ms) Term_keypress('S');
438 if (ma) Term_keypress('A');
440 /* Introduce the hexidecimal scan code */
443 /* Encode the hexidecimal scan code */
444 Term_keypress(hexsym[s/16]);
445 Term_keypress(hexsym[s%16]);
447 /* End the "macro trigger" */
456 * React to global changes in the colors, graphics, and sound settings.
458 static void Term_xtra_dos_react(void)
462 #ifdef USE_SPECIAL_BACKGROUND
468 #endif /* USE_SPECIAL_BACKGROUND */
471 * Set the Angband colors
473 for (i = 0; i < 16; i++)
475 /* Extract desired values */
476 char rv = angband_color_table[i][1] >> 2;
477 char gv = angband_color_table[i][2] >> 2;
478 char bv = angband_color_table[i][3] >> 2;
480 RGB color = { rv, gv, bv };
482 set_color(COLOR_OFFSET + i, &color);
488 * Handle "arg_graphics"
490 if (use_graphics != arg_graphics)
492 /* Initialize (if needed) */
493 if (arg_graphics && !init_graphics())
496 plog("Cannot initialize graphics!");
499 arg_graphics = GRAPHICS_NONE;
503 use_graphics = arg_graphics;
506 #endif /* USE_GRAPHICS */
513 if (use_sound != arg_sound)
515 /* Clear the old song */
516 if (midi_song) destroy_midi(midi_song);
520 if (mod_file_initialized)
523 destroy_mod(mod_song);
525 #endif /* USE_MOD_FILES */
527 /* Initialize (if needed) */
528 if (arg_sound && !init_sound())
531 plog("Cannot initialize sound!");
538 use_sound = arg_sound;
541 #endif /* USE_SOUND */
543 #ifdef USE_SPECIAL_BACKGROUND
546 * Initialize the window backgrounds
548 for (i = 0; i < 8; i++)
553 for (j = 0; j < 16; j++)
555 if (op_ptr->window_flag[i] & (1L << j))
557 if (background[j + 1])
559 td->window_type = j + 1;
568 #endif /* USE_SPECIAL_BACKGROUND */
575 * Fills the terminal area with black color or with
576 * the background image
578 static void Term_xtra_dos_clear(void)
580 term_data *td = (term_data*)(Term->data);
582 #ifdef USE_BACKGROUND
584 #endif /* USE_BACKGROUND */
594 w1 = td->tile_wid * td->cols;
595 h1 = td->tile_hgt * td->rows;
597 #ifdef USE_BACKGROUND
599 bgrnd = td->window_type;
601 if (background[bgrnd])
603 /* Draw the background */
604 stretch_blit(background[bgrnd], screen,
605 0, 0, background[bgrnd]->w, background[bgrnd]->h,
610 #endif /* USE_BACKGROUND */
613 /* Draw the Term black */
615 x1, y1, x1 + w1 - 1, y1 + h1 - 1,
616 COLOR_OFFSET + TERM_DARK);
622 * Handle a "special request"
624 * The given parameters are "valid".
626 static errr Term_xtra_dos(int n, int v)
628 /* Analyze the request */
631 /* Make a "bell" noise */
632 case TERM_XTRA_NOISE:
634 /* Make a bell noise */
635 (void)write(1, "\007", 1);
641 /* Clear the screen */
642 case TERM_XTRA_CLEAR:
644 /* Clear the screen */
645 Term_xtra_dos_clear();
652 case TERM_XTRA_EVENT:
654 /* Process one event */
655 return (Term_xtra_dos_event(v));
659 case TERM_XTRA_FLUSH:
662 while (!Term_xtra_dos_event(FALSE));
668 /* Do something useful if bored */
669 case TERM_XTRA_BORED:
673 * Check for end of song and start a new one
675 if (!use_sound) return (0);
678 if (song_number && (midi_pos == -1) && !is_mod_playing())
679 #else /* USE_MOD_FILES */
680 if (song_number && (midi_pos == -1))
681 #endif /* USE_MOD_FILES */
685 /* Get a *new* song at random */
688 n = randint1(song_number);
689 if (n != current_song) break;
695 /* We only have one song, so loop it */
703 #endif /* USE_SOUND */
709 /* React to global changes */
710 case TERM_XTRA_REACT:
712 /* Change the colors */
713 Term_xtra_dos_react();
719 /* Delay for some milliseconds */
720 case TERM_XTRA_DELAY:
722 /* Delay if needed */
732 case TERM_XTRA_SOUND:
734 return (Term_xtra_dos_sound(v));
737 #endif /* USE_SOUND */
741 /* Unknown request */
747 * Do a "user action" on the current "term"
749 static errr Term_user_dos(int n)
763 /* Print date and time of compilation */
764 prt(format("Compiled: %s %s\n", __TIME__, __DATE__), 1, 45);
766 /* Why are we here */
767 prt("DOS options", 2, 0);
769 /* Give some choices */
771 prt("(V) Sound Volume", 4, 5);
772 prt("(M) Music Volume", 5, 5);
773 #endif /* USE_SOUND */
779 strcpy(status, "On");
783 strcpy(status, "Off");
785 prt(format("(G) Graphics : %s", status), 7, 5);
787 #endif /* USE_GRAPHICS */
793 strcpy(status, "On");
797 strcpy(status, "Off");
799 prt(format("(S) Sound/Music : %s", status), 8, 5);
801 #endif /* USE_SOUND */
803 prt("(R) Screen resolution", 12, 5);
805 prt("(W) Save current options", 14, 5);
808 prt("Command: ", 18, 0);
814 if (k == ESCAPE) break;
825 prt("Command: Sound Volume", 18, 0);
827 /* Get a new value */
830 prt(format("Current Volume: %d", digi_volume), 22, 0);
831 prt("Change Volume (+, - or ESC to accept): ", 20, 0);
833 if (k == ESCAPE) break;
839 if (digi_volume > 255) digi_volume = 255;
845 if (digi_volume < 0) digi_volume = 0;
854 set_volume(digi_volume, -1);
864 prt("Command: Music Volume", 18, 0);
866 /* Get a new value */
869 prt(format("Current Volume: %d", midi_volume), 22, 0);
870 prt("Change Volume (+, - or ESC to accept): ", 20, 0);
872 if (k == ESCAPE) break;
878 if (midi_volume > 255) midi_volume = 255;
884 if (midi_volume < 0) midi_volume = 0;
893 set_volume(-1, midi_volume);
898 #endif /* USE_SOUND */
902 /* Switch graphics on/off */
906 /* Toggle "arg_graphics" */
907 arg_graphics = !arg_graphics;
909 /* React to changes */
910 Term_xtra_dos_react();
915 #else /* ANGBAND_2_8_1 */
917 #endif /* ANGBAND_2_8_1 */
921 #endif /* USE_GRAPHICS */
925 /* Sound/Music On/Off */
929 /* Toggle "arg_sound" */
930 arg_sound = !arg_sound;
932 /* React to changes */
933 Term_xtra_dos_react();
938 #endif /* USE_SOUND */
940 /* Screen Resolution */
951 prt("Command: Screen Resolution", 1, 0);
952 prt("Restart Angband to get the new screenmode.", 3, 0);
954 /* Get a list of the available presets */
958 sprintf(section, "Mode-%d", i);
960 /* Get new values or end the list */
961 if (!(w = get_config_int(section, "screen_wid", 0)) || (i == 16)) break;
962 h = get_config_int(section, "screen_hgt", 0);
964 /* Get a extra description of the resolution */
965 descr = get_config_string(section, "Description", "");
968 prt(format("(%d) %d x %d %s", i, w, h, descr), 4 + i, 0);
974 /* Get a new resolution */
975 prt(format("Screen Resolution : %d", resolution), 20, 0);
977 if (k == ESCAPE) break;
978 if (isdigit(k)) resolution = D2I(k);
980 /* Check for min, max value */
981 if ((resolution < 1) || (resolution >= i)) resolution = 1;
983 /* Save the resolution */
984 set_config_int("Angband", "Resolution", resolution);
991 /* Save current option */
995 prt("Saving current options", 18, 0);
998 set_config_int("sound", "digi_volume", digi_volume);
999 set_config_int("sound", "midi_volume", midi_volume);
1000 #endif /* USE_SOUND */
1001 set_config_int("Angband", "Graphics", arg_graphics);
1002 set_config_int("Angband", "Sound", arg_sound);
1007 /* Unknown option */
1014 /* Flush messages */
1019 Term_key_push(KTRL('R'));
1029 * The given parameters are "valid".
1031 static errr Term_curs_dos(int x, int y)
1033 term_data *td = (term_data*)(Term->data);
1038 x1 = x * td->tile_wid + td->x;
1039 y1 = y * td->tile_hgt + td->y;
1041 /* Draw the cursor */
1042 draw_sprite(screen, cursor, x1, y1);
1050 * Erase a block of the screen
1052 * The given parameters are "valid".
1054 static errr Term_wipe_dos(int x, int y, int n)
1056 term_data *td = (term_data*)(Term->data);
1058 #ifdef USE_BACKGROUND
1060 #endif /* USE_BACKGROUND */
1066 x1 = x * td->tile_wid + td->x;
1067 y1 = y * td->tile_hgt + td->y;
1070 w1 = n * td->tile_wid;
1073 #ifdef USE_BACKGROUND
1075 bgrnd = td->window_type;
1077 if (background[bgrnd])
1079 int source_x = x * background[bgrnd]->w / td->cols;
1080 int source_y = y * background[bgrnd]->h / td->rows;
1081 int source_w = n * background[bgrnd]->w / td->cols;
1082 int source_h = background[bgrnd]->h / td->rows;
1084 /* Draw the background */
1085 stretch_blit(background[bgrnd], screen,
1086 source_x, source_y, source_w, source_h,
1091 #endif /* USE_BACKGROUND */
1094 /* Draw a black block */
1095 rectfill(screen, x1, y1, x1 + w1 - 1, y1 + h1 - 1,
1096 COLOR_OFFSET + TERM_DARK);
1105 * Place some text on the screen using an attribute
1107 * The given parameters are "valid". Be careful with "a".
1109 * The string "cp" has length "n" and is NOT null-terminated.
1111 static errr Term_text_dos(int x, int y, int n, byte a, const char *cp)
1113 term_data *td = (term_data*)(Term->data);
1122 x1 = x * td->tile_wid + td->x;
1123 y1 = y * td->tile_hgt + td->y;
1125 /* Erase old contents */
1126 Term_wipe_dos(x, y, n);
1128 #ifdef USE_SPECIAL_BACKGROUND
1130 /* Show text in black in the message window */
1131 if (op_ptr->window_flag[td->number] & (PW_MESSAGE)) a = 0;
1133 #endif /* USE_SPECIAL_BACKGROUND */
1135 /* No stretch needed */
1136 if (td->font_wid == td->tile_wid)
1138 /* Copy the string */
1139 for (i = 0; i < n; ++i) text[i] = cp[i];
1145 textout(screen, td->font, text, x1, y1,
1146 COLOR_OFFSET + (a & 0x0F));
1148 /* Stretch needed */
1154 /* Write the chars to the screen */
1155 for (i = 0; i < n; ++i)
1157 /* Build a one character string */
1160 /* Dump some text */
1161 textout(screen, td->font, text, x1, y1,
1162 COLOR_OFFSET + (a & 0x0F));
1177 * Place some attr/char pairs on the screen
1179 * The given parameters are "valid".
1181 * To prevent crashes, we must not only remove the high bits of the
1182 * "ap[i]" and "cp[i]" values, but we must map the resulting value
1183 * onto the legal bitmap size, which is normally 32x32. XXX XXX XXX
1185 static errr Term_pict_dos(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp)
1187 term_data *td = (term_data*)(Term->data);
1201 /* Location (window) */
1205 /* Dump the tiles */
1206 for (i = 0; i < n; i++)
1208 /* Location (bitmap) */
1209 x2 = (cp[i] & 0x7F) * w;
1210 y2 = (ap[i] & 0x7F) * h;
1212 x3 = (tcp[i] & 0x7F) * w;
1213 y3 = (tap[i] & 0x7F) * h;
1215 /* Blit the tile to the screen */
1216 blit(td->tiles, screen, x3, y3, x1, y1, w, h);
1218 /* Blit the tile to the screen */
1219 masked_blit(td->tiles, screen, x2, y2, x1, y1, w, h);
1221 /* Advance (window) */
1229 #endif /* USE_GRAPHICS */
1235 static void Term_init_dos(term *t)
1244 static void Term_nuke_dos(term *t)
1246 term_data *td = (term_data*)(t->data);
1248 /* Free the terminal font */
1249 if (td->font) destroy_font(td->font);
1253 /* Free the terminal bitmap */
1254 if (td->tiles) destroy_bitmap(td->tiles);
1256 #endif /* USE_GRAPHICS */
1262 * Instantiate a "term_data" structure
1264 static void term_data_link(term_data *td)
1268 /* Initialize the term */
1269 term_init(t, td->cols, td->rows, 255);
1271 /* Use a "software" cursor */
1272 t->soft_cursor = TRUE;
1274 /* Ignore the "TERM_XTRA_BORED" action */
1275 t->never_bored = FALSE;
1277 /* Ignore the "TERM_XTRA_FROSH" action */
1278 t->never_frosh = TRUE;
1280 /* Erase with "white space" */
1281 t->attr_blank = TERM_WHITE;
1282 t->char_blank = ' ';
1284 /* Prepare the init/nuke hooks */
1285 t->init_hook = Term_init_dos;
1286 t->nuke_hook = Term_nuke_dos;
1288 /* Prepare the template hooks */
1289 t->xtra_hook = Term_xtra_dos;
1290 t->curs_hook = Term_curs_dos;
1291 t->wipe_hook = Term_wipe_dos;
1292 t->user_hook = Term_user_dos;
1293 t->text_hook = Term_text_dos;
1297 /* Prepare the graphics hook */
1298 t->pict_hook = Term_pict_dos;
1300 /* Use "Term_pict" for "graphic" data */
1301 t->higher_pict = TRUE;
1303 #endif /* USE_GRAPHICS */
1305 /* Remember where we came from */
1306 t->data = (vptr)(td);
1311 * Shut down visual system, then fall back into standard "quit()"
1313 static void dos_quit_hook(cptr str)
1317 /* Destroy sub-windows */
1318 for (i = MAX_TERM_DATA - 1; i >= 1; i--)
1321 if (!angband_term[i]) continue;
1324 term_nuke(angband_term[i]);
1328 /* Free all resources */
1329 if (cursor) destroy_bitmap(cursor);
1331 #ifdef USE_BACKGROUND
1333 /* Free the background bitmaps */
1334 for (i = 0; i < 17; i++)
1336 if (background[i]) destroy_bitmap(background[i]);
1339 #endif /* USE_BACKGROUND */
1344 if (sound_initialized)
1346 /* Destroy samples */
1347 for (i = 1; i < SOUND_MAX; i++)
1351 for (j = 0; j < sample_count[i]; j++)
1353 if (samples[i][j]) destroy_sample(samples[i][j]);
1358 /* Clear the old song */
1359 if (midi_song) destroy_midi(midi_song);
1361 # ifdef USE_MOD_FILES
1362 if (mod_file_initialized)
1365 destroy_mod(mod_song);
1367 # endif /* USE_MOD_FILES */
1369 #endif /* USE_SOUND */
1371 /* Shut down Allegro */
1377 * Dump the screen to "lib/user/dump.bmp"
1379 static void dos_dump_screen(void)
1381 /* Bitmap and palette of the screen */
1386 char filename[1024];
1388 /* Get bitmap and palette of the screen */
1389 bmp = create_sub_bitmap(screen, 0, 0, SCREEN_W, SCREEN_H);
1392 /* Build the filename for the screen-dump */
1393 path_build(filename, sizeof(filename), ANGBAND_DIR_USER, "dump.bmp");
1396 save_bmp(filename, bmp, pal);
1398 /* Free up the memory */
1399 if (bmp) destroy_bitmap(bmp);
1401 /* Success message */
1402 msg_print("Screen dump saved.");
1407 /* GRX font file reader by Mark Wodrich.
1409 * GRX FNT files consist of the header data (see struct below). If the font
1410 * is proportional, followed by a table of widths per character (unsigned
1411 * shorts). Then, the data for each character follows. 1 bit/pixel is used,
1412 * with each line of the character stored in contiguous bytes. High bit of
1413 * first byte is leftmost pixel of line.
1415 * Note : FNT files can have a variable number of characters, so we must
1416 * check that the chars 32..127 exist.
1419 #define FONTMAGIC 0x19590214L
1422 /* .FNT file header */
1425 unsigned long magic;
1426 unsigned long bmpsize;
1427 unsigned short width;
1428 unsigned short height;
1429 unsigned short minchar;
1430 unsigned short maxchar;
1431 unsigned short isfixed;
1432 unsigned short reserved;
1433 unsigned short baseline;
1434 unsigned short undwidth;
1440 #define GRX_TMP_SIZE 4096
1444 /* converts images from bit to byte format */
1445 static void convert_grx_bitmap(int width, int height, unsigned char *src, unsigned char *dest)
1447 unsigned short x, y, bytes_per_line;
1448 unsigned char bitpos, bitset;
1450 bytes_per_line = (width + 7) >> 3;
1452 for (y = 0; y < height; y++)
1454 for (x = 0; x < width; x++)
1457 bitset = !!(src[(bytes_per_line * y) + (x >> 3)] & (1 << bitpos));
1458 dest[y * width + x] = bitset;
1465 /* reads GRX format images from disk */
1466 static unsigned char **load_grx_bmps(PACKFILE *f, FNTfile_header *hdr, int numchar, unsigned short *wtable)
1468 int t, width, bmp_size;
1469 unsigned char *temp;
1470 unsigned char **bmp;
1472 /* alloc array of bitmap pointers */
1473 bmp = malloc(sizeof(unsigned char *) * numchar);
1475 /* assume it's fixed width for now */
1478 /* temporary working area to store FNT bitmap */
1479 temp = malloc(GRX_TMP_SIZE);
1481 for (t = 0; t < numchar; t++)
1483 /* if prop. get character width */
1487 /* work out how many bytes to read */
1488 bmp_size = ((width + 7) >> 3) * hdr->height;
1490 /* oops, out of space! */
1491 if (bmp_size > GRX_TMP_SIZE)
1494 for (t--; t >= 0; t--)
1500 /* alloc space for converted bitmap */
1501 bmp[t] = malloc(width * hdr->height);
1504 pack_fread(temp, bmp_size, f);
1506 /* convert to 1 byte/pixel */
1507 convert_grx_bitmap(width, hdr->height, temp, bmp[t]);
1516 /* main import routine for the GRX font format */
1517 static FONT *import_grx_font(char *fname)
1520 FNTfile_header hdr; /* GRX font header */
1521 int numchar; /* number of characters in the font */
1522 unsigned short *wtable = NULL; /* table of widths for each character */
1523 unsigned char **bmp; /* array of font bitmaps */
1524 FONT *font = NULL; /* the Allegro font */
1525 FONT_PROP *font_prop;
1526 int c, c2, start, width;
1528 f = pack_fopen(fname, F_READ);
1532 pack_fread(&hdr, sizeof(hdr), f); /* read the header structure */
1534 if (hdr.magic != FONTMAGIC) /* check magic number */
1540 numchar = hdr.maxchar - hdr.minchar + 1;
1542 if (!hdr.isfixed) /* proportional font */
1544 wtable = malloc(sizeof(unsigned short) * numchar);
1545 pack_fread(wtable, sizeof(unsigned short) * numchar, f);
1548 bmp = load_grx_bmps(f, &hdr, numchar, wtable);
1555 font = malloc(sizeof(FONT));
1557 font->dat.dat_prop = font_prop = malloc(sizeof(FONT_PROP));
1558 font_prop->render = NULL;
1560 start = 32 - hdr.minchar;
1563 for (c = 0; c <FONT_SIZE; c++)
1567 if ((c2 >= 0) && (c2 < numchar))
1572 font_prop->dat[c] = create_bitmap_ex(8, width, hdr.height);
1573 memcpy(font_prop->dat[c]->dat, bmp[c2], width * hdr.height);
1577 font_prop->dat[c] = create_bitmap_ex(8, 8, hdr.height);
1578 clear(font_prop->dat[c]);
1591 for (c = 0; c < numchar; c++)
1602 * Initialize the terminal windows
1604 static bool init_windows(void)
1612 char filename[1024];
1617 sprintf(section, "Mode-%d", resolution);
1619 /* Get number of windows */
1620 num_windows = get_config_int(section, "num_windows", 1);
1623 if (num_windows > 8) num_windows = 8;
1625 /* Init the terms */
1626 for (i = 0; i < num_windows; i++)
1629 WIPE(td, term_data);
1632 sprintf(section, "Term-%d-%d", resolution, i);
1637 /* Coordinates of left top corner */
1638 td->x = get_config_int(section, "x", 0);
1639 td->y = get_config_int(section, "y", 0);
1641 /* Rows and cols of term */
1642 td->rows = get_config_int(section, "rows", 24);
1643 td->cols = get_config_int(section, "cols", 80);
1646 td->tile_wid = get_config_int(section, "tile_wid", 8);
1647 td->tile_hgt = get_config_int(section, "tile_hgt", 13);
1650 td->font_wid = get_config_int(section, "tile_wid", 8);
1651 td->font_hgt = get_config_int(section, "tile_hgt", 13);
1653 /* Get font filename */
1654 strcpy(buf, get_config_string(section, "font_file", "xm8x13.fnt"));
1656 /* Build the name of the font file */
1657 path_build(filename, sizeof(filename), xtra_font_dir, buf);
1659 /* Load a "*.fnt" file */
1660 if (suffix(filename, ".fnt"))
1662 /* Load the font file */
1663 if (!(td->font = import_grx_font(filename)))
1665 quit_fmt("Error reading font file '%s'", filename);
1669 /* Load a "*.dat" file */
1670 else if (suffix(filename, ".dat"))
1674 /* Load the font file */
1675 if (!(fontdata = load_datafile(filename)))
1677 quit_fmt("Error reading font file '%s'", filename);
1680 /* Save the font data */
1681 td->font = fontdata[1].dat;
1683 /* Unload the font file */
1684 unload_datafile_object(fontdata);
1690 quit_fmt("Unknown suffix in font file '%s'", filename);
1695 angband_term[i] = &td->t;
1703 #ifdef USE_BACKGROUND
1706 * Initialize the window backgrounds
1708 static void init_background(void)
1712 char filename[1024];
1716 PALLETE background_pallete;
1718 /* Get the backgrounds */
1719 for (i = 0; i < 16; i++)
1721 /* Get background filename */
1722 strcpy(buf, get_config_string("Background", format("Background-%d", i), ""));
1724 /* Build the filename for the background-bitmap */
1725 path_build(filename, sizeof(filename), xtra_graf_dir, buf);
1727 /* Try to open the bitmap file */
1728 background[i] = load_bitmap(filename, background_pallete);
1731 #ifndef USE_SPECIAL_BACKGROUND
1733 * Set the palette for the background
1737 set_palette_range(background_pallete, 0, COLOR_OFFSET - 1, 0);
1739 #endif /* USE_SPECIAL_BACKGROUND */
1742 #endif /* USE_BACKGROUND */
1748 * Initialize graphics
1750 static bool init_graphics(void)
1752 char filename[1024];
1754 char name_tiles[128];
1756 /* Large bitmap for the tiles */
1757 BITMAP *tiles = NULL;
1758 PALLETE tiles_pallete;
1760 /* Size of each bitmap tile */
1766 if (!graphics_initialized)
1769 sprintf(section, "Mode-%d", resolution);
1771 /* Get bitmap tile size */
1772 bitmap_wid = get_config_int(section, "bitmap_wid", 8);
1773 bitmap_hgt = get_config_int(section, "bitmap_hgt", 8);
1775 /* Get bitmap filename */
1776 strcpy(name_tiles, get_config_string(section, "bitmap_file", "8x8.bmp"));
1778 /* Get number of windows */
1779 num_windows = get_config_int(section, "num_windows", 1);
1781 /* Build the name of the bitmap file */
1782 path_build(filename, sizeof(filename), xtra_graf_dir, name_tiles);
1784 /* Open the bitmap file */
1785 if ((tiles = load_bitmap(filename, tiles_pallete)) != NULL)
1790 * Set the graphics mode to "new" if Adam Bolt's
1791 * new 16x16 tiles are used.
1793 ANGBAND_GRAF = get_config_string(section, "graf-mode", "old");
1795 /* Select the bitmap pallete */
1796 set_palette_range(tiles_pallete, 0, COLOR_OFFSET - 1, 0);
1798 /* Prepare the graphics */
1799 for (i = 0; i < num_windows; i++)
1811 cols = tiles->w / bitmap_wid;
1812 rows = tiles->h / bitmap_hgt;
1814 width = td->tile_wid * cols;
1815 height = td->tile_hgt * rows;
1817 /* Initialize the tile graphics */
1818 td->tiles = create_bitmap(width, height);
1820 for (row = 0; row < rows; ++row)
1822 src_y = row * bitmap_hgt;
1823 tgt_y = row * td->tile_hgt;
1825 for (col = 0; col < cols; ++col)
1827 src_x = col * bitmap_wid;
1828 tgt_x = col * td->tile_wid;
1830 stretch_blit(tiles, td->tiles,
1832 bitmap_wid, bitmap_hgt,
1834 td->tile_wid, td->tile_hgt);
1839 /* Free the old tiles bitmap */
1840 if (tiles) destroy_bitmap(tiles);
1842 graphics_initialized = TRUE;
1856 #endif /* USE_GRAPHICS */
1862 * We try to get a list of the available sound-files from "lib/xtra/sound/sound.cfg"
1863 * and then preload the samples. Every Angband-sound-event can have several samples
1864 * assigned. Angband will randomly select which is played. This makes it easy to
1865 * create "sound-packs", just copy wav-files into the "lib/xtra/sound/" folder and
1866 * add the filenames to "sound.cfg" in the same folder.
1868 static bool init_sound(void)
1873 char filename[1024];
1878 if (sound_initialized) return (TRUE);
1880 reserve_voices(16, -1);
1882 /* Initialize Allegro sound */
1883 if (!install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, NULL))
1885 #ifdef USE_MOD_FILES
1887 * Try to enable support for MOD-, and S3M-files
1888 * The parameter for install_mod() is the number
1889 * of channels reserved for the MOD/S3M-file.
1891 if (install_mod(8) > 0) mod_file_initialized = TRUE;
1892 #endif /* USE_MOD_FILES */
1894 /* Access the new sample */
1895 path_build(filename, sizeof(filename), xtra_sound_dir, "sound.cfg");
1897 /* Read config info from "lib/xtra/sound/sound.cfg" */
1898 override_config_file(filename);
1901 strcpy(section, "Sound");
1903 /* Prepare the sounds */
1904 for (i = 1; i < SOUND_MAX; i++)
1906 /* Get the sample names */
1907 argv = get_config_argv(section, angband_sound_name[i], &sample_count[i]);
1909 /* Limit the number of samples */
1910 if (sample_count[i] > SAMPLE_MAX) sample_count[i] = SAMPLE_MAX;
1912 for (j = 0; j < sample_count[i]; j++)
1914 /* Access the new sample */
1915 path_build(filename, sizeof(filename), xtra_sound_dir, argv[j]);
1917 /* Load the sample */
1918 samples[i][j] = load_sample(filename);
1923 * Get a list of music files
1925 #ifdef USE_MOD_FILES
1926 if (mod_file_initialized)
1928 done = findfirst(format("%s/*.*", xtra_music_dir), &f, FA_ARCH|FA_RDONLY);
1931 #endif /* USE_MOD_FILES */
1932 done = findfirst(format("%s/*.mid", xtra_music_dir), &f, FA_ARCH|FA_RDONLY);
1935 while (!done && (song_number <= MAX_SONGS))
1937 /* Add music files */
1939 strcpy(music_files[song_number], f.ff_name);
1943 done = findnext(&f);
1946 /* Use "angdos.cfg" */
1947 override_config_file("angdos.cfg");
1950 strcpy(section, "Sound");
1952 /* Get the volume setting */
1953 digi_volume = get_config_int(section, "digi_volume", 255);
1954 midi_volume = get_config_int(section, "midi_volume", 255);
1956 /* Set the volume */
1957 set_volume(digi_volume, midi_volume);
1971 static errr Term_xtra_dos_sound(int v)
1975 /* Sound disabled */
1976 if (!use_sound) return (1);
1979 if ((v < 0) || (v >= SOUND_MAX)) return (1);
1981 /* Get a random sample from the available ones */
1982 n = randint0(sample_count[v]);
1984 /* Play the sound, catch errors */
1987 return (play_sample(samples[v][n], 255, 128, 1000, 0) == 0);
1998 static void play_song(void)
2002 /* Clear the old song */
2003 if (midi_song) destroy_midi(midi_song);
2006 #ifdef USE_MOD_FILES
2007 if (mod_file_initialized)
2010 destroy_mod(mod_song);
2012 #endif /* USE_MOD_FILES */
2014 /* Access the new song */
2015 path_build(filename, sizeof(filename), xtra_music_dir, music_files[current_song - 1]);
2017 /* Load and play the new song */
2018 midi_song = load_midi(filename);
2022 play_midi(midi_song, 0);
2024 #ifdef USE_MOD_FILES
2025 else if (mod_file_initialized)
2027 mod_song = load_mod(filename);
2029 if (mod_song) play_mod(mod_song, FALSE);
2031 #endif /* USE_MOD_FILES */
2034 #endif /* USE_SOUND */
2038 * Attempt to initialize this file
2040 * Hack -- we assume that "blank space" should be "white space"
2041 * (and not "black space" which might make more sense).
2043 * Note the use of "((x << 2) | (x >> 4))" to "expand" a 6 bit value
2044 * into an 8 bit value, without losing much precision, by using the 2
2045 * most significant bits as the least significant bits in the new value.
2047 * We should attempt to "share" bitmaps (and fonts) between windows
2048 * with the same "tile" size. XXX XXX XXX
2059 /* Initialize the Allegro library (never fails) */
2060 (void)allegro_init();
2062 /* Install timer support for music and sound */
2065 /* Read config info from filename */
2066 set_config_file("angdos.cfg");
2069 strcpy(section, "Angband");
2071 /* Get screen size */
2072 resolution = get_config_int(section, "Resolution", 1);
2075 sprintf(section, "Mode-%d", resolution);
2077 /* Get the screen dimensions */
2078 screen_wid = get_config_int(section, "screen_wid", 640);
2079 screen_hgt = get_config_int(section, "screen_hgt", 480);
2081 /* Set the color depth */
2084 /* Auto-detect, and instantiate, the appropriate graphics mode */
2085 if ((set_gfx_mode(GFX_AUTODETECT, screen_wid, screen_hgt, 0, 0)) < 0)
2088 * Requested graphics mode is not available
2089 * We retry with the basic 640x480 mode
2093 if ((set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0)) < 0)
2095 char error_text[1024];
2097 /* Save the Allegro error description */
2098 strcpy(error_text, allegro_error);
2100 /* Shut down Allegro */
2103 /* Print the error description */
2104 plog_fmt("Error selecting screen mode: %s", error_text);
2111 /* Hook in "z-util.c" hook */
2112 quit_aux = dos_quit_hook;
2114 /* Build the "graf" path */
2115 path_build(xtra_graf_dir, sizeof(xtra_graf_dir), ANGBAND_DIR_XTRA, "graf");
2117 /* Build the "font" path */
2118 path_build(xtra_font_dir, sizeof(xtra_font_dir), ANGBAND_DIR_XTRA, "font");
2120 /* Build the "sound" path */
2121 path_build(xtra_sound_dir, sizeof(xtra_sound_dir), ANGBAND_DIR_XTRA, "sound");
2123 /* Build the "music" path */
2124 path_build(xtra_music_dir, sizeof(xtra_music_dir), ANGBAND_DIR_XTRA, "music");
2126 /* Initialize the windows */
2131 /* Look for the sound preferences in "angdos.cfg" */
2134 arg_sound = get_config_int("Angband", "Sound", TRUE);
2137 #endif /* USE_SOUND */
2141 /* Look for the graphic preferences in "angdos.cfg" */
2144 arg_graphics = get_config_int("Angband", "Graphics", GRAPHICS_ORIGINAL);
2147 #endif /* USE_GRAPHICS */
2149 /* Initialize the "complex" RNG for the midi-shuffle function */
2151 Rand_state_init(time(NULL));
2153 /* Set the Angband colors/graphics/sound mode */
2154 Term_xtra_dos_react();
2156 #ifdef USE_BACKGROUND
2158 /* Initialize the background graphics */
2161 #endif /* USE_BACKGROUND */
2163 /* Clear the screen */
2164 clear_to_color(screen, COLOR_OFFSET + TERM_DARK);
2169 /* Build a cursor bitmap */
2170 cursor = create_bitmap(td->tile_wid, td->tile_hgt);
2172 /* Erase the cursor sprite */
2175 /* Draw the cursor sprite (yellow rectangle) */
2176 rect(cursor, 0, 0, td->tile_wid - 1, td->tile_hgt - 1,
2177 COLOR_OFFSET + TERM_YELLOW);
2179 /* Activate the main term */
2180 Term_activate(angband_term[0]);
2182 /* Place the cursor */
2183 Term_curs_dos(0, 0);
2185 #ifdef USE_BACKGROUND
2187 /* Use transparent text */
2190 #endif /* USE_BACKGROUND */
2196 #endif /* USE_DOS */