OSDN Git Service

[Refactor] #37353 コメント整理 / Refactor comments.
[hengband/hengband.git] / src / main-dos.c
1 /* File: main-dos.c */
2
3 /*
4  * Copyright (c) 1997 Ben Harrison, Robert Ruehlmann, and others
5  *
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.
9  */
10
11
12 /*
13  * This file helps Angband work with DOS computers.
14  *
15  * Adapted from "main-ibm.c".
16  *
17  * Author: Robert Ruehlmann (rr9@angband.org).
18  * See "http://thangorodrim.angband.org/".
19  *
20  * Initial framework (and some code) by Ben Harrison (benh@phial.com).
21  *
22  * This file requires the (free) DJGPP compiler (based on "gcc").
23  * See "http://www.delorie.com/djgpp/".
24  *
25  * This file uses the (free) "Allegro" library (SVGA graphics library).
26  * See "http://www.talula.demon.co.uk/allegro/".
27  *
28  * To compile this file, use "Makefile.dos", which defines "USE_DOS".
29  *
30  * See also "main-ibm.c" and "main-win.c".
31  *
32  *
33  * The "lib/user/pref.prf" file contains macro definitions and possible
34  * alternative color set definitions.
35  *
36  * The "lib/user/font.prf" contains attr/char mappings for use with the
37  * special fonts in the "lib/xtra/font/" directory.
38  *
39  * The "lib/user/graf.prf" contains attr/char mappings for use with the
40  * special bitmaps in the "lib/xtra/graf/" directory.
41  *
42  *
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.
47  *
48  * Note that "Term_xtra_dos_react()" allows runtime color, graphics,
49  * screen resolution, and sound modification.
50  *
51  *
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.
56  *
57  * The background music uses midi-files (and mod files) from the
58  * "lib/xtra/music" folder.
59  *
60  *
61  * Comment by Ben Harrison (benh@phial.com):
62  *
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
68  */
69
70 #include "angband.h"
71
72
73 #ifdef USE_DOS
74
75 #include <allegro.h>
76
77 #ifdef USE_MOD_FILES
78 #include <jgmod.h>
79 #endif /* USE_MOD_FILES */
80
81 #include <bios.h>
82 #include <dos.h>
83 #include <keys.h>
84 #include <unistd.h>
85 #include <dir.h>
86
87 /*
88  * Index of the first standard Angband color.
89  *
90  * All colors below this index are defined by
91  * the palette of the tiles-bitmap.
92  */
93 #define COLOR_OFFSET 240
94
95
96 /*
97  * Maximum number of terminals
98  */
99 #define MAX_TERM_DATA 8
100
101
102 /*
103  * Forward declare
104  */
105 typedef struct term_data term_data;
106
107
108 /*
109  * Extra "term" data
110  */
111 struct term_data
112 {
113         term t;
114
115         int number;
116
117         int x;
118         int y;
119
120         int cols;
121         int rows;
122
123         int tile_wid;
124         int tile_hgt;
125
126         int font_wid;
127         int font_hgt;
128
129         FONT *font;
130
131 #ifdef USE_GRAPHICS
132
133         BITMAP *tiles;
134
135 #endif /* USE_GRAPHICS */
136
137 #ifdef USE_BACKGROUND
138
139         int window_type;
140
141 #endif /* USE_BACKGROUND */
142
143 };
144
145 /*
146  * The current screen resolution
147  */
148 static int resolution;
149
150 #ifdef USE_BACKGROUND
151
152 /*
153  * The background images
154  */
155 BITMAP *background[17];
156
157 #endif /* USE_BACKGROUND */
158
159
160 /*
161  * An array of term_data's
162  */
163 static term_data data[MAX_TERM_DATA];
164
165
166 #ifdef USE_GRAPHICS
167
168 /*
169  * Are graphics already initialized ?
170  */
171 static bool graphics_initialized = FALSE;
172
173 #endif /* USE_GRAPHICS */
174
175
176 /*
177  * Small bitmap for the cursor
178  */
179 static BITMAP *cursor;
180
181
182 #ifdef USE_SOUND
183
184 /*
185  * Is the sound already initialized ?
186  */
187 static bool sound_initialized = FALSE;
188
189 # ifdef USE_MOD_FILES
190 /*
191  * Is the mod-file support already initialized ?
192  */
193 static bool mod_file_initialized = FALSE;
194
195 # endif /* USE_MOD_FILES */
196
197 /*
198  * Volume settings
199  */
200 static int digi_volume;
201 static int midi_volume;
202
203 /*
204  * The currently playing song
205  */
206 static MIDI *midi_song = NULL;
207
208 # ifdef USE_MOD_FILES
209
210 static JGMOD *mod_song = NULL;
211
212 # endif /* USE_MOD_FILES */
213
214 static int current_song;
215
216 /*
217  * The number of available songs
218  */
219 static int song_number;
220
221 /*
222  * The maximum number of available songs
223  */
224 #define MAX_SONGS 255
225
226 static char music_files[MAX_SONGS][16];
227
228 /*
229  * The maximum number of samples per sound-event
230  */
231 #define SAMPLE_MAX 10
232
233 /*
234  * An array of sound files
235  */
236 static SAMPLE* samples[SOUND_MAX][SAMPLE_MAX];
237
238 /*
239  * The number of available samples for every event
240  */
241 static int sample_count[SOUND_MAX];
242
243 #endif /* USE_SOUND */
244
245
246 /*
247  *  Extra paths
248  */
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];
253
254 /*
255  * List of used videomodes to reduce executable size
256  */
257 BEGIN_GFX_DRIVER_LIST
258         GFX_DRIVER_VBEAF
259         GFX_DRIVER_VGA
260         GFX_DRIVER_VESA3
261         GFX_DRIVER_VESA2L
262         GFX_DRIVER_VESA2B
263         GFX_DRIVER_VESA1
264 END_GFX_DRIVER_LIST
265
266
267 /*
268  * List of used color depths to reduce executeable size
269  */
270 BEGIN_COLOR_DEPTH_LIST
271         COLOR_DEPTH_8
272 END_COLOR_DEPTH_LIST
273
274
275 /*
276  * Keypress input modifier flags (hard-coded by DOS)
277  */
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 */
286
287
288 /*
289  * Prototypes
290  */
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);
305 errr init_dos(void);
306 #ifdef USE_SOUND
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 */
311 #ifdef USE_GRAPHICS
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 */
315
316
317 /*
318  * Process an event (check for a keypress)
319  *
320  * The keypress processing code is often the most system dependant part
321  * of Angband, since sometimes even the choice of compiler is important.
322  *
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.
327  *
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.
336  *
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.
342  *
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.
350  *
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)".
358  *
359  *         /      *      -      +      1      2      3      4
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
363  *
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)
368  *
369  * See "lib/user/pref-win.prf" for the "standard" macros for various keys.
370  *
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.
376  *
377  * This function also appears in "main-ibm.c".  XXX XXX XXX
378  *
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".
382  */
383 static errr Term_xtra_dos_event(int v)
384 {
385         int i, k, s;
386
387         bool mc = FALSE;
388         bool ms = FALSE;
389         bool ma = FALSE;
390
391         /* Hack -- Check for a keypress */
392         if (!v && !bioskey(1)) return (1);
393
394         /* Wait for a keypress */
395         k = bioskey(0x10);
396
397         /* Access the "modifiers" */
398         i = bioskey(2);
399
400         /* Extract the "scan code" */
401         s = ((k >> 8) & 0xFF);
402
403         /* Extract the "ascii value" */
404         k = (k & 0xFF);
405
406         /* Process "normal" keys */
407         if ((s <= 58) || (s == 0xE0))
408         {
409                 /* Enqueue it */
410                 if (k) Term_keypress(k);
411
412                 /* Success */
413                 return (0);
414         }
415
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;
421
422         /* Dump the screen with "Ctrl-Print" */
423         if ((s == 0x72) && mc)
424         {
425                 /* Dump the screen */
426                 dos_dump_screen();
427
428                 /* Success */
429                 return (0);
430         }
431
432         /* Begin a "macro trigger" */
433         Term_keypress(31);
434
435         /* Hack -- Send the modifiers */
436         if (mc) Term_keypress('C');
437         if (ms) Term_keypress('S');
438         if (ma) Term_keypress('A');
439
440         /* Introduce the hexidecimal scan code */
441         Term_keypress('x');
442
443         /* Encode the hexidecimal scan code */
444         Term_keypress(hexsym[s/16]);
445         Term_keypress(hexsym[s%16]);
446
447         /* End the "macro trigger" */
448         Term_keypress(13);
449
450         /* Success */
451         return (0);
452 }
453
454
455 /*
456  * React to global changes in the colors, graphics, and sound settings.
457  */
458 static void Term_xtra_dos_react(void)
459 {
460         int i;
461
462 #ifdef USE_SPECIAL_BACKGROUND
463
464         int j;
465
466         term_data *td;
467
468 #endif /* USE_SPECIAL_BACKGROUND */
469
470         /*
471          * Set the Angband colors
472          */
473         for (i = 0; i < 16; i++)
474         {
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;
479
480                 RGB color = { rv,  gv,  bv  };
481
482                 set_color(COLOR_OFFSET + i, &color);
483         }
484
485 #ifdef USE_GRAPHICS
486
487         /*
488          * Handle "arg_graphics"
489          */
490         if (use_graphics != arg_graphics)
491         {
492                 /* Initialize (if needed) */
493                 if (arg_graphics && !init_graphics())
494                 {
495                         /* Warning */
496                         plog("Cannot initialize graphics!");
497
498                         /* Cannot enable */
499                         arg_graphics = GRAPHICS_NONE;
500                 }
501
502                 /* Change setting */
503                 use_graphics = arg_graphics;
504         }
505
506 #endif /* USE_GRAPHICS */
507
508 #ifdef USE_SOUND
509
510         /*
511          * Handle "arg_sound"
512          */
513         if (use_sound != arg_sound)
514         {
515                 /* Clear the old song */
516                 if (midi_song) destroy_midi(midi_song);
517                 midi_song = NULL;
518
519 #ifdef USE_MOD_FILES
520                 if (mod_file_initialized)
521                 {
522                         stop_mod();
523                         destroy_mod(mod_song);
524                 }
525 #endif /* USE_MOD_FILES */
526
527                 /* Initialize (if needed) */
528                 if (arg_sound && !init_sound())
529                 {
530                         /* Warning */
531                         plog("Cannot initialize sound!");
532
533                         /* Cannot enable */
534                         arg_sound = FALSE;
535                 }
536
537                 /* Change setting */
538                 use_sound = arg_sound;
539         }
540
541 #endif /* USE_SOUND */
542
543 #ifdef USE_SPECIAL_BACKGROUND
544
545         /*
546          * Initialize the window backgrounds
547          */
548         for (i = 0; i < 8; i++)
549         {
550                 td = &data[i];
551
552                 /* Window flags */
553                 for (j = 0; j < 16; j++)
554                 {
555                         if (op_ptr->window_flag[i] & (1L << j))
556                         {
557                                 if (background[j + 1])
558                                 {
559                                         td->window_type = j + 1;
560                                 }
561                                 else
562                                 {
563                                         td->window_type = 0;
564                                 }
565                         }
566                 }
567         }
568 #endif /* USE_SPECIAL_BACKGROUND */
569 }
570
571
572 /*
573  * Clear a terminal
574  *
575  * Fills the terminal area with black color or with
576  * the background image
577  */
578 static void Term_xtra_dos_clear(void)
579 {
580         term_data *td = (term_data*)(Term->data);
581
582 #ifdef USE_BACKGROUND
583         int bgrnd;
584 #endif /* USE_BACKGROUND */
585
586         int x1, y1;
587         int w1, h1;
588
589         /* Location */
590         x1 = td->x;
591         y1 = td->y;
592
593         /* Size */
594         w1 = td->tile_wid * td->cols;
595         h1 = td->tile_hgt * td->rows;
596
597 #ifdef USE_BACKGROUND
598
599         bgrnd = td->window_type;
600
601         if (background[bgrnd])
602         {
603                 /* Draw the background */
604                 stretch_blit(background[bgrnd], screen,
605                         0, 0, background[bgrnd]->w, background[bgrnd]->h,
606                         x1, y1, w1, h1);
607         }
608         else
609
610 #endif /* USE_BACKGROUND */
611
612         {
613                 /* Draw the Term black */
614                 rectfill(screen,
615                         x1, y1, x1 + w1 - 1, y1 + h1 - 1,
616                         COLOR_OFFSET + TERM_DARK);
617         }
618 }
619
620
621 /*
622  * Handle a "special request"
623  *
624  * The given parameters are "valid".
625  */
626 static errr Term_xtra_dos(int n, int v)
627 {
628         /* Analyze the request */
629         switch (n)
630         {
631                 /* Make a "bell" noise */
632                 case TERM_XTRA_NOISE:
633                 {
634                         /* Make a bell noise */
635                         (void)write(1, "\007", 1);
636
637                         /* Success */
638                         return (0);
639                 }
640
641                 /* Clear the screen */
642                 case TERM_XTRA_CLEAR:
643                 {
644                         /* Clear the screen */
645                         Term_xtra_dos_clear();
646
647                         /* Success */
648                         return (0);
649                 }
650
651                 /* Process events */
652                 case TERM_XTRA_EVENT:
653                 {
654                         /* Process one event */
655                         return (Term_xtra_dos_event(v));
656                 }
657
658                 /* Flush events */
659                 case TERM_XTRA_FLUSH:
660                 {
661                         /* Strip events */
662                         while (!Term_xtra_dos_event(FALSE));
663
664                         /* Success */
665                         return (0);
666                 }
667
668                 /* Do something useful if bored */
669                 case TERM_XTRA_BORED:
670                 {
671 #ifdef USE_SOUND
672                         /*
673                          * Check for end of song and start a new one
674                          */
675                         if (!use_sound) return (0);
676
677 #ifdef USE_MOD_FILES
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 */
682                         {
683                                 if (song_number > 1)
684                                 {
685                                         /* Get a *new* song at random */
686                                         while (1)
687                                         {
688                                                 n = randint1(song_number);
689                                                 if (n != current_song) break;
690                                         }
691                                         current_song = n;
692                                 }
693                                 else
694                                 {
695                                         /* We only have one song, so loop it */
696                                         current_song = 1;
697                                 }
698
699                                 /* Play the song */
700                                 play_song();
701                         }
702
703 #endif /* USE_SOUND */
704
705                         /* Success */
706                         return (0);
707                 }
708
709                 /* React to global changes */
710                 case TERM_XTRA_REACT:
711                 {
712                         /* Change the colors */
713                         Term_xtra_dos_react();
714
715                         /* Success */
716                         return (0);
717                 }
718
719                 /* Delay for some milliseconds */
720                 case TERM_XTRA_DELAY:
721                 {
722                         /* Delay if needed */
723                         if (v > 0) delay(v);
724
725                         /* Success */
726                         return (0);
727                 }
728
729 #ifdef USE_SOUND
730
731                 /* Make a sound */
732                 case TERM_XTRA_SOUND:
733                 {
734                         return (Term_xtra_dos_sound(v));
735                 }
736
737 #endif /* USE_SOUND */
738
739         }
740
741         /* Unknown request */
742         return (1);
743 }
744
745
746 /*
747  * Do a "user action" on the current "term"
748  */
749 static errr Term_user_dos(int n)
750 {
751         int k;
752
753         char status[4];
754
755         char section[80];
756
757         /* Interact */
758         while (1)
759         {
760                 /* Clear screen */
761                 Term_clear();
762
763                 /* Print date and time of compilation */
764                 prt(format("Compiled: %s %s\n", __TIME__, __DATE__), 1, 45);
765
766                 /* Why are we here */
767                 prt("DOS options", 2, 0);
768
769                 /* Give some choices */
770 #ifdef USE_SOUND
771                 prt("(V) Sound Volume", 4, 5);
772                 prt("(M) Music Volume", 5, 5);
773 #endif /* USE_SOUND */
774
775 #ifdef USE_GRAPHICS
776
777                 if (arg_graphics)
778                 {
779                         strcpy(status, "On");
780                 }
781                 else
782                 {
783                         strcpy(status, "Off");
784                 }
785                 prt(format("(G) Graphics : %s", status), 7, 5);
786
787 #endif /* USE_GRAPHICS */
788
789 #ifdef USE_SOUND
790
791                 if (arg_sound)
792                 {
793                         strcpy(status, "On");
794                 }
795                 else
796                 {
797                         strcpy(status, "Off");
798                 }
799                 prt(format("(S) Sound/Music : %s", status), 8, 5);
800
801 #endif /* USE_SOUND */
802
803                 prt("(R) Screen resolution", 12, 5);
804
805                 prt("(W) Save current options", 14, 5);
806
807                 /* Prompt */
808                 prt("Command: ", 18, 0);
809
810                 /* Get command */
811                 k = inkey();
812
813                 /* Exit */
814                 if (k == ESCAPE) break;
815
816                 /* Analyze */
817                 switch (k)
818                 {
819 #ifdef USE_SOUND
820                         /* Sound Volume */
821                         case 'V':
822                         case 'v':
823                         {
824                                 /* Prompt */
825                                 prt("Command: Sound Volume", 18, 0);
826
827                                 /* Get a new value */
828                                 while (1)
829                                 {
830                                         prt(format("Current Volume: %d", digi_volume), 22, 0);
831                                         prt("Change Volume (+, - or ESC to accept): ", 20, 0);
832                                         k = inkey();
833                                         if (k == ESCAPE) break;
834                                         switch (k)
835                                         {
836                                                 case '+':
837                                                 {
838                                                         digi_volume++;
839                                                         if (digi_volume > 255) digi_volume = 255;
840                                                         break;
841                                                 }
842                                                 case '-':
843                                                 {
844                                                         digi_volume--;
845                                                         if (digi_volume < 0) digi_volume = 0;
846                                                         break;
847                                                 }
848                                                 /* Unknown option */
849                                                 default:
850                                                 {
851                                                         break;
852                                                 }
853                                         }
854                                         set_volume(digi_volume, -1);
855                                 }
856                                 break;
857                         }
858
859                         /* Music Volume */
860                         case 'M':
861                         case 'm':
862                         {
863                                 /* Prompt */
864                                 prt("Command: Music Volume", 18, 0);
865
866                                 /* Get a new value */
867                                 while (1)
868                                 {
869                                         prt(format("Current Volume: %d", midi_volume), 22, 0);
870                                         prt("Change Volume (+, - or ESC to accept): ", 20, 0);
871                                         k = inkey();
872                                         if (k == ESCAPE) break;
873                                         switch (k)
874                                         {
875                                                 case '+':
876                                                 {
877                                                         midi_volume++;
878                                                         if (midi_volume > 255) midi_volume = 255;
879                                                         break;
880                                                 }
881                                                 case '-':
882                                                 {
883                                                         midi_volume--;
884                                                         if (midi_volume < 0) midi_volume = 0;
885                                                         break;
886                                                 }
887                                                 /* Unknown option */
888                                                 default:
889                                                 {
890                                                         break;
891                                                 }
892                                         }
893                                         set_volume(-1, midi_volume);
894                                 }
895                                 break;
896                         }
897
898 #endif /* USE_SOUND */
899
900 #ifdef USE_GRAPHICS
901
902                         /* Switch graphics on/off */
903                         case 'G':
904                         case 'g':
905                         {
906                                 /* Toggle "arg_graphics" */
907                                 arg_graphics = !arg_graphics;
908
909                                 /* React to changes */
910                                 Term_xtra_dos_react();
911
912                                 /* Reset visuals */
913 #ifdef ANGBAND_2_8_1
914                                 reset_visuals();
915 #else /* ANGBAND_2_8_1 */
916                                 reset_visuals(TRUE);
917 #endif /* ANGBAND_2_8_1 */
918                                 break;
919                         }
920
921 #endif /* USE_GRAPHICS */
922
923 #ifdef USE_SOUND
924
925                         /* Sound/Music On/Off */
926                         case 'S':
927                         case 's':
928                         {
929                                 /* Toggle "arg_sound" */
930                                 arg_sound = !arg_sound;
931
932                                 /* React to changes */
933                                 Term_xtra_dos_react();
934
935                                 break;
936                         }
937
938 #endif /* USE_SOUND */
939
940                         /* Screen Resolution */
941                         case 'R':
942                         case 'r':
943                         {
944                                 int h, w, i = 1;
945                                 char *descr;
946
947                                 /* Clear screen */
948                                 Term_clear();
949
950                                 /* Prompt */
951                                 prt("Command: Screen Resolution", 1, 0);
952                                 prt("Restart Angband to get the new screenmode.", 3, 0);
953
954                                 /* Get a list of the available presets */
955                                 while (1)
956                                 {
957                                         /* Section name */
958                                         sprintf(section, "Mode-%d", i);
959
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);
963
964                                         /* Get a extra description of the resolution */
965                                         descr = get_config_string(section, "Description", "");
966
967                                         /* Print it */
968                                         prt(format("(%d) %d x %d   %s", i, w, h, descr), 4 + i, 0);
969
970                                         /* Next */
971                                         i++;
972                                 }
973
974                                 /* Get a new resolution */
975                                 prt(format("Screen Resolution : %d", resolution), 20, 0);
976                                 k = inkey();
977                                 if (k == ESCAPE) break;
978                                 if (isdigit(k)) resolution = D2I(k);
979
980                                 /* Check for min, max value */
981                                 if ((resolution < 1) || (resolution >= i)) resolution = 1;
982
983                                 /* Save the resolution */
984                                 set_config_int("Angband", "Resolution", resolution);
985
986                                 /* Return */
987                                 break;
988                         }
989
990
991                         /* Save current option */
992                         case 'W':
993                         case 'w':
994                         {
995                                 prt("Saving current options", 18, 0);
996
997 #ifdef USE_SOUND
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);
1003
1004                                 break;
1005                         }
1006
1007                         /* Unknown option */
1008                         default:
1009                         {
1010                                 break;
1011                         }
1012                 }
1013
1014                 /* Flush messages */
1015                 msg_print(NULL);
1016         }
1017
1018         /* Redraw it */
1019         Term_key_push(KTRL('R'));
1020
1021         /* Unknown */
1022         return (0);
1023 }
1024
1025
1026 /*
1027  * Move the cursor
1028  *
1029  * The given parameters are "valid".
1030  */
1031 static errr Term_curs_dos(int x, int y)
1032 {
1033         term_data *td = (term_data*)(Term->data);
1034
1035         int x1, y1;
1036
1037         /* Location */
1038         x1 = x * td->tile_wid + td->x;
1039         y1 = y * td->tile_hgt + td->y;
1040
1041         /* Draw the cursor */
1042         draw_sprite(screen, cursor, x1, y1);
1043
1044         /* Success */
1045         return (0);
1046 }
1047
1048
1049 /*
1050  * Erase a block of the screen
1051  *
1052  * The given parameters are "valid".
1053  */
1054 static errr Term_wipe_dos(int x, int y, int n)
1055 {
1056         term_data *td = (term_data*)(Term->data);
1057
1058 #ifdef USE_BACKGROUND
1059         int bgrnd;
1060 #endif /* USE_BACKGROUND */
1061
1062         int x1, y1;
1063         int w1, h1;
1064
1065         /* Location */
1066         x1 = x * td->tile_wid + td->x;
1067         y1 = y * td->tile_hgt + td->y;
1068
1069         /* Size */
1070         w1 = n * td->tile_wid;
1071         h1 = td->tile_hgt;
1072
1073 #ifdef USE_BACKGROUND
1074
1075         bgrnd = td->window_type;
1076
1077         if (background[bgrnd])
1078         {
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;
1083
1084                 /* Draw the background */
1085                 stretch_blit(background[bgrnd], screen,
1086                         source_x, source_y, source_w, source_h,
1087                         x1, y1, w1, h1);
1088         }
1089         else
1090
1091 #endif /* USE_BACKGROUND */
1092
1093         {
1094                 /* Draw a black block */
1095                 rectfill(screen, x1, y1, x1 + w1 - 1, y1 + h1 - 1,
1096                         COLOR_OFFSET + TERM_DARK);
1097         }
1098
1099         /* Success */
1100         return (0);
1101 }
1102
1103
1104 /*
1105  * Place some text on the screen using an attribute
1106  *
1107  * The given parameters are "valid".  Be careful with "a".
1108  *
1109  * The string "cp" has length "n" and is NOT null-terminated.
1110  */
1111 static errr Term_text_dos(int x, int y, int n, byte a, const char *cp)
1112 {
1113         term_data *td = (term_data*)(Term->data);
1114
1115         int i;
1116
1117         int x1, y1;
1118
1119         char text[257];
1120
1121         /* Location */
1122         x1 = x * td->tile_wid + td->x;
1123         y1 = y * td->tile_hgt + td->y;
1124
1125         /* Erase old contents */
1126         Term_wipe_dos(x, y, n);
1127
1128 #ifdef USE_SPECIAL_BACKGROUND
1129
1130         /* Show text in black in the message window */
1131         if (op_ptr->window_flag[td->number] & (PW_MESSAGE)) a = 0;
1132
1133 #endif /* USE_SPECIAL_BACKGROUND */
1134
1135         /* No stretch needed */
1136         if (td->font_wid == td->tile_wid)
1137         {
1138                 /* Copy the string */
1139                 for (i = 0; i < n; ++i) text[i] = cp[i];
1140
1141                 /* Terminate */
1142                 text[i] = '\0';
1143
1144                 /* Dump the text */
1145                 textout(screen, td->font, text, x1, y1,
1146                         COLOR_OFFSET + (a & 0x0F));
1147         }
1148         /* Stretch needed */
1149         else
1150         {
1151                 /* Pre-Terminate */
1152                 text[1] = '\0';
1153
1154                 /* Write the chars to the screen */
1155                 for (i = 0; i < n; ++i)
1156                 {
1157                         /* Build a one character string */
1158                         text[0] = cp[i];
1159
1160                         /* Dump some text */
1161                         textout(screen, td->font, text, x1, y1,
1162                                 COLOR_OFFSET + (a & 0x0F));
1163
1164                         /* Advance */
1165                         x1 += td->tile_wid;
1166                 }
1167         }
1168
1169         /* Success */
1170         return (0);
1171 }
1172
1173
1174 #ifdef USE_GRAPHICS
1175
1176 /*
1177  * Place some attr/char pairs on the screen
1178  *
1179  * The given parameters are "valid".
1180  *
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
1184  */
1185 static errr Term_pict_dos(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp)
1186 {
1187         term_data *td = (term_data*)(Term->data);
1188
1189         int i;
1190
1191         int w, h;
1192
1193         int x1, y1;
1194         int x2, y2;
1195         int x3, y3;
1196
1197         /* Size */
1198         w = td->tile_wid;
1199         h = td->tile_hgt;
1200
1201         /* Location (window) */
1202         x1 = x * w + td->x;
1203         y1 = y * h + td->y;
1204
1205         /* Dump the tiles */
1206         for (i = 0; i < n; i++)
1207         {
1208                 /* Location (bitmap) */
1209                 x2 = (cp[i] & 0x7F) * w;
1210                 y2 = (ap[i] & 0x7F) * h;
1211
1212                 x3 = (tcp[i] & 0x7F) * w;
1213                 y3 = (tap[i] & 0x7F) * h;
1214
1215                 /* Blit the tile to the screen */
1216                 blit(td->tiles, screen, x3, y3, x1, y1, w, h);
1217
1218                 /* Blit the tile to the screen */
1219                 masked_blit(td->tiles, screen, x2, y2, x1, y1, w, h);
1220
1221                 /* Advance (window) */
1222                 x1 += w;
1223         }
1224
1225         /* Success */
1226         return (0);
1227 }
1228
1229 #endif /* USE_GRAPHICS */
1230
1231
1232 /*
1233  * Init a Term
1234  */
1235 static void Term_init_dos(term *t)
1236 {
1237         /* XXX Nothing */
1238 }
1239
1240
1241 /*
1242  * Nuke a Term
1243  */
1244 static void Term_nuke_dos(term *t)
1245 {
1246         term_data *td = (term_data*)(t->data);
1247
1248         /* Free the terminal font */
1249         if (td->font) destroy_font(td->font);
1250
1251 #ifdef USE_GRAPHICS
1252
1253         /* Free the terminal bitmap */
1254         if (td->tiles) destroy_bitmap(td->tiles);
1255
1256 #endif /* USE_GRAPHICS */
1257 }
1258
1259
1260
1261 /*
1262  * Instantiate a "term_data" structure
1263  */
1264 static void term_data_link(term_data *td)
1265 {
1266         term *t = &td->t;
1267
1268         /* Initialize the term */
1269         term_init(t, td->cols, td->rows, 255);
1270
1271         /* Use a "software" cursor */
1272         t->soft_cursor = TRUE;
1273
1274         /* Ignore the "TERM_XTRA_BORED" action */
1275         t->never_bored = FALSE;
1276
1277         /* Ignore the "TERM_XTRA_FROSH" action */
1278         t->never_frosh = TRUE;
1279
1280         /* Erase with "white space" */
1281         t->attr_blank = TERM_WHITE;
1282         t->char_blank = ' ';
1283
1284         /* Prepare the init/nuke hooks */
1285         t->init_hook = Term_init_dos;
1286         t->nuke_hook = Term_nuke_dos;
1287
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;
1294
1295 #ifdef USE_GRAPHICS
1296
1297         /* Prepare the graphics hook */
1298         t->pict_hook = Term_pict_dos;
1299
1300         /* Use "Term_pict" for "graphic" data */
1301         t->higher_pict = TRUE;
1302
1303 #endif /* USE_GRAPHICS */
1304
1305         /* Remember where we came from */
1306         t->data = (vptr)(td);
1307 }
1308
1309
1310 /*
1311  * Shut down visual system, then fall back into standard "quit()"
1312  */
1313 static void dos_quit_hook(cptr str)
1314 {
1315         int i;
1316
1317         /* Destroy sub-windows */
1318         for (i = MAX_TERM_DATA - 1; i >= 1; i--)
1319         {
1320                 /* Unused */
1321                 if (!angband_term[i]) continue;
1322
1323                 /* Nuke it */
1324                 term_nuke(angband_term[i]);
1325         }
1326
1327
1328         /* Free all resources */
1329         if (cursor) destroy_bitmap(cursor);
1330
1331 #ifdef USE_BACKGROUND
1332
1333         /* Free the background bitmaps */
1334         for (i = 0; i < 17; i++)
1335         {
1336                 if (background[i]) destroy_bitmap(background[i]);
1337         }
1338
1339 #endif /* USE_BACKGROUND */
1340
1341
1342 #ifdef USE_SOUND
1343
1344         if (sound_initialized)
1345         {
1346                 /* Destroy samples */
1347                 for (i = 1; i < SOUND_MAX; i++)
1348                 {
1349                         int j;
1350
1351                         for (j = 0; j < sample_count[i]; j++)
1352                         {
1353                                 if (samples[i][j]) destroy_sample(samples[i][j]);
1354                         }
1355                 }
1356         }
1357
1358         /* Clear the old song */
1359         if (midi_song) destroy_midi(midi_song);
1360         midi_song =NULL;
1361 # ifdef USE_MOD_FILES
1362         if (mod_file_initialized)
1363         {
1364                 stop_mod();
1365                 destroy_mod(mod_song);
1366         }
1367 # endif /* USE_MOD_FILES */
1368
1369 #endif /* USE_SOUND */
1370
1371         /* Shut down Allegro */
1372         allegro_exit();
1373 }
1374
1375
1376 /*
1377  * Dump the screen to "lib/user/dump.bmp"
1378  */
1379 static void dos_dump_screen(void)
1380 {
1381         /* Bitmap and palette of the screen */
1382         BITMAP *bmp;
1383         PALETTE pal;
1384
1385         /* Filename */
1386         char filename[1024];
1387
1388         /* Get bitmap and palette of the screen */
1389         bmp = create_sub_bitmap(screen, 0, 0, SCREEN_W, SCREEN_H);
1390         get_palette(pal);
1391
1392         /* Build the filename for the screen-dump */
1393         path_build(filename, sizeof(filename), ANGBAND_DIR_USER, "dump.bmp");
1394
1395         /* Save it */
1396         save_bmp(filename, bmp, pal);
1397
1398         /* Free up the memory */
1399         if (bmp) destroy_bitmap(bmp);
1400
1401         /* Success message */
1402         msg_print("Screen dump saved.");
1403         msg_print(NULL);
1404 }
1405
1406
1407 /* GRX font file reader by Mark Wodrich.
1408  *
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.
1414  *
1415  * Note : FNT files can have a variable number of characters, so we must
1416  *        check that the chars 32..127 exist.
1417  */
1418
1419 #define FONTMAGIC       0x19590214L
1420
1421
1422 /* .FNT file header */
1423 typedef struct
1424 {
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;
1435         char           fname[16];
1436         char           family[16];
1437 } FNTfile_header;
1438
1439
1440 #define GRX_TMP_SIZE    4096
1441
1442
1443
1444 /* converts images from bit to byte format */
1445 static void convert_grx_bitmap(int width, int height, unsigned char *src, unsigned char *dest)
1446 {
1447         unsigned short x, y, bytes_per_line;
1448         unsigned char bitpos, bitset;
1449
1450         bytes_per_line = (width + 7) >> 3;
1451
1452         for (y = 0; y < height; y++)
1453         {
1454                 for (x = 0; x < width; x++)
1455                 {
1456                         bitpos = 7-(x&7);
1457                         bitset = !!(src[(bytes_per_line * y) + (x >> 3)] & (1 << bitpos));
1458                         dest[y * width + x] = bitset;
1459                 }
1460         }
1461 }
1462
1463
1464
1465 /* reads GRX format images from disk */
1466 static unsigned char **load_grx_bmps(PACKFILE *f, FNTfile_header *hdr, int numchar, unsigned short *wtable)
1467 {
1468         int t, width, bmp_size;
1469         unsigned char *temp;
1470         unsigned char **bmp;
1471
1472         /* alloc array of bitmap pointers */
1473         bmp = malloc(sizeof(unsigned char *) * numchar);
1474
1475         /* assume it's fixed width for now */
1476         width = hdr->width;
1477
1478         /* temporary working area to store FNT bitmap */
1479         temp = malloc(GRX_TMP_SIZE);
1480
1481         for (t = 0; t < numchar; t++)
1482         {
1483                 /* if prop. get character width */
1484                 if (!hdr->isfixed)
1485                         width = wtable[t];
1486
1487                 /* work out how many bytes to read */
1488                 bmp_size = ((width + 7) >> 3) * hdr->height;
1489
1490                 /* oops, out of space! */
1491                 if (bmp_size > GRX_TMP_SIZE)
1492                 {
1493                         free(temp);
1494                         for (t--; t >= 0; t--)
1495                         free(bmp[t]);
1496                         free(bmp);
1497                         return NULL;
1498                 }
1499
1500                 /* alloc space for converted bitmap */
1501                 bmp[t] = malloc(width * hdr->height);
1502
1503                 /* read data */
1504                 pack_fread(temp, bmp_size, f);
1505
1506                 /* convert to 1 byte/pixel */
1507                 convert_grx_bitmap(width, hdr->height, temp, bmp[t]);
1508         }
1509
1510         free(temp);
1511         return bmp;
1512 }
1513
1514
1515
1516 /* main import routine for the GRX font format */
1517 static FONT *import_grx_font(char *fname)
1518 {
1519         PACKFILE *f;
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;
1527
1528         f = pack_fopen(fname, F_READ);
1529         if (!f)
1530                 return NULL;
1531
1532         pack_fread(&hdr, sizeof(hdr), f);      /* read the header structure */
1533
1534         if (hdr.magic != FONTMAGIC)             /* check magic number */
1535         {
1536                 pack_fclose(f);
1537                 return NULL;
1538         }
1539
1540         numchar = hdr.maxchar - hdr.minchar + 1;
1541
1542         if (!hdr.isfixed)                    /* proportional font */
1543         {
1544                 wtable = malloc(sizeof(unsigned short) * numchar);
1545                 pack_fread(wtable, sizeof(unsigned short) * numchar, f);
1546         }
1547
1548         bmp = load_grx_bmps(f, &hdr, numchar, wtable);
1549         if (!bmp)
1550                 goto get_out;
1551
1552         if (pack_ferror(f))
1553                 goto get_out;
1554
1555         font = malloc(sizeof(FONT));
1556         font->height = -1;
1557         font->dat.dat_prop = font_prop = malloc(sizeof(FONT_PROP));
1558         font_prop->render = NULL;
1559
1560         start = 32 - hdr.minchar;
1561         width = hdr.width;
1562
1563         for (c = 0; c  <FONT_SIZE; c++)
1564         {
1565                 c2 = c+start;
1566
1567                 if ((c2 >= 0) && (c2 < numchar))
1568                 {
1569                         if (!hdr.isfixed)
1570                                 width = wtable[c2];
1571
1572                         font_prop->dat[c] = create_bitmap_ex(8, width, hdr.height);
1573                         memcpy(font_prop->dat[c]->dat, bmp[c2], width * hdr.height);
1574                 }
1575                 else
1576                 {
1577                         font_prop->dat[c] = create_bitmap_ex(8, 8, hdr.height);
1578                         clear(font_prop->dat[c]);
1579                 }
1580         }
1581
1582         get_out:
1583
1584         pack_fclose(f);
1585
1586         if (wtable)
1587         free(wtable);
1588
1589         if (bmp)
1590         {
1591                 for (c = 0; c < numchar; c++)
1592                         free(bmp[c]);
1593
1594                 free(bmp);
1595         }
1596
1597         return font;
1598 }
1599
1600
1601 /*
1602  * Initialize the terminal windows
1603  */
1604 static bool init_windows(void)
1605 {
1606         int i, num_windows;
1607
1608         term_data *td;
1609
1610         char section[80];
1611
1612         char filename[1024];
1613
1614         char buf[128];
1615
1616         /* Section name */
1617         sprintf(section, "Mode-%d", resolution);
1618
1619         /* Get number of windows */
1620         num_windows = get_config_int(section, "num_windows", 1);
1621
1622         /* Paranoia */
1623         if (num_windows > 8) num_windows = 8;
1624
1625         /* Init the terms */
1626         for (i = 0; i < num_windows; i++)
1627         {
1628                 td = &data[i];
1629                 WIPE(td, term_data);
1630
1631                 /* Section name */
1632                 sprintf(section, "Term-%d-%d", resolution, i);
1633
1634                 /* Term number */
1635                 td->number = i;
1636
1637                 /* Coordinates of left top corner */
1638                 td->x = get_config_int(section, "x", 0);
1639                 td->y = get_config_int(section, "y", 0);
1640
1641                 /* Rows and cols of term */
1642                 td->rows = get_config_int(section, "rows", 24);
1643                 td->cols = get_config_int(section, "cols", 80);
1644
1645                 /* Tile size */
1646                 td->tile_wid = get_config_int(section, "tile_wid", 8);
1647                 td->tile_hgt = get_config_int(section, "tile_hgt", 13);
1648
1649                 /* Font size */
1650                 td->font_wid = get_config_int(section, "tile_wid", 8);
1651                 td->font_hgt = get_config_int(section, "tile_hgt", 13);
1652
1653                 /* Get font filename */
1654                 strcpy(buf, get_config_string(section, "font_file", "xm8x13.fnt"));
1655
1656                 /* Build the name of the font file */
1657                 path_build(filename, sizeof(filename), xtra_font_dir, buf);
1658
1659                 /* Load a "*.fnt" file */
1660                 if (suffix(filename, ".fnt"))
1661                 {
1662                         /* Load the font file */
1663                         if (!(td->font = import_grx_font(filename)))
1664                         {
1665                                 quit_fmt("Error reading font file '%s'", filename);
1666                         }
1667                 }
1668
1669                 /* Load a "*.dat" file */
1670                 else if (suffix(filename, ".dat"))
1671                 {
1672                         DATAFILE *fontdata;
1673
1674                         /* Load the font file */
1675                         if (!(fontdata = load_datafile(filename)))
1676                         {
1677                                 quit_fmt("Error reading font file '%s'", filename);
1678                         }
1679
1680                         /* Save the font data */
1681                         td->font = fontdata[1].dat;
1682
1683                         /* Unload the font file */
1684                         unload_datafile_object(fontdata);
1685                 }
1686
1687                 /* Oops */
1688                 else
1689                 {
1690                         quit_fmt("Unknown suffix in font file '%s'", filename);
1691                 }
1692
1693                 /* Link the term */
1694                 term_data_link(td);
1695                 angband_term[i] = &td->t;
1696         }
1697
1698         /* Success */
1699         return 0;
1700 }
1701
1702
1703 #ifdef USE_BACKGROUND
1704
1705 /*
1706  * Initialize the window backgrounds
1707  */
1708 static void init_background(void)
1709 {
1710         int i;
1711
1712         char filename[1024];
1713
1714         char buf[128];
1715
1716         PALLETE background_pallete;
1717
1718         /* Get the backgrounds */
1719         for (i = 0; i < 16; i++)
1720         {
1721                 /* Get background filename */
1722                 strcpy(buf, get_config_string("Background", format("Background-%d", i), ""));
1723
1724                 /* Build the filename for the background-bitmap */
1725                 path_build(filename, sizeof(filename), xtra_graf_dir, buf);
1726
1727                 /* Try to open the bitmap file */
1728                 background[i] = load_bitmap(filename, background_pallete);
1729         }
1730
1731 #ifndef USE_SPECIAL_BACKGROUND
1732         /*
1733          * Set the palette for the background
1734          */
1735         if (background[0])
1736         {
1737                 set_palette_range(background_pallete, 0, COLOR_OFFSET - 1, 0);
1738         }
1739 #endif /* USE_SPECIAL_BACKGROUND */
1740 }
1741
1742 #endif /* USE_BACKGROUND */
1743
1744
1745 #ifdef USE_GRAPHICS
1746
1747 /*
1748  * Initialize graphics
1749  */
1750 static bool init_graphics(void)
1751 {
1752         char filename[1024];
1753         char section[80];
1754         char name_tiles[128];
1755
1756         /* Large bitmap for the tiles */
1757         BITMAP *tiles = NULL;
1758         PALLETE tiles_pallete;
1759
1760         /* Size of each bitmap tile */
1761         int bitmap_wid;
1762         int bitmap_hgt;
1763
1764         int num_windows;
1765
1766         if (!graphics_initialized)
1767         {
1768                 /* Section name */
1769                 sprintf(section, "Mode-%d", resolution);
1770
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);
1774
1775                 /* Get bitmap filename */
1776                 strcpy(name_tiles, get_config_string(section, "bitmap_file", "8x8.bmp"));
1777
1778                 /* Get number of windows */
1779                 num_windows = get_config_int(section, "num_windows", 1);
1780
1781                 /* Build the name of the bitmap file */
1782                 path_build(filename, sizeof(filename), xtra_graf_dir, name_tiles);
1783
1784                 /* Open the bitmap file */
1785                 if ((tiles = load_bitmap(filename, tiles_pallete)) != NULL)
1786                 {
1787                         int i;
1788
1789                         /*
1790                          * Set the graphics mode to "new" if Adam Bolt's
1791                          * new 16x16 tiles are used.
1792                          */
1793                         ANGBAND_GRAF = get_config_string(section, "graf-mode", "old");
1794
1795                         /* Select the bitmap pallete */
1796                         set_palette_range(tiles_pallete, 0, COLOR_OFFSET - 1, 0);
1797
1798                         /* Prepare the graphics */
1799                         for (i = 0; i < num_windows; i++)
1800                         {
1801                                 term_data *td;
1802
1803                                 int col, row;
1804                                 int cols, rows;
1805                                 int width, height;
1806                                 int src_x, src_y;
1807                                 int tgt_x, tgt_y;
1808
1809                                 td = &data[i];
1810
1811                                 cols = tiles->w / bitmap_wid;
1812                                 rows = tiles->h / bitmap_hgt;
1813
1814                                 width = td->tile_wid * cols;
1815                                 height = td->tile_hgt * rows;
1816
1817                                 /* Initialize the tile graphics */
1818                                 td->tiles = create_bitmap(width, height);
1819
1820                                 for (row = 0; row < rows; ++row)
1821                                 {
1822                                         src_y = row * bitmap_hgt;
1823                                         tgt_y = row * td->tile_hgt;
1824
1825                                         for (col = 0; col < cols; ++col)
1826                                         {
1827                                                 src_x = col * bitmap_wid;
1828                                                 tgt_x = col * td->tile_wid;
1829
1830                                                 stretch_blit(tiles, td->tiles,
1831                                                         src_x, src_y,
1832                                                         bitmap_wid, bitmap_hgt,
1833                                                         tgt_x, tgt_y,
1834                                                         td->tile_wid, td->tile_hgt);
1835                                         }
1836                                 }
1837                         }
1838
1839                         /* Free the old tiles bitmap */
1840                         if (tiles) destroy_bitmap(tiles);
1841
1842                         graphics_initialized = TRUE;
1843
1844                         /* Success */
1845                         return (TRUE);
1846                 }
1847
1848                 /* Failure */
1849                 return (FALSE);
1850         }
1851
1852         /* Success */
1853         return (TRUE);
1854 }
1855
1856 #endif /* USE_GRAPHICS */
1857
1858 #ifdef USE_SOUND
1859
1860 /*
1861  * Initialize sound
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.
1867  */
1868 static bool init_sound(void)
1869 {
1870         int i, j, done;
1871
1872         char section[128];
1873         char filename[1024];
1874         char **argv;
1875
1876         struct ffblk f;
1877
1878         if (sound_initialized) return (TRUE);
1879
1880         reserve_voices(16, -1);
1881
1882         /* Initialize Allegro sound */
1883         if (!install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, NULL))
1884         {
1885 #ifdef USE_MOD_FILES
1886                 /*
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.
1890                  */
1891                 if (install_mod(8) > 0) mod_file_initialized = TRUE;
1892 #endif /* USE_MOD_FILES */
1893
1894                 /* Access the new sample */
1895                 path_build(filename, sizeof(filename), xtra_sound_dir, "sound.cfg");
1896
1897                 /* Read config info from "lib/xtra/sound/sound.cfg" */
1898                 override_config_file(filename);
1899
1900                 /* Sound section */
1901                 strcpy(section, "Sound");
1902
1903                 /* Prepare the sounds */
1904                 for (i = 1; i < SOUND_MAX; i++)
1905                 {
1906                         /* Get the sample names */
1907                         argv = get_config_argv(section, angband_sound_name[i], &sample_count[i]);
1908
1909                         /* Limit the number of samples */
1910                         if (sample_count[i] > SAMPLE_MAX) sample_count[i] = SAMPLE_MAX;
1911
1912                         for (j = 0; j < sample_count[i]; j++)
1913                         {
1914                                 /* Access the new sample */
1915                                 path_build(filename, sizeof(filename), xtra_sound_dir, argv[j]);
1916
1917                                 /* Load the sample */
1918                                 samples[i][j] = load_sample(filename);
1919                         }
1920                 }
1921
1922                 /*
1923                  * Get a list of music files
1924                  */
1925 #ifdef USE_MOD_FILES
1926                 if (mod_file_initialized)
1927                 {
1928                         done = findfirst(format("%s/*.*", xtra_music_dir), &f, FA_ARCH|FA_RDONLY);
1929                 }
1930                 else
1931 #endif /* USE_MOD_FILES */
1932                 done = findfirst(format("%s/*.mid", xtra_music_dir), &f, FA_ARCH|FA_RDONLY);
1933
1934
1935                 while (!done && (song_number <= MAX_SONGS))
1936                 {
1937                         /* Add music files */
1938                         {
1939                                 strcpy(music_files[song_number], f.ff_name);
1940                                 song_number++;
1941                         }
1942
1943                         done = findnext(&f);
1944                 }
1945
1946                 /* Use "angdos.cfg" */
1947                 override_config_file("angdos.cfg");
1948
1949                 /* Sound section */
1950                 strcpy(section, "Sound");
1951
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);
1955
1956                 /* Set the volume */
1957                 set_volume(digi_volume, midi_volume);
1958
1959                 /* Success */
1960                 return (TRUE);
1961         }
1962
1963         /* Init failed */
1964         return (FALSE);
1965 }
1966
1967
1968 /*
1969  * Make a sound
1970  */
1971 static errr Term_xtra_dos_sound(int v)
1972 {
1973         int n;
1974
1975         /* Sound disabled */
1976         if (!use_sound) return (1);
1977
1978         /* Illegal sound */
1979         if ((v < 0) || (v >= SOUND_MAX)) return (1);
1980
1981         /* Get a random sample from the available ones */
1982         n = randint0(sample_count[v]);
1983
1984         /* Play the sound, catch errors */
1985         if (samples[v][n])
1986         {
1987                 return (play_sample(samples[v][n], 255, 128, 1000, 0) == 0);
1988         }
1989
1990         /* Oops */
1991         return (1);
1992 }
1993
1994
1995 /*
1996  * Play a song-file
1997  */
1998 static void play_song(void)
1999 {
2000         char filename[256];
2001
2002         /* Clear the old song */
2003         if (midi_song) destroy_midi(midi_song);
2004         midi_song = NULL;
2005
2006 #ifdef USE_MOD_FILES
2007         if (mod_file_initialized)
2008         {
2009                 stop_mod();
2010                 destroy_mod(mod_song);
2011         }
2012 #endif /* USE_MOD_FILES */
2013
2014         /* Access the new song */
2015         path_build(filename, sizeof(filename), xtra_music_dir, music_files[current_song - 1]);
2016
2017         /* Load and play the new song */
2018         midi_song = load_midi(filename);
2019
2020         if (midi_song)
2021         {
2022                 play_midi(midi_song, 0);
2023         }
2024 #ifdef USE_MOD_FILES
2025         else if (mod_file_initialized)
2026         {
2027                 mod_song = load_mod(filename);
2028
2029                 if (mod_song) play_mod(mod_song, FALSE);
2030         }
2031 #endif /* USE_MOD_FILES */
2032 }
2033
2034 #endif /* USE_SOUND */
2035
2036
2037 /*
2038  * Attempt to initialize this file
2039  *
2040  * Hack -- we assume that "blank space" should be "white space"
2041  * (and not "black space" which might make more sense).
2042  *
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.
2046  *
2047  * We should attempt to "share" bitmaps (and fonts) between windows
2048  * with the same "tile" size.  XXX XXX XXX
2049  */
2050 errr init_dos(void)
2051 {
2052         term_data *td;
2053
2054         char section[80];
2055
2056         int screen_wid;
2057         int screen_hgt;
2058
2059         /* Initialize the Allegro library (never fails) */
2060         (void)allegro_init();
2061
2062         /* Install timer support for music and sound */
2063         install_timer();
2064
2065         /* Read config info from filename */
2066         set_config_file("angdos.cfg");
2067
2068         /* Main section */
2069         strcpy(section, "Angband");
2070
2071         /* Get screen size */
2072         resolution = get_config_int(section, "Resolution", 1);
2073
2074         /* Section name */
2075         sprintf(section, "Mode-%d", resolution);
2076
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);
2080
2081         /* Set the color depth */
2082         set_color_depth(8);
2083
2084         /* Auto-detect, and instantiate, the appropriate graphics mode */
2085         if ((set_gfx_mode(GFX_AUTODETECT, screen_wid, screen_hgt, 0, 0)) < 0)
2086         {
2087                 /*
2088                  * Requested graphics mode is not available
2089                  * We retry with the basic 640x480 mode
2090                  */
2091                 resolution = 1;
2092
2093                 if ((set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0)) < 0)
2094                 {
2095                         char error_text[1024];
2096
2097                         /* Save the Allegro error description */
2098                         strcpy(error_text, allegro_error);
2099
2100                         /* Shut down Allegro */
2101                         allegro_exit();
2102
2103                         /* Print the error description */
2104                         plog_fmt("Error selecting screen mode: %s", error_text);
2105
2106                         /* Failure */
2107                         return (-1);
2108                 }
2109         }
2110
2111         /* Hook in "z-util.c" hook */
2112         quit_aux = dos_quit_hook;
2113
2114         /* Build the "graf" path */
2115         path_build(xtra_graf_dir, sizeof(xtra_graf_dir), ANGBAND_DIR_XTRA, "graf");
2116
2117         /* Build the "font" path */
2118         path_build(xtra_font_dir, sizeof(xtra_font_dir), ANGBAND_DIR_XTRA, "font");
2119
2120         /* Build the "sound" path */
2121         path_build(xtra_sound_dir, sizeof(xtra_sound_dir), ANGBAND_DIR_XTRA, "sound");
2122
2123         /* Build the "music" path */
2124         path_build(xtra_music_dir, sizeof(xtra_music_dir), ANGBAND_DIR_XTRA, "music");
2125
2126         /* Initialize the windows */
2127         init_windows();
2128
2129 #ifdef USE_SOUND
2130
2131         /* Look for the sound preferences in "angdos.cfg" */
2132         if (!arg_sound)
2133         {
2134                 arg_sound = get_config_int("Angband", "Sound", TRUE);
2135         }
2136
2137 #endif /* USE_SOUND */
2138
2139 #ifdef USE_GRAPHICS
2140
2141         /* Look for the graphic preferences in "angdos.cfg" */
2142         if (!arg_graphics)
2143         {
2144                 arg_graphics = get_config_int("Angband", "Graphics", GRAPHICS_ORIGINAL);
2145         }
2146
2147 #endif /* USE_GRAPHICS */
2148
2149         /* Initialize the "complex" RNG for the midi-shuffle function */
2150         Rand_quick = FALSE;
2151         Rand_state_init(time(NULL));
2152
2153         /* Set the Angband colors/graphics/sound mode */
2154         Term_xtra_dos_react();
2155
2156 #ifdef USE_BACKGROUND
2157
2158         /* Initialize the background graphics */
2159         init_background();
2160
2161 #endif /* USE_BACKGROUND */
2162
2163         /* Clear the screen */
2164         clear_to_color(screen, COLOR_OFFSET + TERM_DARK);
2165
2166         /* Main screen */
2167         td = &data[0];
2168
2169         /* Build a cursor bitmap */
2170         cursor = create_bitmap(td->tile_wid, td->tile_hgt);
2171
2172         /* Erase the cursor sprite */
2173         clear(cursor);
2174
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);
2178
2179         /* Activate the main term */
2180         Term_activate(angband_term[0]);
2181
2182         /* Place the cursor */
2183         Term_curs_dos(0, 0);
2184
2185 #ifdef USE_BACKGROUND
2186
2187         /* Use transparent text */
2188         text_mode(-1);
2189
2190 #endif /* USE_BACKGROUND */
2191
2192         /* Success */
2193         return 0;
2194 }
2195
2196 #endif /* USE_DOS */
2197