OSDN Git Service

9df1f6ba26c53a2dcd85d1390163067e41ceca23
[jnethack/source.git] / win / win32 / mswproc.c
1 /* NetHack 3.6  mswproc.c       $NHDT-Date: 1575245201 2019/12/02 00:06:41 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.137 $ */
2 /* Copyright (C) 2001 by Alex Kompel     */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 /* JNetHack Copyright */
6 /* For 3.6-, Copyright (c) SHIRAKATA Kentaro, 2016-2023            */
7 /* JNetHack may be freely redistributed.  See license for details. */
8
9 /*
10  * This file implements the interface between the window port specific
11  * code in the mswin port and the rest of the nethack game engine.
12 */
13
14 #include "hack.h"
15 #include "color.h"
16 #include "dlb.h"
17 #include "func_tab.h" /* for extended commands */
18 #include "winMS.h"
19 #include <assert.h>
20 #include <mmsystem.h>
21 #include "mhmap.h"
22 #include "mhstatus.h"
23 #include "mhtext.h"
24 #include "mhmsgwnd.h"
25 #include "mhmenu.h"
26 #include "mhsplash.h"
27 #include "mhmsg.h"
28 #include "mhinput.h"
29 #include "mhaskyn.h"
30 #include "mhdlg.h"
31 #include "mhrip.h"
32 #include "mhmain.h"
33 #include "mhfont.h"
34 #include "resource.h"
35
36 #define LLEN 128
37
38 #define NHTRACE_LOG "nhtrace.log"
39
40 #ifdef DEBUG
41 # ifdef _DEBUG
42 static FILE* _s_debugfp = NULL;
43 extern void logDebug(const char *fmt, ...);
44 # endif
45 #endif
46
47 #ifndef _DEBUG
48 void
49 logDebug(const char *fmt, ...)
50 {
51 }
52 #endif
53
54 static void mswin_main_loop(void);
55 static BOOL initMapTiles(void);
56 static void mswin_color_from_string(char *colorstring, HBRUSH *brushptr,
57                                     COLORREF *colorptr);
58 static void prompt_for_player_selection(void);
59
60 #define TOTAL_BRUSHES 10
61 HBRUSH brush_table[TOTAL_BRUSHES];
62 int max_brush = 0;
63
64 HBRUSH menu_bg_brush = NULL;
65 HBRUSH menu_fg_brush = NULL;
66 HBRUSH text_bg_brush = NULL;
67 HBRUSH text_fg_brush = NULL;
68 HBRUSH status_bg_brush = NULL;
69 HBRUSH status_fg_brush = NULL;
70 HBRUSH message_bg_brush = NULL;
71 HBRUSH message_fg_brush = NULL;
72
73 COLORREF menu_bg_color = RGB(0, 0, 0);
74 COLORREF menu_fg_color = RGB(0xFF, 0xFF, 0xFF);
75 COLORREF text_bg_color = RGB(0, 0, 0);
76 COLORREF text_fg_color = RGB(0xFF, 0xFF, 0xFF);
77 COLORREF status_bg_color = RGB(0, 0, 0);
78 COLORREF status_fg_color = RGB(0xFF, 0xFF, 0xFF);
79 COLORREF message_bg_color = RGB(0, 0, 0);
80 COLORREF message_fg_color = RGB(0xFF, 0xFF, 0xFF);
81
82 strbuf_t raw_print_strbuf = { 0 };
83
84 /* Interface definition, for windows.c */
85 struct window_procs mswin_procs = {
86     "MSWIN",
87     WC_COLOR | WC_HILITE_PET | WC_ALIGN_MESSAGE | WC_ALIGN_STATUS | WC_INVERSE
88         | WC_SCROLL_AMOUNT | WC_SCROLL_MARGIN | WC_MAP_MODE | WC_FONT_MESSAGE
89         | WC_FONT_STATUS | WC_FONT_MENU | WC_FONT_TEXT | WC_FONT_MAP
90         | WC_FONTSIZ_MESSAGE | WC_FONTSIZ_STATUS | WC_FONTSIZ_MENU
91         | WC_FONTSIZ_TEXT | WC_TILE_WIDTH | WC_TILE_HEIGHT | WC_TILE_FILE
92         | WC_VARY_MSGCOUNT | WC_WINDOWCOLORS | WC_PLAYER_SELECTION
93         | WC_SPLASH_SCREEN | WC_POPUP_DIALOG | WC_MOUSE_SUPPORT,
94 #ifdef STATUS_HILITES
95     WC2_HITPOINTBAR | WC2_FLUSH_STATUS | WC2_RESET_STATUS | WC2_HILITE_STATUS |
96 #endif
97     0L,
98     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},   /* color availability */
99     mswin_init_nhwindows, mswin_player_selection, mswin_askname,
100     mswin_get_nh_event, mswin_exit_nhwindows, mswin_suspend_nhwindows,
101     mswin_resume_nhwindows, mswin_create_nhwindow, mswin_clear_nhwindow,
102     mswin_display_nhwindow, mswin_destroy_nhwindow, mswin_curs, mswin_putstr,
103     genl_putmixed, mswin_display_file, mswin_start_menu, mswin_add_menu,
104     mswin_end_menu, mswin_select_menu,
105     genl_message_menu, /* no need for X-specific handling */
106     mswin_update_inventory, mswin_mark_synch, mswin_wait_synch,
107 #ifdef CLIPPING
108     mswin_cliparound,
109 #endif
110 #ifdef POSITIONBAR
111     donull,
112 #endif
113     mswin_print_glyph, mswin_raw_print, mswin_raw_print_bold, mswin_nhgetch,
114     mswin_nh_poskey, mswin_nhbell, mswin_doprev_message, mswin_yn_function,
115     mswin_getlin, mswin_get_ext_cmd, mswin_number_pad, mswin_delay_output,
116 #ifdef CHANGE_COLOR /* only a Mac option currently */
117     mswin, mswin_change_background,
118 #endif
119     /* other defs that really should go away (they're tty specific) */
120     mswin_start_screen, mswin_end_screen, mswin_outrip,
121     mswin_preference_update, mswin_getmsghistory, mswin_putmsghistory,
122     mswin_status_init, mswin_status_finish, mswin_status_enablefield,
123     mswin_status_update,
124     genl_can_suspend_yes,
125 };
126
127 /*
128 init_nhwindows(int* argcp, char** argv)
129                 -- Initialize the windows used by NetHack.  This can also
130                    create the standard windows listed at the top, but does
131                    not display them.
132                 -- Any commandline arguments relevant to the windowport
133                    should be interpreted, and *argcp and *argv should
134                    be changed to remove those arguments.
135                 -- When the message window is created, the variable
136                    iflags.window_inited needs to be set to TRUE.  Otherwise
137                    all plines() will be done via raw_print().
138                 ** Why not have init_nhwindows() create all of the "standard"
139                 ** windows?  Or at least all but WIN_INFO?      -dean
140 */
141 void
142 mswin_init_nhwindows(int *argc, char **argv)
143 {
144     UNREFERENCED_PARAMETER(argc);
145     UNREFERENCED_PARAMETER(argv);
146
147 #ifdef DEBUG
148 # ifdef _DEBUG
149     if (showdebug(NHTRACE_LOG) && !_s_debugfp) {
150         /* truncate trace file */
151         /* BUG: this relies on current working directory */
152         _s_debugfp = fopen(NHTRACE_LOG, "w");
153     }
154 # endif
155 #endif
156     logDebug("mswin_init_nhwindows()\n");
157
158     mswin_nh_input_init();
159
160     /* set it to WIN_ERR so we can detect attempts to
161        use this ID before it is inialized */
162     WIN_MAP = WIN_ERR;
163
164     /* Read Windows settings from the reqistry */
165     /* First set safe defaults */
166     GetNHApp()->regMainMinX = CW_USEDEFAULT;
167     mswin_read_reg();
168     /* Create the main window */
169     GetNHApp()->hMainWnd = mswin_init_main_window();
170     if (!GetNHApp()->hMainWnd) {
171         panic("Cannot create main window");
172     }
173
174     /* Set menu check mark for interface mode */
175     mswin_menu_check_intf_mode();
176
177     /* check default values */
178     if (iflags.wc_fontsiz_status < NHFONT_SIZE_MIN
179         || iflags.wc_fontsiz_status > NHFONT_SIZE_MAX)
180         iflags.wc_fontsiz_status = NHFONT_DEFAULT_SIZE;
181
182     if (iflags.wc_fontsiz_message < NHFONT_SIZE_MIN
183         || iflags.wc_fontsiz_message > NHFONT_SIZE_MAX)
184         iflags.wc_fontsiz_message = NHFONT_DEFAULT_SIZE;
185
186     if (iflags.wc_fontsiz_text < NHFONT_SIZE_MIN
187         || iflags.wc_fontsiz_text > NHFONT_SIZE_MAX)
188         iflags.wc_fontsiz_text = NHFONT_DEFAULT_SIZE;
189
190     if (iflags.wc_fontsiz_menu < NHFONT_SIZE_MIN
191         || iflags.wc_fontsiz_menu > NHFONT_SIZE_MAX)
192         iflags.wc_fontsiz_menu = NHFONT_DEFAULT_SIZE;
193
194     if (iflags.wc_align_message == 0)
195         iflags.wc_align_message = ALIGN_TOP;
196     if (iflags.wc_align_status == 0)
197         iflags.wc_align_status = ALIGN_BOTTOM;
198     if (iflags.wc_scroll_margin == 0)
199         iflags.wc_scroll_margin = DEF_CLIPAROUND_MARGIN;
200     if (iflags.wc_scroll_amount == 0)
201         iflags.wc_scroll_amount = DEF_CLIPAROUND_AMOUNT;
202     if (iflags.wc_tile_width == 0)
203         iflags.wc_tile_width = TILE_X;
204     if (iflags.wc_tile_height == 0)
205         iflags.wc_tile_height = TILE_Y;
206
207     if (iflags.wc_vary_msgcount == 0)
208         iflags.wc_vary_msgcount = 4;
209
210     /* force tabs in menus */
211     iflags.menu_tab_sep = 1;
212
213     /* force toptenwin to be true.  toptenwin is the option that decides
214      * whether to
215      * write output to a window or stdout.  stdout doesn't make sense on
216      * Windows
217      * non-console applications
218      */
219     iflags.toptenwin = 1;
220     set_option_mod_status("toptenwin", SET_IN_FILE);
221     //set_option_mod_status("perm_invent", SET_IN_FILE);
222     set_option_mod_status("mouse_support", SET_IN_GAME);
223
224     /* initialize map tiles bitmap */
225     initMapTiles();
226
227     /* set tile-related options to readonly */
228     set_wc_option_mod_status(WC_TILE_WIDTH | WC_TILE_HEIGHT | WC_TILE_FILE,
229                              DISP_IN_GAME);
230
231     /* set font-related options to change in the game */
232     set_wc_option_mod_status(
233         WC_HILITE_PET | WC_ALIGN_MESSAGE | WC_ALIGN_STATUS | WC_SCROLL_AMOUNT
234             | WC_SCROLL_MARGIN | WC_MAP_MODE | WC_FONT_MESSAGE
235             | WC_FONT_STATUS | WC_FONT_MENU | WC_FONT_TEXT
236             | WC_FONTSIZ_MESSAGE | WC_FONTSIZ_STATUS | WC_FONTSIZ_MENU
237             | WC_FONTSIZ_TEXT | WC_VARY_MSGCOUNT,
238         SET_IN_GAME);
239
240     mswin_color_from_string(iflags.wc_foregrnd_menu, &menu_fg_brush,
241                             &menu_fg_color);
242     mswin_color_from_string(iflags.wc_foregrnd_message, &message_fg_brush,
243                             &message_fg_color);
244     mswin_color_from_string(iflags.wc_foregrnd_status, &status_fg_brush,
245                             &status_fg_color);
246     mswin_color_from_string(iflags.wc_foregrnd_text, &text_fg_brush,
247                             &text_fg_color);
248     mswin_color_from_string(iflags.wc_backgrnd_menu, &menu_bg_brush,
249                             &menu_bg_color);
250     mswin_color_from_string(iflags.wc_backgrnd_message, &message_bg_brush,
251                             &message_bg_color);
252     mswin_color_from_string(iflags.wc_backgrnd_status, &status_bg_brush,
253                             &status_bg_color);
254     mswin_color_from_string(iflags.wc_backgrnd_text, &text_bg_brush,
255                             &text_bg_color);
256
257     if (iflags.wc_splash_screen)
258         mswin_display_splash_window(FALSE);
259
260     iflags.window_inited = TRUE;
261 }
262
263 /* Do a window-port specific player type selection. If player_selection()
264    offers a Quit option, it is its responsibility to clean up and terminate
265    the process. You need to fill in pl_character[0].
266 */
267 void
268 mswin_player_selection(void)
269 {
270     logDebug("mswin_player_selection()\n");
271
272     if (iflags.wc_player_selection == VIA_DIALOG) {
273         /* pick player type randomly (use pre-selected
274          * role/race/gender/alignment) */
275         if (flags.randomall) {
276             if (flags.initrole < 0) {
277                 flags.initrole = pick_role(flags.initrace, flags.initgend,
278                                            flags.initalign, PICK_RANDOM);
279                 if (flags.initrole < 0) {
280                     raw_print("Incompatible role!");
281                     flags.initrole = randrole(FALSE);
282                 }
283             }
284
285             if (flags.initrace < 0
286                 || !validrace(flags.initrole, flags.initrace)) {
287                 flags.initrace = pick_race(flags.initrole, flags.initgend,
288                                            flags.initalign, PICK_RANDOM);
289                 if (flags.initrace < 0) {
290                     raw_print("Incompatible race!");
291                     flags.initrace = randrace(flags.initrole);
292                 }
293             }
294
295             if (flags.initgend < 0
296                 || !validgend(flags.initrole, flags.initrace,
297                               flags.initgend)) {
298                 flags.initgend = pick_gend(flags.initrole, flags.initrace,
299                                            flags.initalign, PICK_RANDOM);
300                 if (flags.initgend < 0) {
301                     raw_print("Incompatible gender!");
302                     flags.initgend = randgend(flags.initrole, flags.initrace);
303                 }
304             }
305
306             if (flags.initalign < 0
307                 || !validalign(flags.initrole, flags.initrace,
308                                flags.initalign)) {
309                 flags.initalign = pick_align(flags.initrole, flags.initrace,
310                                              flags.initgend, PICK_RANDOM);
311                 if (flags.initalign < 0) {
312                     raw_print("Incompatible alignment!");
313                     flags.initalign =
314                         randalign(flags.initrole, flags.initrace);
315                 }
316             }
317         } else {
318             /* select a role */
319             if (!mswin_player_selection_window()) {
320                 bail(0);
321             }
322         }
323     } else { /* iflags.wc_player_selection == VIA_PROMPTS */
324         prompt_for_player_selection();
325     }
326 }
327
328 void
329 prompt_for_player_selection(void)
330 {
331     int i, k, n;
332     char pick4u = 'n', thisch, lastch = 0;
333     char pbuf[QBUFSZ], plbuf[QBUFSZ];
334     winid win;
335     anything any;
336     menu_item *selected = 0;
337     DWORD box_result;
338
339     logDebug("prompt_for_player_selection()\n");
340
341     /* prevent an unnecessary prompt */
342     rigid_role_checks();
343
344     /* Should we randomly pick for the player? */
345     if (!flags.randomall
346         && (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE
347             || flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE)) {
348         /* int echoline; */
349         char *prompt = build_plselection_prompt(
350             pbuf, QBUFSZ, flags.initrole, flags.initrace, flags.initgend,
351             flags.initalign);
352
353         /* tty_putstr(BASE_WINDOW, 0, ""); */
354         /* echoline = wins[BASE_WINDOW]->cury; */
355         box_result = NHMessageBox(NULL, prompt, MB_YESNOCANCEL | MB_DEFBUTTON1
356                                                     | MB_ICONQUESTION);
357         pick4u =
358             (box_result == IDYES) ? 'y' : (box_result == IDNO) ? 'n' : '\033';
359         /* tty_putstr(BASE_WINDOW, 0, prompt); */
360         do {
361             /* pick4u = lowc(readchar()); */
362             if (index(quitchars, pick4u))
363                 pick4u = 'y';
364         } while (!index(ynqchars, pick4u));
365         if ((int) strlen(prompt) + 1 < CO) {
366             /* Echo choice and move back down line */
367             /* tty_putsym(BASE_WINDOW, (int)strlen(prompt)+1, echoline,
368              * pick4u); */
369             /* tty_putstr(BASE_WINDOW, 0, ""); */
370         } else
371             /* Otherwise it's hard to tell where to echo, and things are
372              * wrapping a bit messily anyway, so (try to) make sure the next
373              * question shows up well and doesn't get wrapped at the
374              * bottom of the window.
375              */
376             /* tty_clear_nhwindow(BASE_WINDOW) */;
377
378         if (pick4u != 'y' && pick4u != 'n') {
379         give_up: /* Quit */
380             if (selected)
381                 free((genericptr_t) selected);
382             bail((char *) 0);
383             /*NOTREACHED*/
384             return;
385         }
386     }
387
388     (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
389                                    flags.initrace, flags.initgend,
390                                    flags.initalign);
391
392     /* Select a role, if necessary */
393     /* we'll try to be compatible with pre-selected race/gender/alignment,
394      * but may not succeed */
395     if (flags.initrole < 0) {
396         char rolenamebuf[QBUFSZ];
397         /* Process the choice */
398         if (pick4u == 'y' || flags.initrole == ROLE_RANDOM
399             || flags.randomall) {
400             /* Pick a random role */
401             flags.initrole = pick_role(flags.initrace, flags.initgend,
402                                        flags.initalign, PICK_RANDOM);
403             if (flags.initrole < 0) {
404                 /* tty_putstr(BASE_WINDOW, 0, "Incompatible role!"); */
405                 flags.initrole = randrole(FALSE);
406             }
407         } else {
408             /* tty_clear_nhwindow(BASE_WINDOW); */
409             /* tty_putstr(BASE_WINDOW, 0, "Choosing Character's Role"); */
410             /* Prompt for a role */
411             win = create_nhwindow(NHW_MENU);
412             start_menu(win);
413             any = zeroany; /* zero out all bits */
414             for (i = 0; roles[i].name.m; i++) {
415                 if (ok_role(i, flags.initrace, flags.initgend,
416                             flags.initalign)) {
417                     any.a_int = i + 1; /* must be non-zero */
418                     thisch = lowc(roles[i].name.m[0]);
419                     if (thisch == lastch)
420                         thisch = highc(thisch);
421                     if (flags.initgend != ROLE_NONE
422                         && flags.initgend != ROLE_RANDOM) {
423                         if (flags.initgend == 1 && roles[i].name.f)
424                             Strcpy(rolenamebuf, roles[i].name.f);
425                         else
426                             Strcpy(rolenamebuf, roles[i].name.m);
427                     } else {
428                         if (roles[i].name.f) {
429                             Strcpy(rolenamebuf, roles[i].name.m);
430                             Strcat(rolenamebuf, "/");
431                             Strcat(rolenamebuf, roles[i].name.f);
432                         } else
433                             Strcpy(rolenamebuf, roles[i].name.m);
434                     }
435                     add_menu(win, NO_GLYPH, &any, thisch, 0, ATR_NONE,
436                              an(rolenamebuf), MENU_UNSELECTED);
437                     lastch = thisch;
438                 }
439             }
440             any.a_int = pick_role(flags.initrace, flags.initgend,
441                                   flags.initalign, PICK_RANDOM) + 1;
442             if (any.a_int == 0) /* must be non-zero */
443                 any.a_int = randrole(FALSE) + 1;
444             add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
445                      MENU_UNSELECTED);
446             any.a_int = i + 1; /* must be non-zero */
447             add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
448                      MENU_UNSELECTED);
449             Sprintf(pbuf, "Pick a role for your %s", plbuf);
450             end_menu(win, pbuf);
451             n = select_menu(win, PICK_ONE, &selected);
452             destroy_nhwindow(win);
453
454             /* Process the choice */
455             if (n != 1 || selected[0].item.a_int == any.a_int)
456                 goto give_up; /* Selected quit */
457
458             flags.initrole = selected[0].item.a_int - 1;
459             free((genericptr_t) selected), selected = 0;
460         }
461         (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
462                                        flags.initrace, flags.initgend,
463                                        flags.initalign);
464     }
465
466     /* Select a race, if necessary */
467     /* force compatibility with role, try for compatibility with
468      * pre-selected gender/alignment */
469     if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) {
470         /* pre-selected race not valid */
471         if (pick4u == 'y' || flags.initrace == ROLE_RANDOM
472             || flags.randomall) {
473             flags.initrace = pick_race(flags.initrole, flags.initgend,
474                                        flags.initalign, PICK_RANDOM);
475             if (flags.initrace < 0) {
476                 /* tty_putstr(BASE_WINDOW, 0, "Incompatible race!"); */
477                 flags.initrace = randrace(flags.initrole);
478             }
479         } else { /* pick4u == 'n' */
480             /* Count the number of valid races */
481             n = 0; /* number valid */
482             k = 0; /* valid race */
483             for (i = 0; races[i].noun; i++) {
484                 if (ok_race(flags.initrole, i, flags.initgend,
485                             flags.initalign)) {
486                     n++;
487                     k = i;
488                 }
489             }
490             if (n == 0) {
491                 for (i = 0; races[i].noun; i++) {
492                     if (validrace(flags.initrole, i)) {
493                         n++;
494                         k = i;
495                     }
496                 }
497             }
498
499             /* Permit the user to pick, if there is more than one */
500             if (n > 1) {
501                 /* tty_clear_nhwindow(BASE_WINDOW); */
502                 /* tty_putstr(BASE_WINDOW, 0, "Choosing Race"); */
503                 win = create_nhwindow(NHW_MENU);
504                 start_menu(win);
505                 any = zeroany; /* zero out all bits */
506                 for (i = 0; races[i].noun; i++)
507                     if (ok_race(flags.initrole, i, flags.initgend,
508                                 flags.initalign)) {
509                         any.a_int = i + 1; /* must be non-zero */
510                         add_menu(win, NO_GLYPH, &any, races[i].noun[0], 0,
511                                  ATR_NONE, races[i].noun, MENU_UNSELECTED);
512                     }
513                 any.a_int = pick_race(flags.initrole, flags.initgend,
514                                       flags.initalign, PICK_RANDOM) + 1;
515                 if (any.a_int == 0) /* must be non-zero */
516                     any.a_int = randrace(flags.initrole) + 1;
517                 add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
518                          MENU_UNSELECTED);
519                 any.a_int = i + 1; /* must be non-zero */
520                 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
521                          MENU_UNSELECTED);
522                 Sprintf(pbuf, "Pick the race of your %s", plbuf);
523                 end_menu(win, pbuf);
524                 n = select_menu(win, PICK_ONE, &selected);
525                 destroy_nhwindow(win);
526                 if (n != 1 || selected[0].item.a_int == any.a_int)
527                     goto give_up; /* Selected quit */
528
529                 k = selected[0].item.a_int - 1;
530                 free((genericptr_t) selected), selected = 0;
531             }
532             flags.initrace = k;
533         }
534         (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
535                                        flags.initrace, flags.initgend,
536                                        flags.initalign);
537     }
538
539     /* Select a gender, if necessary */
540     /* force compatibility with role/race, try for compatibility with
541      * pre-selected alignment */
542     if (flags.initgend < 0
543         || !validgend(flags.initrole, flags.initrace, flags.initgend)) {
544         /* pre-selected gender not valid */
545         if (pick4u == 'y' || flags.initgend == ROLE_RANDOM
546             || flags.randomall) {
547             flags.initgend = pick_gend(flags.initrole, flags.initrace,
548                                        flags.initalign, PICK_RANDOM);
549             if (flags.initgend < 0) {
550                 /* tty_putstr(BASE_WINDOW, 0, "Incompatible gender!"); */
551                 flags.initgend = randgend(flags.initrole, flags.initrace);
552             }
553         } else { /* pick4u == 'n' */
554             /* Count the number of valid genders */
555             n = 0; /* number valid */
556             k = 0; /* valid gender */
557             for (i = 0; i < ROLE_GENDERS; i++) {
558                 if (ok_gend(flags.initrole, flags.initrace, i,
559                             flags.initalign)) {
560                     n++;
561                     k = i;
562                 }
563             }
564             if (n == 0) {
565                 for (i = 0; i < ROLE_GENDERS; i++) {
566                     if (validgend(flags.initrole, flags.initrace, i)) {
567                         n++;
568                         k = i;
569                     }
570                 }
571             }
572
573             /* Permit the user to pick, if there is more than one */
574             if (n > 1) {
575                 /* tty_clear_nhwindow(BASE_WINDOW); */
576                 /* tty_putstr(BASE_WINDOW, 0, "Choosing Gender"); */
577                 win = create_nhwindow(NHW_MENU);
578                 start_menu(win);
579                 any = zeroany; /* zero out all bits */
580                 for (i = 0; i < ROLE_GENDERS; i++)
581                     if (ok_gend(flags.initrole, flags.initrace, i,
582                                 flags.initalign)) {
583                         any.a_int = i + 1;
584                         add_menu(win, NO_GLYPH, &any, genders[i].adj[0], 0,
585                                  ATR_NONE, genders[i].adj, MENU_UNSELECTED);
586                     }
587                 any.a_int = pick_gend(flags.initrole, flags.initrace,
588                                       flags.initalign, PICK_RANDOM) + 1;
589                 if (any.a_int == 0) /* must be non-zero */
590                     any.a_int = randgend(flags.initrole, flags.initrace) + 1;
591                 add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
592                          MENU_UNSELECTED);
593                 any.a_int = i + 1; /* must be non-zero */
594                 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
595                          MENU_UNSELECTED);
596                 Sprintf(pbuf, "Pick the gender of your %s", plbuf);
597                 end_menu(win, pbuf);
598                 n = select_menu(win, PICK_ONE, &selected);
599                 destroy_nhwindow(win);
600                 if (n != 1 || selected[0].item.a_int == any.a_int)
601                     goto give_up; /* Selected quit */
602
603                 k = selected[0].item.a_int - 1;
604                 free((genericptr_t) selected), selected = 0;
605             }
606             flags.initgend = k;
607         }
608         (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole,
609                                        flags.initrace, flags.initgend,
610                                        flags.initalign);
611     }
612
613     /* Select an alignment, if necessary */
614     /* force compatibility with role/race/gender */
615     if (flags.initalign < 0
616         || !validalign(flags.initrole, flags.initrace, flags.initalign)) {
617         /* pre-selected alignment not valid */
618         if (pick4u == 'y' || flags.initalign == ROLE_RANDOM
619             || flags.randomall) {
620             flags.initalign = pick_align(flags.initrole, flags.initrace,
621                                          flags.initgend, PICK_RANDOM);
622             if (flags.initalign < 0) {
623                 /* tty_putstr(BASE_WINDOW, 0, "Incompatible alignment!"); */
624                 flags.initalign = randalign(flags.initrole, flags.initrace);
625             }
626         } else { /* pick4u == 'n' */
627             /* Count the number of valid alignments */
628             n = 0; /* number valid */
629             k = 0; /* valid alignment */
630             for (i = 0; i < ROLE_ALIGNS; i++) {
631                 if (ok_align(flags.initrole, flags.initrace, flags.initgend,
632                              i)) {
633                     n++;
634                     k = i;
635                 }
636             }
637             if (n == 0) {
638                 for (i = 0; i < ROLE_ALIGNS; i++) {
639                     if (validalign(flags.initrole, flags.initrace, i)) {
640                         n++;
641                         k = i;
642                     }
643                 }
644             }
645
646             /* Permit the user to pick, if there is more than one */
647             if (n > 1) {
648                 /* tty_clear_nhwindow(BASE_WINDOW); */
649                 /* tty_putstr(BASE_WINDOW, 0, "Choosing Alignment"); */
650                 win = create_nhwindow(NHW_MENU);
651                 start_menu(win);
652                 any = zeroany; /* zero out all bits */
653                 for (i = 0; i < ROLE_ALIGNS; i++)
654                     if (ok_align(flags.initrole, flags.initrace,
655                                  flags.initgend, i)) {
656                         any.a_int = i + 1;
657                         add_menu(win, NO_GLYPH, &any, aligns[i].adj[0], 0,
658                                  ATR_NONE, aligns[i].adj, MENU_UNSELECTED);
659                     }
660                 any.a_int = pick_align(flags.initrole, flags.initrace,
661                                        flags.initgend, PICK_RANDOM) + 1;
662                 if (any.a_int == 0) /* must be non-zero */
663                     any.a_int = randalign(flags.initrole, flags.initrace) + 1;
664                 add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
665                          MENU_UNSELECTED);
666                 any.a_int = i + 1; /* must be non-zero */
667                 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
668                          MENU_UNSELECTED);
669                 Sprintf(pbuf, "Pick the alignment of your %s", plbuf);
670                 end_menu(win, pbuf);
671                 n = select_menu(win, PICK_ONE, &selected);
672                 destroy_nhwindow(win);
673                 if (n != 1 || selected[0].item.a_int == any.a_int)
674                     goto give_up; /* Selected quit */
675
676                 k = selected[0].item.a_int - 1;
677                 free((genericptr_t) selected), selected = 0;
678             }
679             flags.initalign = k;
680         }
681     }
682     /* Success! */
683     /* tty_display_nhwindow(BASE_WINDOW, FALSE); */
684 }
685
686 /* Ask the user for a player name. */
687 void
688 mswin_askname(void)
689 {
690     logDebug("mswin_askname()\n");
691
692     if (mswin_getlin_window("Who are you?", plname, PL_NSIZ) == IDCANCEL) {
693         bail("bye-bye");
694         /* not reached */
695     }
696 }
697
698 /* Does window event processing (e.g. exposure events).
699    A noop for the tty and X window-ports.
700 */
701 void
702 mswin_get_nh_event(void)
703 {
704     MSG msg;
705
706     logDebug("mswin_get_nh_event()\n");
707
708     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != 0) {
709         if (!TranslateAccelerator(msg.hwnd, GetNHApp()->hAccelTable, &msg)) {
710             TranslateMessage(&msg);
711             DispatchMessage(&msg);
712         }
713     }
714     return;
715 }
716
717 /* Exits the window system.  This should dismiss all windows,
718    except the "window" used for raw_print().  str is printed if possible.
719 */
720 void
721 mswin_exit_nhwindows(const char *str)
722 {
723     logDebug("mswin_exit_nhwindows(%s)\n", str);
724
725     /* Write Window settings to the registry */
726     mswin_write_reg();
727
728     /* set things back to failsafes */
729     windowprocs = *get_safe_procs(0);
730
731     /* and make sure there is still a way to communicate something */
732     windowprocs.win_raw_print = mswin_raw_print;
733     windowprocs.win_raw_print_bold = mswin_raw_print_bold;
734     windowprocs.win_wait_synch = mswin_wait_synch;
735 }
736
737 /* Prepare the window to be suspended. */
738 void
739 mswin_suspend_nhwindows(const char *str)
740 {
741     logDebug("mswin_suspend_nhwindows(%s)\n", str);
742
743     return;
744 }
745
746 /* Restore the windows after being suspended. */
747 void
748 mswin_resume_nhwindows()
749 {
750     logDebug("mswin_resume_nhwindows()\n");
751
752     return;
753 }
754
755 /*  Create a window of type "type" which can be
756         NHW_MESSAGE     (top line)
757         NHW_STATUS      (bottom lines)
758         NHW_MAP         (main dungeon)
759         NHW_MENU        (inventory or other "corner" windows)
760         NHW_TEXT        (help/text, full screen paged window)
761 */
762 winid
763 mswin_create_nhwindow(int type)
764 {
765     winid i = 0;
766     MSNHMsgAddWnd data;
767
768     logDebug("mswin_create_nhwindow(%d)\n", type);
769
770     /* Return the next available winid
771      */
772
773     for (i = 1; i < MAXWINDOWS; i++)
774         if (GetNHApp()->windowlist[i].win == NULL
775             && !GetNHApp()->windowlist[i].dead)
776             break;
777     if (i == MAXWINDOWS)
778         panic("ERROR:  No windows available...\n");
779
780     switch (type) {
781     case NHW_MAP: {
782         GetNHApp()->windowlist[i].win = mswin_init_map_window();
783         GetNHApp()->windowlist[i].type = type;
784         GetNHApp()->windowlist[i].dead = 0;
785         break;
786     }
787     case NHW_MESSAGE: {
788         GetNHApp()->windowlist[i].win = mswin_init_message_window();
789         GetNHApp()->windowlist[i].type = type;
790         GetNHApp()->windowlist[i].dead = 0;
791         break;
792     }
793     case NHW_STATUS: {
794         GetNHApp()->windowlist[i].win = mswin_init_status_window();
795         GetNHApp()->windowlist[i].type = type;
796         GetNHApp()->windowlist[i].dead = 0;
797         break;
798     }
799     case NHW_MENU: {
800         GetNHApp()->windowlist[i].win = NULL; // will create later
801         GetNHApp()->windowlist[i].type = type;
802         GetNHApp()->windowlist[i].dead = 1;
803         break;
804     }
805     case NHW_TEXT: {
806         GetNHApp()->windowlist[i].win = mswin_init_text_window();
807         GetNHApp()->windowlist[i].type = type;
808         GetNHApp()->windowlist[i].dead = 0;
809         break;
810     }
811     }
812
813     ZeroMemory(&data, sizeof(data));
814     data.wid = i;
815     SendMessage(GetNHApp()->hMainWnd, WM_MSNH_COMMAND,
816                 (WPARAM) MSNH_MSG_ADDWND, (LPARAM) &data);
817     return i;
818 }
819
820 /* Clear the given window, when asked to. */
821 void
822 mswin_clear_nhwindow(winid wid)
823 {
824     logDebug("mswin_clear_nhwindow(%d)\n", wid);
825
826     if ((wid >= 0) && (wid < MAXWINDOWS)
827         && (GetNHApp()->windowlist[wid].win != NULL)) {
828         if (GetNHApp()->windowlist[wid].type == NHW_MAP) {
829             if (Is_rogue_level(&u.uz))
830                 if (iflags.wc_map_mode == MAP_MODE_ASCII_FIT_TO_SCREEN ||
831                     iflags.wc_map_mode == MAP_MODE_TILES_FIT_TO_SCREEN)
832
833                     mswin_map_mode(mswin_hwnd_from_winid(WIN_MAP),
834                                    ROGUE_LEVEL_MAP_MODE_FIT_TO_SCREEN);
835                 else
836                     mswin_map_mode(mswin_hwnd_from_winid(WIN_MAP),
837                                    ROGUE_LEVEL_MAP_MODE);
838             else
839                 mswin_map_mode(mswin_hwnd_from_winid(WIN_MAP),
840                                iflags.wc_map_mode);
841         }
842
843         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
844                     (WPARAM) MSNH_MSG_CLEAR_WINDOW, (LPARAM) NULL);
845     }
846 }
847
848 /* -- Display the window on the screen.  If there is data
849                    pending for output in that window, it should be sent.
850                    If blocking is TRUE, display_nhwindow() will not
851                    return until the data has been displayed on the screen,
852                    and acknowledged by the user where appropriate.
853                 -- All calls are blocking in the tty window-port.
854                 -- Calling display_nhwindow(WIN_MESSAGE,???) will do a
855                    --more--, if necessary, in the tty window-port.
856 */
857 void
858 mswin_display_nhwindow(winid wid, BOOLEAN_P block)
859 {
860     logDebug("mswin_display_nhwindow(%d, %d)\n", wid, block);
861     if (GetNHApp()->windowlist[wid].win != NULL) {
862         ShowWindow(GetNHApp()->windowlist[wid].win, SW_SHOW);
863         mswin_layout_main_window(GetNHApp()->windowlist[wid].win);
864         if (GetNHApp()->windowlist[wid].type == NHW_MENU) {
865             MENU_ITEM_P *p;
866             mswin_menu_window_select_menu(GetNHApp()->windowlist[wid].win,
867                                           PICK_NONE, &p, TRUE);
868         }
869         if (GetNHApp()->windowlist[wid].type == NHW_TEXT) {
870             mswin_display_text_window(GetNHApp()->windowlist[wid].win);
871         }
872         if (GetNHApp()->windowlist[wid].type == NHW_RIP) {
873             mswin_display_RIP_window(GetNHApp()->windowlist[wid].win);
874         } else {
875             if (!block) {
876                 UpdateWindow(GetNHApp()->windowlist[wid].win);
877             } else {
878                 if (GetNHApp()->windowlist[wid].type == NHW_MAP) {
879                     (void) mswin_nhgetch();
880                 }
881             }
882         }
883         SetFocus(GetNHApp()->hMainWnd);
884     }
885 }
886
887 HWND
888 mswin_hwnd_from_winid(winid wid)
889 {
890     if (wid >= 0 && wid < MAXWINDOWS) {
891         return GetNHApp()->windowlist[wid].win;
892     } else {
893         return NULL;
894     }
895 }
896
897 winid
898 mswin_winid_from_handle(HWND hWnd)
899 {
900     winid i = 0;
901
902     for (i = 1; i < MAXWINDOWS; i++)
903         if (GetNHApp()->windowlist[i].win == hWnd)
904             return i;
905     return -1;
906 }
907
908 winid
909 mswin_winid_from_type(int type)
910 {
911     winid i = 0;
912
913     for (i = 1; i < MAXWINDOWS; i++)
914         if (GetNHApp()->windowlist[i].type == type)
915             return i;
916     return -1;
917 }
918
919 void
920 mswin_window_mark_dead(winid wid)
921 {
922     if (wid >= 0 && wid < MAXWINDOWS) {
923         GetNHApp()->windowlist[wid].win = NULL;
924         GetNHApp()->windowlist[wid].dead = 1;
925     }
926 }
927
928 /* Destroy will dismiss the window if the window has not
929  * already been dismissed.
930 */
931 void
932 mswin_destroy_nhwindow(winid wid)
933 {
934     logDebug("mswin_destroy_nhwindow(%d)\n", wid);
935
936     if ((GetNHApp()->windowlist[wid].type == NHW_MAP)
937         || (GetNHApp()->windowlist[wid].type == NHW_MESSAGE)
938         || (GetNHApp()->windowlist[wid].type == NHW_STATUS)) {
939         /* main windows is going to take care of those */
940         return;
941     }
942
943     if (wid != -1) {
944         if (!GetNHApp()->windowlist[wid].dead
945             && GetNHApp()->windowlist[wid].win != NULL)
946             DestroyWindow(GetNHApp()->windowlist[wid].win);
947         GetNHApp()->windowlist[wid].win = NULL;
948         GetNHApp()->windowlist[wid].type = 0;
949         GetNHApp()->windowlist[wid].dead = 0;
950     }
951 }
952
953 /* Next output to window will start at (x,y), also moves
954  displayable cursor to (x,y).  For backward compatibility,
955  1 <= x < cols, 0 <= y < rows, where cols and rows are
956  the size of window.
957 */
958 void
959 mswin_curs(winid wid, int x, int y)
960 {
961     logDebug("mswin_curs(%d, %d, %d)\n", wid, x, y);
962
963     if ((wid >= 0) && (wid < MAXWINDOWS)
964         && (GetNHApp()->windowlist[wid].win != NULL)) {
965         MSNHMsgCursor data;
966         data.x = x;
967         data.y = y;
968         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
969                     (WPARAM) MSNH_MSG_CURSOR, (LPARAM) &data);
970     }
971 }
972
973 /*
974 putstr(window, attr, str)
975                 -- Print str on the window with the given attribute.  Only
976                    printable ASCII characters (040-0126) must be supported.
977                    Multiple putstr()s are output on separate lines.
978 Attributes
979                    can be one of
980                         ATR_NONE (or 0)
981                         ATR_ULINE
982                         ATR_BOLD
983                         ATR_BLINK
984                         ATR_INVERSE
985                    If a window-port does not support all of these, it may map
986                    unsupported attributes to a supported one (e.g. map them
987                    all to ATR_INVERSE).  putstr() may compress spaces out of
988                    str, break str, or truncate str, if necessary for the
989                    display.  Where putstr() breaks a line, it has to clear
990                    to end-of-line.
991                 -- putstr should be implemented such that if two putstr()s
992                    are done consecutively the user will see the first and
993                    then the second.  In the tty port, pline() achieves this
994                    by calling more() or displaying both on the same line.
995 */
996 void
997 mswin_putstr(winid wid, int attr, const char *text)
998 {
999     logDebug("mswin_putstr(%d, %d, %s)\n", wid, attr, text);
1000
1001     mswin_putstr_ex(wid, attr, text, 0);
1002 }
1003
1004 void
1005 mswin_putstr_ex(winid wid, int attr, const char *text, int app)
1006 {
1007     if ((wid >= 0) && (wid < MAXWINDOWS)) {
1008         if (GetNHApp()->windowlist[wid].win == NULL
1009             && GetNHApp()->windowlist[wid].type == NHW_MENU) {
1010             GetNHApp()->windowlist[wid].win =
1011                 mswin_init_menu_window(MENU_TYPE_TEXT);
1012             GetNHApp()->windowlist[wid].dead = 0;
1013         }
1014
1015         if (GetNHApp()->windowlist[wid].win != NULL) {
1016             MSNHMsgPutstr data;
1017             ZeroMemory(&data, sizeof(data));
1018             data.attr = attr;
1019             data.text = text;
1020             data.append = app;
1021             SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1022                         (WPARAM) MSNH_MSG_PUTSTR, (LPARAM) &data);
1023         }
1024         /* yield a bit so it gets done immediately */
1025         mswin_get_nh_event();
1026     } else {
1027         // build text to display later in message box
1028         GetNHApp()->saved_text =
1029             realloc(GetNHApp()->saved_text,
1030                     strlen(text) + strlen(GetNHApp()->saved_text) + 1);
1031         strcat(GetNHApp()->saved_text, text);
1032     }
1033 }
1034
1035 /* Display the file named str.  Complain about missing files
1036                    iff complain is TRUE.
1037 */
1038 void
1039 mswin_display_file(const char *filename, BOOLEAN_P must_exist)
1040 {
1041     dlb *f;
1042     TCHAR wbuf[BUFSZ];
1043
1044     logDebug("mswin_display_file(%s, %d)\n", filename, must_exist);
1045
1046     f = dlb_fopen(filename, RDTMODE);
1047     if (!f) {
1048         if (must_exist) {
1049             TCHAR message[90];
1050             _stprintf(message, TEXT("Warning! Could not find file: %s\n"),
1051                       NH_A2W(filename, wbuf, sizeof(wbuf)));
1052             NHMessageBox(GetNHApp()->hMainWnd, message,
1053                          MB_OK | MB_ICONEXCLAMATION);
1054         }
1055     } else {
1056         winid text;
1057         char line[LLEN];
1058
1059         text = mswin_create_nhwindow(NHW_TEXT);
1060
1061         while (dlb_fgets(line, LLEN, f)) {
1062             size_t len;
1063             len = strlen(line);
1064             if (line[len - 1] == '\n')
1065                 line[len - 1] = '\x0';
1066             mswin_putstr(text, ATR_NONE, line);
1067         }
1068         (void) dlb_fclose(f);
1069
1070         mswin_display_nhwindow(text, 1);
1071         mswin_destroy_nhwindow(text);
1072     }
1073 }
1074
1075 /* Start using window as a menu.  You must call start_menu()
1076    before add_menu().  After calling start_menu() you may not
1077    putstr() to the window.  Only windows of type NHW_MENU may
1078    be used for menus.
1079 */
1080 void
1081 mswin_start_menu(winid wid)
1082 {
1083     logDebug("mswin_start_menu(%d)\n", wid);
1084     if ((wid >= 0) && (wid < MAXWINDOWS)) {
1085         if (GetNHApp()->windowlist[wid].win == NULL
1086             && GetNHApp()->windowlist[wid].type == NHW_MENU) {
1087             GetNHApp()->windowlist[wid].win =
1088                 mswin_init_menu_window(MENU_TYPE_MENU);
1089             GetNHApp()->windowlist[wid].dead = 0;
1090         }
1091
1092         if (GetNHApp()->windowlist[wid].win != NULL) {
1093             SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1094                         (WPARAM) MSNH_MSG_STARTMENU, (LPARAM) NULL);
1095         }
1096     }
1097 }
1098
1099 /*
1100 add_menu(windid window, int glyph, const anything identifier,
1101                                 char accelerator, char groupacc,
1102                                 int attr, char *str, boolean preselected)
1103                 -- Add a text line str to the given menu window.  If
1104 identifier
1105                    is 0, then the line cannot be selected (e.g. a title).
1106                    Otherwise, identifier is the value returned if the line is
1107                    selected.  Accelerator is a keyboard key that can be used
1108                    to select the line.  If the accelerator of a selectable
1109                    item is 0, the window system is free to select its own
1110                    accelerator.  It is up to the window-port to make the
1111                    accelerator visible to the user (e.g. put "a - " in front
1112                    of str).  The value attr is the same as in putstr().
1113                    Glyph is an optional glyph to accompany the line.  If
1114                    window port cannot or does not want to display it, this
1115                    is OK.  If there is no glyph applicable, then this
1116                    value will be NO_GLYPH.
1117                 -- All accelerators should be in the range [A-Za-z].
1118                 -- It is expected that callers do not mix accelerator
1119                    choices.  Either all selectable items have an accelerator
1120                    or let the window system pick them.  Don't do both.
1121                 -- Groupacc is a group accelerator.  It may be any character
1122                    outside of the standard accelerator (see above) or a
1123                    number.  If 0, the item is unaffected by any group
1124                    accelerator.  If this accelerator conflicts with
1125                    the menu command (or their user defined aliases), it loses.
1126                    The menu commands and aliases take care not to interfere
1127                    with the default object class symbols.
1128                 -- If you want this choice to be preselected when the
1129                    menu is displayed, set preselected to TRUE.
1130 */
1131 void
1132 mswin_add_menu(winid wid, int glyph, const ANY_P *identifier,
1133                CHAR_P accelerator, CHAR_P group_accel, int attr,
1134                const char *str, BOOLEAN_P presel)
1135 {
1136     logDebug("mswin_add_menu(%d, %d, %p, %c, %c, %d, %s, %d)\n", wid, glyph,
1137              identifier, (char) accelerator, (char) group_accel, attr, str,
1138              presel);
1139     if ((wid >= 0) && (wid < MAXWINDOWS)
1140         && (GetNHApp()->windowlist[wid].win != NULL)) {
1141         MSNHMsgAddMenu data;
1142         ZeroMemory(&data, sizeof(data));
1143         data.glyph = glyph;
1144         data.identifier = identifier;
1145         data.accelerator = accelerator;
1146         data.group_accel = group_accel;
1147         data.attr = attr;
1148         data.str = str;
1149         data.presel = presel;
1150
1151         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1152                     (WPARAM) MSNH_MSG_ADDMENU, (LPARAM) &data);
1153     }
1154 }
1155
1156 /*
1157 end_menu(window, prompt)
1158                 -- Stop adding entries to the menu and flushes the window
1159                    to the screen (brings to front?).  Prompt is a prompt
1160                    to give the user.  If prompt is NULL, no prompt will
1161                    be printed.
1162                 ** This probably shouldn't flush the window any more (if
1163                 ** it ever did).  That should be select_menu's job.  -dean
1164 */
1165 void
1166 mswin_end_menu(winid wid, const char *prompt)
1167 {
1168     logDebug("mswin_end_menu(%d, %s)\n", wid, prompt);
1169     if ((wid >= 0) && (wid < MAXWINDOWS)
1170         && (GetNHApp()->windowlist[wid].win != NULL)) {
1171         MSNHMsgEndMenu data;
1172         ZeroMemory(&data, sizeof(data));
1173         data.text = prompt;
1174
1175         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1176                     (WPARAM) MSNH_MSG_ENDMENU, (LPARAM) &data);
1177     }
1178 }
1179
1180 /*
1181 int select_menu(windid window, int how, menu_item **selected)
1182                 -- Return the number of items selected; 0 if none were chosen,
1183                    -1 when explicitly cancelled.  If items were selected, then
1184                    selected is filled in with an allocated array of menu_item
1185                    structures, one for each selected line.  The caller must
1186                    free this array when done with it.  The "count" field
1187                    of selected is a user supplied count.  If the user did
1188                    not supply a count, then the count field is filled with
1189                    -1 (meaning all).  A count of zero is equivalent to not
1190                    being selected and should not be in the list.  If no items
1191                    were selected, then selected is NULL'ed out.  How is the
1192                    mode of the menu.  Three valid values are PICK_NONE,
1193                    PICK_ONE, and PICK_N, meaning: nothing is selectable,
1194                    only one thing is selectable, and any number valid items
1195                    may selected.  If how is PICK_NONE, this function should
1196                    never return anything but 0 or -1.
1197                 -- You may call select_menu() on a window multiple times --
1198                    the menu is saved until start_menu() or destroy_nhwindow()
1199                    is called on the window.
1200                 -- Note that NHW_MENU windows need not have select_menu()
1201                    called for them. There is no way of knowing whether
1202                    select_menu() will be called for the window at
1203                    create_nhwindow() time.
1204 */
1205 int
1206 mswin_select_menu(winid wid, int how, MENU_ITEM_P **selected)
1207 {
1208     int nReturned = -1;
1209
1210     logDebug("mswin_select_menu(%d, %d)\n", wid, how);
1211
1212     if ((wid >= 0) && (wid < MAXWINDOWS)
1213         && (GetNHApp()->windowlist[wid].win != NULL)) {
1214         ShowWindow(GetNHApp()->windowlist[wid].win, SW_SHOW);
1215         nReturned = mswin_menu_window_select_menu(
1216             GetNHApp()->windowlist[wid].win, how, selected,
1217             !(iflags.perm_invent && wid == WIN_INVEN
1218               && how == PICK_NONE) /* don't activate inventory window if
1219                                       perm_invent is on */
1220             );
1221     }
1222     return nReturned;
1223 }
1224
1225 /*
1226     -- Indicate to the window port that the inventory has been changed.
1227     -- Merely calls display_inventory() for window-ports that leave the
1228         window up, otherwise empty.
1229 */
1230 void
1231 mswin_update_inventory()
1232 {
1233     logDebug("mswin_update_inventory()\n");
1234     if (iflags.perm_invent && program_state.something_worth_saving
1235         && iflags.window_inited && WIN_INVEN != WIN_ERR)
1236         display_inventory(NULL, FALSE);
1237 }
1238
1239 /*
1240 mark_synch()    -- Don't go beyond this point in I/O on any channel until
1241                    all channels are caught up to here.  Can be an empty call
1242                    for the moment
1243 */
1244 void
1245 mswin_mark_synch()
1246 {
1247     logDebug("mswin_mark_synch()\n");
1248 }
1249
1250 /*
1251 wait_synch()    -- Wait until all pending output is complete (*flush*() for
1252                    streams goes here).
1253                 -- May also deal with exposure events etc. so that the
1254                    display is OK when return from wait_synch().
1255 */
1256 void
1257 mswin_wait_synch()
1258 {
1259     logDebug("mswin_wait_synch()\n");
1260     mswin_raw_print_flush();
1261 }
1262
1263 /*
1264 cliparound(x, y)-- Make sure that the user is more-or-less centered on the
1265                    screen if the playing area is larger than the screen.
1266                 -- This function is only defined if CLIPPING is defined.
1267 */
1268 void
1269 mswin_cliparound(int x, int y)
1270 {
1271     winid wid = WIN_MAP;
1272
1273     logDebug("mswin_cliparound(%d, %d)\n", x, y);
1274
1275     if ((wid >= 0) && (wid < MAXWINDOWS)
1276         && (GetNHApp()->windowlist[wid].win != NULL)) {
1277         MSNHMsgClipAround data;
1278         data.x = x;
1279         data.y = y;
1280         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1281                     (WPARAM) MSNH_MSG_CLIPAROUND, (LPARAM) &data);
1282     }
1283 }
1284
1285 /*
1286 print_glyph(window, x, y, glyph, bkglyph)
1287                 -- Print the glyph at (x,y) on the given window.  Glyphs are
1288                    integers at the interface, mapped to whatever the window-
1289                    port wants (symbol, font, color, attributes, ...there's
1290                    a 1-1 map between glyphs and distinct things on the map).
1291                 -- bkglyph is a background glyph for potential use by some
1292                    graphical or tiled environments to allow the depiction
1293                    to fall against a background consistent with the grid 
1294                    around x,y.
1295                    
1296 */
1297 void
1298 mswin_print_glyph(winid wid, XCHAR_P x, XCHAR_P y, int glyph, int bkglyph)
1299 {
1300     logDebug("mswin_print_glyph(%d, %d, %d, %d, %d)\n", wid, x, y, glyph, bkglyph);
1301
1302     if ((wid >= 0) && (wid < MAXWINDOWS)
1303         && (GetNHApp()->windowlist[wid].win != NULL)) {
1304         MSNHMsgPrintGlyph data;
1305
1306         ZeroMemory(&data, sizeof(data));
1307         data.x = x;
1308         data.y = y;
1309         data.glyph = glyph;
1310         data.bkglyph = bkglyph;
1311         SendMessage(GetNHApp()->windowlist[wid].win, WM_MSNH_COMMAND,
1312                     (WPARAM) MSNH_MSG_PRINT_GLYPH, (LPARAM) &data);
1313     }
1314 }
1315
1316 /*
1317  * mswin_raw_print_accumulate() accumulate the given text into
1318  *   raw_print_strbuf.
1319  */
1320 void
1321 mswin_raw_print_accumulate(const char * str, boolean bold)
1322 {
1323     bold; // ignored for now
1324
1325     if (raw_print_strbuf.str != NULL) strbuf_append(&raw_print_strbuf, "\n");
1326     strbuf_append(&raw_print_strbuf, str);
1327 }
1328
1329 /*
1330  * mswin_raw_print_flush() - display any text found in raw_print_strbuf in a
1331  *   dialog box and clear raw_print_strbuf.
1332  */
1333 void
1334 mswin_raw_print_flush()
1335 {
1336     if (raw_print_strbuf.str != NULL) {
1337         int wlen = strlen(raw_print_strbuf.str) + 1;
1338         TCHAR * wbuf = (TCHAR *) alloc(wlen * sizeof(TCHAR));
1339         if (wbuf != NULL) {
1340             NHMessageBox(GetNHApp()->hMainWnd,
1341                             NH_A2W(raw_print_strbuf.str, wbuf, wlen),
1342                             MB_ICONINFORMATION | MB_OK);
1343             free(wbuf);
1344         }
1345         strbuf_empty(&raw_print_strbuf);
1346     }
1347 }
1348
1349
1350 /*
1351 raw_print(str)  -- Print directly to a screen, or otherwise guarantee that
1352                    the user sees str.  raw_print() appends a newline to str.
1353                    It need not recognize ASCII control characters.  This is
1354                    used during startup (before windowing system initialization
1355                    -- maybe this means only error startup messages are raw),
1356                    for error messages, and maybe other "msg" uses.  E.g.
1357                    updating status for micros (i.e, "saving").
1358 */
1359 void
1360 mswin_raw_print(const char *str)
1361 {
1362     logDebug("mswin_raw_print(%s)\n", str);
1363
1364     if (str && *str) {
1365         extern int redirect_stdout;
1366         if (!redirect_stdout)
1367             mswin_raw_print_accumulate(str, FALSE);
1368         else
1369             fprintf(stdout, "%s", str);
1370     }
1371 }
1372
1373 /*
1374 raw_print_bold(str)
1375                 -- Like raw_print(), but prints in bold/standout (if
1376 possible).
1377 */
1378 void
1379 mswin_raw_print_bold(const char *str)
1380 {
1381     logDebug("mswin_raw_print_bold(%s)\n", str);
1382     if (str && *str) {
1383         extern int redirect_stdout;
1384         if (!redirect_stdout)
1385             mswin_raw_print_accumulate(str, TRUE);
1386         else
1387             fprintf(stdout, "%s", str);
1388     }
1389 }
1390
1391 /*
1392 int nhgetch()   -- Returns a single character input from the user.
1393                 -- In the tty window-port, nhgetch() assumes that tgetch()
1394                    will be the routine the OS provides to read a character.
1395                    Returned character _must_ be non-zero.
1396 */
1397 int
1398 mswin_nhgetch()
1399 {
1400     PMSNHEvent event;
1401     int key = 0;
1402
1403     logDebug("mswin_nhgetch()\n");
1404
1405     while ((event = mswin_input_pop()) == NULL || event->type != NHEVENT_CHAR)
1406         mswin_main_loop();
1407
1408     key = event->kbd.ch;
1409     return (key);
1410 }
1411
1412 /*
1413 int nh_poskey(int *x, int *y, int *mod)
1414                 -- Returns a single character input from the user or a
1415                    a positioning event (perhaps from a mouse).  If the
1416                    return value is non-zero, a character was typed, else,
1417                    a position in the MAP window is returned in x, y and mod.
1418                    mod may be one of
1419
1420                         CLICK_1         -- mouse click type 1
1421                         CLICK_2         -- mouse click type 2
1422
1423                    The different click types can map to whatever the
1424                    hardware supports.  If no mouse is supported, this
1425                    routine always returns a non-zero character.
1426 */
1427 int
1428 mswin_nh_poskey(int *x, int *y, int *mod)
1429 {
1430     PMSNHEvent event;
1431     int key;
1432
1433     logDebug("mswin_nh_poskey()\n");
1434
1435     while ((event = mswin_input_pop()) == NULL)
1436         mswin_main_loop();
1437
1438     if (event->type == NHEVENT_MOUSE) {
1439         if (iflags.wc_mouse_support) {
1440             *mod = event->ms.mod;
1441             *x = event->ms.x;
1442             *y = event->ms.y;
1443         }
1444         key = 0;
1445     } else {
1446         key = event->kbd.ch;
1447     }
1448     return (key);
1449 }
1450
1451 /*
1452 nhbell()        -- Beep at user.  [This will exist at least until sounds are
1453                    redone, since sounds aren't attributable to windows
1454 anyway.]
1455 */
1456 void
1457 mswin_nhbell()
1458 {
1459     logDebug("mswin_nhbell()\n");
1460 }
1461
1462 /*
1463 doprev_message()
1464                 -- Display previous messages.  Used by the ^P command.
1465                 -- On the tty-port this scrolls WIN_MESSAGE back one line.
1466 */
1467 int
1468 mswin_doprev_message()
1469 {
1470     logDebug("mswin_doprev_message()\n");
1471     SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_VSCROLL,
1472                 MAKEWPARAM(SB_LINEUP, 0), (LPARAM) NULL);
1473     return 0;
1474 }
1475
1476 /*
1477 char yn_function(const char *ques, const char *choices, char default)
1478                 -- Print a prompt made up of ques, choices and default.
1479                    Read a single character response that is contained in
1480                    choices or default.  If choices is NULL, all possible
1481                    inputs are accepted and returned.  This overrides
1482                    everything else.  The choices are expected to be in
1483                    lower case.  Entering ESC always maps to 'q', or 'n',
1484                    in that order, if present in choices, otherwise it maps
1485                    to default.  Entering any other quit character (SPACE,
1486                    RETURN, NEWLINE) maps to default.
1487                 -- If the choices string contains ESC, then anything after
1488                    it is an acceptable response, but the ESC and whatever
1489                    follows is not included in the prompt.
1490                 -- If the choices string contains a '#' then accept a count.
1491                    Place this value in the global "yn_number" and return '#'.
1492                 -- This uses the top line in the tty window-port, other
1493                    ports might use a popup.
1494 */
1495 char
1496 mswin_yn_function(const char *question, const char *choices, CHAR_P def)
1497 {
1498     char ch;
1499     char yn_esc_map = '\033';
1500     char message[BUFSZ];
1501     char res_ch[2];
1502     int createcaret;
1503     boolean digit_ok, allow_num;
1504
1505     logDebug("mswin_yn_function(%s, %s, %d)\n", question, choices, def);
1506
1507     if (WIN_MESSAGE == WIN_ERR && choices == ynchars) {
1508         char *text =
1509             realloc(strdup(GetNHApp()->saved_text),
1510                     strlen(question) + strlen(GetNHApp()->saved_text) + 1);
1511         DWORD box_result;
1512         strcat(text, question);
1513         box_result =
1514             NHMessageBox(NULL, NH_W2A(text, message, sizeof(message)),
1515                          MB_ICONQUESTION | MB_YESNOCANCEL
1516                              | ((def == 'y') ? MB_DEFBUTTON1
1517                                              : (def == 'n') ? MB_DEFBUTTON2
1518                                                             : MB_DEFBUTTON3));
1519         free(text);
1520         GetNHApp()->saved_text = strdup("");
1521         return box_result == IDYES ? 'y' : box_result == IDNO ? 'n' : '\033';
1522     }
1523
1524     if (choices) {
1525         char *cb, choicebuf[QBUFSZ];
1526
1527         allow_num = (index(choices, '#') != 0);
1528
1529         Strcpy(choicebuf, choices);
1530         if ((cb = index(choicebuf, '\033')) != 0) {
1531             /* anything beyond <esc> is hidden */
1532             *cb = '\0';
1533         }
1534         (void) strncpy(message, question, QBUFSZ - 1);
1535         message[QBUFSZ - 1] = '\0';
1536         sprintf(eos(message), " [%s]", choicebuf);
1537         if (def)
1538             sprintf(eos(message), " (%c)", def);
1539         Strcat(message, " ");
1540         /* escape maps to 'q' or 'n' or default, in that order */
1541         yn_esc_map =
1542             (index(choices, 'q') ? 'q' : (index(choices, 'n') ? 'n' : def));
1543     } else {
1544         Strcpy(message, question);
1545         Strcat(message, " ");
1546     }
1547
1548     createcaret = 1;
1549     SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1550                 (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1551
1552     mswin_clear_nhwindow(WIN_MESSAGE);
1553     mswin_putstr(WIN_MESSAGE, ATR_BOLD, message);
1554
1555     /* Only here if main window is not present */
1556     ch = 0;
1557     do {
1558         ShowCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1559         ch = mswin_nhgetch();
1560         HideCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1561         if (choices)
1562             ch = lowc(ch);
1563         else
1564             break; /* If choices is NULL, all possible inputs are accepted and
1565                       returned. */
1566
1567         digit_ok = allow_num && digit(ch);
1568         if (ch == '\033') {
1569             if (index(choices, 'q'))
1570                 ch = 'q';
1571             else if (index(choices, 'n'))
1572                 ch = 'n';
1573             else
1574                 ch = def;
1575             break;
1576         } else if (index(quitchars, ch)) {
1577             ch = def;
1578             break;
1579         } else if (!index(choices, ch) && !digit_ok) {
1580             mswin_nhbell();
1581             ch = (char) 0;
1582             /* and try again... */
1583         } else if (ch == '#' || digit_ok) {
1584             char z, digit_string[2];
1585             int n_len = 0;
1586             long value = 0;
1587             mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, ("#"), 1);
1588             n_len++;
1589             digit_string[1] = '\0';
1590             if (ch != '#') {
1591                 digit_string[0] = ch;
1592                 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, digit_string, 1);
1593                 n_len++;
1594                 value = ch - '0';
1595                 ch = '#';
1596             }
1597             do { /* loop until we get a non-digit */
1598                 z = lowc(readchar());
1599                 if (digit(z)) {
1600                     value = (10 * value) + (z - '0');
1601                     if (value < 0)
1602                         break; /* overflow: try again */
1603                     digit_string[0] = z;
1604                     mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, digit_string, 1);
1605                     n_len++;
1606                 } else if (z == 'y' || index(quitchars, z)) {
1607                     if (z == '\033')
1608                         value = -1; /* abort */
1609                     z = '\n';       /* break */
1610                 } else if (z == '\b') {
1611                     if (n_len <= 1) {
1612                         value = -1;
1613                         break;
1614                     } else {
1615                         value /= 10;
1616                         mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, digit_string,
1617                                         -1);
1618                         n_len--;
1619                     }
1620                 } else {
1621                     value = -1; /* abort */
1622                     mswin_nhbell();
1623                     break;
1624                 }
1625             } while (z != '\n');
1626             if (value > 0)
1627                 yn_number = value;
1628             else if (value == 0)
1629                 ch = 'n'; /* 0 => "no" */
1630             else {        /* remove number from top line, then try again */
1631                 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, digit_string, -n_len);
1632                 n_len = 0;
1633                 ch = (char) 0;
1634             }
1635         }
1636     } while (!ch);
1637
1638     createcaret = 0;
1639     SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1640                 (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1641
1642     /* display selection in the message window */
1643     if (isprint((uchar) ch) && ch != '#') {
1644         res_ch[0] = ch;
1645         res_ch[1] = '\x0';
1646         mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, res_ch, 1);
1647     }
1648
1649     return ch;
1650 }
1651
1652 /*
1653 getlin(const char *ques, char *input)
1654             -- Prints ques as a prompt and reads a single line of text,
1655                up to a newline.  The string entered is returned without the
1656                newline.  ESC is used to cancel, in which case the string
1657                "\033\000" is returned.
1658             -- getlin() must call flush_screen(1) before doing anything.
1659             -- This uses the top line in the tty window-port, other
1660                ports might use a popup.
1661 */
1662 void
1663 mswin_getlin(const char *question, char *input)
1664 {
1665     logDebug("mswin_getlin(%s, %p)\n", question, input);
1666
1667     if (!iflags.wc_popup_dialog) {
1668 #if 0 /*JP*/
1669         char c;
1670 #else
1671         int c;
1672 #endif
1673         int len;
1674         int done;
1675         int createcaret;
1676
1677         createcaret = 1;
1678         SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1679                     (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1680
1681         /* mswin_clear_nhwindow(WIN_MESSAGE); */
1682         mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, question, 0);
1683         mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, " ", 1);
1684 #ifdef EDIT_GETLIN
1685         mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, input, 0);
1686         len = strlen(input);
1687 #else
1688         input[0] = '\0';
1689         len = 0;
1690 #endif
1691         ShowCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1692         done = FALSE;
1693         while (!done) {
1694             c = mswin_nhgetch();
1695             switch (c) {
1696             case VK_ESCAPE:
1697                 strcpy(input, "\033");
1698                 done = TRUE;
1699                 break;
1700             case '\n':
1701             case '\r':
1702             case -115:
1703                 done = TRUE;
1704                 break;
1705             default:
1706                 if (input[0])
1707                     mswin_putstr_ex(WIN_MESSAGE, ATR_NONE, input, -len);
1708                 if (c == VK_BACK) {
1709                     if (len > 0)
1710                         len--;
1711 #if 1 /*JP*//*2\83o\83C\83g\95\8e\9a\82È\82ç\82à\82¤1\83o\83C\83g\8fÁ\82·*/
1712                     if (len > 0 && is_kanji2(input, len))
1713                         len--;
1714 #endif
1715                     input[len] = '\0';
1716                 } else if (len>=(BUFSZ-1)) {
1717                     PlaySound((LPCSTR)SND_ALIAS_SYSTEMEXCLAMATION, NULL, SND_ALIAS_ID|SND_ASYNC);
1718                 } else {
1719                     input[len++] = c;
1720 #if 1 /*JP*//*2\83o\83C\83g\95\8e\9a\82È\82ç\82»\82Ì\8fê\82Å\82à\82¤1\83o\83C\83g\93Ç\82Ý\8d\9e\82Þ*/
1721                     if (is_kanji(c)){
1722                         c = mswin_nhgetch();
1723                         input[len++] = c;
1724                     }
1725 #endif
1726                     input[len] = '\0';
1727                 }
1728                 mswin_putstr_ex(WIN_MESSAGE, ATR_NONE, input, 1);
1729                 break;
1730             }
1731         }
1732         HideCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1733         createcaret = 0;
1734         SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1735                     (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1736     } else {
1737         if (mswin_getlin_window(question, input, BUFSZ) == IDCANCEL) {
1738             strcpy(input, "\033");
1739         }
1740     }
1741 }
1742
1743 /*
1744 int get_ext_cmd(void)
1745             -- Get an extended command in a window-port specific way.
1746                An index into extcmdlist[] is returned on a successful
1747                selection, -1 otherwise.
1748 */
1749 int
1750 mswin_get_ext_cmd()
1751 {
1752     int ret;
1753     logDebug("mswin_get_ext_cmd()\n");
1754
1755     if (!iflags.wc_popup_dialog) {
1756         char c;
1757         char cmd[BUFSZ];
1758         int i, len;
1759         int createcaret;
1760
1761         createcaret = 1;
1762         SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1763                     (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1764
1765         cmd[0] = '\0';
1766         i = -2;
1767         mswin_clear_nhwindow(WIN_MESSAGE);
1768         mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, "#", 0);
1769         len = 0;
1770         ShowCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1771         while (i == -2) {
1772             int oindex, com_index;
1773             c = mswin_nhgetch();
1774             switch (c) {
1775             case VK_ESCAPE:
1776                 i = -1;
1777                 break;
1778             case '\n':
1779             case '\r':
1780             case -115:
1781                 for (i = 0; extcmdlist[i].ef_txt != (char *) 0; i++)
1782                     if (!strcmpi(cmd, extcmdlist[i].ef_txt))
1783                         break;
1784
1785                 if (extcmdlist[i].ef_txt == (char *) 0) {
1786                     pline("%s: unknown extended command.", cmd);
1787                     i = -1;
1788                 }
1789                 break;
1790             default:
1791                 if (cmd[0])
1792                     mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, cmd,
1793                                     -(int) strlen(cmd));
1794                 if (c == VK_BACK) {
1795                     if (len > 0)
1796                         len--;
1797                     cmd[len] = '\0';
1798                 } else {
1799                     cmd[len++] = c;
1800                     cmd[len] = '\0';
1801                     /* Find a command with this prefix in extcmdlist */
1802                     com_index = -1;
1803                     for (oindex = 0; extcmdlist[oindex].ef_txt != (char *) 0;
1804                          oindex++) {
1805                         if ((extcmdlist[oindex].flags & AUTOCOMPLETE)
1806                             && !(!wizard && (extcmdlist[oindex].flags & WIZMODECMD))
1807                             && !strncmpi(cmd, extcmdlist[oindex].ef_txt, len)) {
1808                             if (com_index == -1) /* no matches yet */
1809                                 com_index = oindex;
1810                             else
1811                                 com_index =
1812                                     -2; /* two matches, don't complete */
1813                         }
1814                     }
1815                     if (com_index >= 0) {
1816                         Strcpy(cmd, extcmdlist[com_index].ef_txt);
1817                     }
1818                 }
1819                 mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, cmd, 1);
1820                 break;
1821             }
1822         }
1823         HideCaret(mswin_hwnd_from_winid(WIN_MESSAGE));
1824         createcaret = 0;
1825         SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1826                     (WPARAM) MSNH_MSG_CARET, (LPARAM) &createcaret);
1827         return i;
1828     } else {
1829         if (mswin_ext_cmd_window(&ret) == IDCANCEL)
1830             return -1;
1831         else
1832             return ret;
1833     }
1834 }
1835
1836 /*
1837 number_pad(state)
1838             -- Initialize the number pad to the given state.
1839 */
1840 void
1841 mswin_number_pad(int state)
1842 {
1843     /* Do Nothing */
1844     logDebug("mswin_number_pad(%d)\n", state);
1845 }
1846
1847 /*
1848 delay_output()  -- Causes a visible delay of 50ms in the output.
1849                Conceptually, this is similar to wait_synch() followed
1850                by a nap(50ms), but allows asynchronous operation.
1851 */
1852 void
1853 mswin_delay_output()
1854 {
1855     logDebug("mswin_delay_output()\n");
1856     mswin_map_update(mswin_hwnd_from_winid(WIN_MAP));
1857     Sleep(50);
1858 }
1859
1860 void
1861 mswin_change_color()
1862 {
1863     logDebug("mswin_change_color()\n");
1864 }
1865
1866 char *
1867 mswin_get_color_string()
1868 {
1869     logDebug("mswin_get_color_string()\n");
1870     return ("");
1871 }
1872
1873 /*
1874 start_screen()  -- Only used on Unix tty ports, but must be declared for
1875                completeness.  Sets up the tty to work in full-screen
1876                graphics mode.  Look at win/tty/termcap.c for an
1877                example.  If your window-port does not need this function
1878                just declare an empty function.
1879 */
1880 void
1881 mswin_start_screen()
1882 {
1883     /* Do Nothing */
1884     logDebug("mswin_start_screen()\n");
1885 }
1886
1887 /*
1888 end_screen()    -- Only used on Unix tty ports, but must be declared for
1889                completeness.  The complement of start_screen().
1890 */
1891 void
1892 mswin_end_screen()
1893 {
1894     /* Do Nothing */
1895     logDebug("mswin_end_screen()\n");
1896 }
1897
1898 /*
1899 outrip(winid, int, when)
1900             -- The tombstone code.  If you want the traditional code use
1901                genl_outrip for the value and check the #if in rip.c.
1902 */
1903 #define STONE_LINE_LEN 16
1904 void
1905 mswin_outrip(winid wid, int how, time_t when)
1906 {
1907     char buf[BUFSZ];
1908     long year;
1909
1910     logDebug("mswin_outrip(%d, %d, %ld)\n", wid, how, (long) when);
1911     if ((wid >= 0) && (wid < MAXWINDOWS)) {
1912         DestroyWindow(GetNHApp()->windowlist[wid].win);
1913         GetNHApp()->windowlist[wid].win = mswin_init_RIP_window();
1914         GetNHApp()->windowlist[wid].type = NHW_RIP;
1915         GetNHApp()->windowlist[wid].dead = 0;
1916     }
1917
1918     /* Put name on stone */
1919     Sprintf(buf, "%s", plname);
1920     buf[STONE_LINE_LEN] = 0;
1921     putstr(wid, 0, buf);
1922
1923     /* Put $ on stone */
1924     Sprintf(buf, "%ld Au", done_money);
1925     buf[STONE_LINE_LEN] = 0; /* It could be a *lot* of gold :-) */
1926     putstr(wid, 0, buf);
1927
1928     /* Put together death description */
1929     formatkiller(buf, sizeof buf, how, FALSE);
1930
1931     /* Put death type on stone */
1932     putstr(wid, 0, buf);
1933
1934     /* Put year on stone */
1935     year = yyyymmdd(when) / 10000L;
1936     Sprintf(buf, "%4ld", year);
1937     putstr(wid, 0, buf);
1938     mswin_finish_rip_text(wid);
1939 }
1940
1941 /* handle options updates here */
1942 void
1943 mswin_preference_update(const char *pref)
1944 {
1945     if (stricmp(pref, "font_menu") == 0
1946         || stricmp(pref, "font_size_menu") == 0) {
1947         if (iflags.wc_fontsiz_menu < NHFONT_SIZE_MIN
1948             || iflags.wc_fontsiz_menu > NHFONT_SIZE_MAX)
1949             iflags.wc_fontsiz_menu = NHFONT_DEFAULT_SIZE;
1950
1951         HDC hdc = GetDC(GetNHApp()->hMainWnd);
1952         mswin_get_font(NHW_MENU, ATR_NONE, hdc, TRUE);
1953         mswin_get_font(NHW_MENU, ATR_BOLD, hdc, TRUE);
1954         mswin_get_font(NHW_MENU, ATR_DIM, hdc, TRUE);
1955         mswin_get_font(NHW_MENU, ATR_ULINE, hdc, TRUE);
1956         mswin_get_font(NHW_MENU, ATR_BLINK, hdc, TRUE);
1957         mswin_get_font(NHW_MENU, ATR_INVERSE, hdc, TRUE);
1958         ReleaseDC(GetNHApp()->hMainWnd, hdc);
1959
1960         mswin_layout_main_window(NULL);
1961         return;
1962     }
1963
1964     if (stricmp(pref, "font_status") == 0
1965         || stricmp(pref, "font_size_status") == 0) {
1966         if (iflags.wc_fontsiz_status < NHFONT_SIZE_MIN
1967             || iflags.wc_fontsiz_status > NHFONT_SIZE_MAX)
1968             iflags.wc_fontsiz_status = NHFONT_DEFAULT_SIZE;
1969
1970         HDC hdc = GetDC(GetNHApp()->hMainWnd);
1971         mswin_get_font(NHW_STATUS, ATR_NONE, hdc, TRUE);
1972         mswin_get_font(NHW_STATUS, ATR_BOLD, hdc, TRUE);
1973         mswin_get_font(NHW_STATUS, ATR_DIM, hdc, TRUE);
1974         mswin_get_font(NHW_STATUS, ATR_ULINE, hdc, TRUE);
1975         mswin_get_font(NHW_STATUS, ATR_BLINK, hdc, TRUE);
1976         mswin_get_font(NHW_STATUS, ATR_INVERSE, hdc, TRUE);
1977         ReleaseDC(GetNHApp()->hMainWnd, hdc);
1978
1979         for (int i = 1; i < MAXWINDOWS; i++) {
1980             if (GetNHApp()->windowlist[i].type == NHW_STATUS
1981                 && GetNHApp()->windowlist[i].win != NULL) {
1982                 InvalidateRect(GetNHApp()->windowlist[i].win, NULL, TRUE);
1983             }
1984         }
1985         mswin_layout_main_window(NULL);
1986         return;
1987     }
1988
1989     if (stricmp(pref, "font_message") == 0
1990         || stricmp(pref, "font_size_message") == 0) {
1991         if (iflags.wc_fontsiz_message < NHFONT_SIZE_MIN
1992             || iflags.wc_fontsiz_message > NHFONT_SIZE_MAX)
1993             iflags.wc_fontsiz_message = NHFONT_DEFAULT_SIZE;
1994
1995         HDC hdc = GetDC(GetNHApp()->hMainWnd);
1996         mswin_get_font(NHW_MESSAGE, ATR_NONE, hdc, TRUE);
1997         mswin_get_font(NHW_MESSAGE, ATR_BOLD, hdc, TRUE);
1998         mswin_get_font(NHW_MESSAGE, ATR_DIM, hdc, TRUE);
1999         mswin_get_font(NHW_MESSAGE, ATR_ULINE, hdc, TRUE);
2000         mswin_get_font(NHW_MESSAGE, ATR_BLINK, hdc, TRUE);
2001         mswin_get_font(NHW_MESSAGE, ATR_INVERSE, hdc, TRUE);
2002         ReleaseDC(GetNHApp()->hMainWnd, hdc);
2003
2004         InvalidateRect(mswin_hwnd_from_winid(WIN_MESSAGE), NULL, TRUE);
2005         mswin_layout_main_window(NULL);
2006         return;
2007     }
2008
2009     if (stricmp(pref, "font_text") == 0
2010         || stricmp(pref, "font_size_text") == 0) {
2011         if (iflags.wc_fontsiz_text < NHFONT_SIZE_MIN
2012             || iflags.wc_fontsiz_text > NHFONT_SIZE_MAX)
2013             iflags.wc_fontsiz_text = NHFONT_DEFAULT_SIZE;
2014
2015         HDC hdc = GetDC(GetNHApp()->hMainWnd);
2016         mswin_get_font(NHW_TEXT, ATR_NONE, hdc, TRUE);
2017         mswin_get_font(NHW_TEXT, ATR_BOLD, hdc, TRUE);
2018         mswin_get_font(NHW_TEXT, ATR_DIM, hdc, TRUE);
2019         mswin_get_font(NHW_TEXT, ATR_ULINE, hdc, TRUE);
2020         mswin_get_font(NHW_TEXT, ATR_BLINK, hdc, TRUE);
2021         mswin_get_font(NHW_TEXT, ATR_INVERSE, hdc, TRUE);
2022         ReleaseDC(GetNHApp()->hMainWnd, hdc);
2023
2024         mswin_layout_main_window(NULL);
2025         return;
2026     }
2027
2028     if (stricmp(pref, "scroll_amount") == 0) {
2029         mswin_cliparound(u.ux, u.uy);
2030         return;
2031     }
2032
2033     if (stricmp(pref, "scroll_margin") == 0) {
2034         mswin_cliparound(u.ux, u.uy);
2035         return;
2036     }
2037
2038     if (stricmp(pref, "map_mode") == 0) {
2039         mswin_select_map_mode(iflags.wc_map_mode);
2040         return;
2041     }
2042
2043     if (stricmp(pref, "hilite_pet") == 0) {
2044         InvalidateRect(mswin_hwnd_from_winid(WIN_MAP), NULL, TRUE);
2045         return;
2046     }
2047
2048     if (stricmp(pref, "align_message") == 0
2049         || stricmp(pref, "align_status") == 0) {
2050         mswin_layout_main_window(NULL);
2051         return;
2052     }
2053
2054     if (stricmp(pref, "vary_msgcount") == 0) {
2055         InvalidateRect(mswin_hwnd_from_winid(WIN_MESSAGE), NULL, TRUE);
2056         mswin_layout_main_window(NULL);
2057         return;
2058     }
2059
2060     if (stricmp(pref, "perm_invent") == 0) {
2061         mswin_update_inventory();
2062         return;
2063     }
2064 }
2065
2066 #define TEXT_BUFFER_SIZE 4096
2067 char *
2068 mswin_getmsghistory(BOOLEAN_P init)
2069 {
2070     static PMSNHMsgGetText text = 0;
2071     static char *next_message = 0;
2072
2073     if (init) {
2074         text = (PMSNHMsgGetText) malloc(sizeof(MSNHMsgGetText)
2075                                         + TEXT_BUFFER_SIZE);
2076         text->max_size =
2077             TEXT_BUFFER_SIZE
2078             - 1; /* make sure we always have 0 at the end of the buffer */
2079
2080         ZeroMemory(text->buffer, TEXT_BUFFER_SIZE);
2081         SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
2082                     (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text);
2083
2084         next_message = text->buffer;
2085     }
2086
2087     if (!(next_message && next_message[0])) {
2088         free(text);
2089         next_message = 0;
2090         return (char *) 0;
2091     } else {
2092         char *retval = next_message;
2093         char *p;
2094         next_message = p = strchr(next_message, '\n');
2095         if (next_message)
2096             next_message++;
2097         if (p)
2098             while (p >= retval && isspace((uchar) *p))
2099                 *p-- = (char) 0; /* delete trailing whitespace */
2100         return retval;
2101     }
2102 }
2103
2104 void
2105 mswin_putmsghistory(const char *msg, BOOLEAN_P restoring)
2106 {
2107     BOOL save_sound_opt;
2108
2109     UNREFERENCED_PARAMETER(restoring);
2110
2111     if (!msg)
2112         return; /* end of message history restore */
2113     save_sound_opt = GetNHApp()->bNoSounds;
2114     GetNHApp()->bNoSounds =
2115         TRUE; /* disable sounds while restoring message history */
2116     mswin_putstr_ex(WIN_MESSAGE, ATR_NONE, msg, 0);
2117     clear_nhwindow(WIN_MESSAGE); /* it is in fact end-of-turn indication so
2118                                     each message will print on the new line */
2119     GetNHApp()->bNoSounds = save_sound_opt; /* restore sounds option */
2120 }
2121
2122 void
2123 mswin_main_loop()
2124 {
2125     while (!mswin_have_input()) {
2126         MSG msg;
2127
2128         mswin_map_update(mswin_hwnd_from_winid(WIN_MAP));
2129
2130         if (!iflags.debug_fuzzer || PeekMessage(&msg, NULL, 0, 0, FALSE)) {
2131             if(GetMessage(&msg, NULL, 0, 0) != 0) {
2132                 if (GetNHApp()->regNetHackMode
2133                     || !TranslateAccelerator(msg.hwnd, GetNHApp()->hAccelTable,
2134                                              &msg)) {
2135                     TranslateMessage(&msg);
2136                     DispatchMessage(&msg);
2137                 }
2138             } else {
2139                 /* WM_QUIT */
2140                 break;
2141             }
2142         } else {
2143             nhassert(iflags.debug_fuzzer);
2144             PostMessage(GetNHApp()->hMainWnd, WM_MSNH_COMMAND,
2145                         MSNH_MSG_RANDOM_INPUT, 0);
2146         }
2147     }
2148 }
2149
2150 /* clean up and quit */
2151 void
2152 bail(const char *mesg)
2153 {
2154     clearlocks();
2155     mswin_exit_nhwindows(mesg);
2156     nh_terminate(EXIT_SUCCESS);
2157     /*NOTREACHED*/
2158 }
2159
2160 BOOL
2161 initMapTiles(void)
2162 {
2163     HBITMAP hBmp;
2164     BITMAP bm;
2165     TCHAR wbuf[MAX_PATH];
2166     DWORD errcode;
2167     int tl_num;
2168     SIZE map_size;
2169     extern int total_tiles_used;
2170
2171     /* no file - no tile */
2172     if (!(iflags.wc_tile_file && *iflags.wc_tile_file))
2173         return TRUE;
2174
2175     /* load bitmap */
2176     hBmp = LoadImage(GetNHApp()->hApp,
2177                      NH_A2W(iflags.wc_tile_file, wbuf, MAX_PATH),
2178                      IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
2179     if (hBmp == NULL) {
2180         char errmsg[BUFSZ];
2181
2182         errcode = GetLastError();
2183         Sprintf(errmsg, "%s (0x%x).",
2184             "Cannot load tiles from the file. Reverting back to default",
2185             errcode);
2186         raw_print(errmsg);
2187         return FALSE;
2188     }
2189
2190     /* calculate tile dimensions */
2191     GetObject(hBmp, sizeof(BITMAP), (LPVOID) &bm);
2192     if (bm.bmWidth % iflags.wc_tile_width
2193         || bm.bmHeight % iflags.wc_tile_height) {
2194         DeleteObject(hBmp);
2195         raw_print("Tiles bitmap does not match tile_width and tile_height "
2196                   "options. Reverting back to default.");
2197         return FALSE;
2198     }
2199
2200     tl_num = (bm.bmWidth / iflags.wc_tile_width)
2201              * (bm.bmHeight / iflags.wc_tile_height);
2202     if (tl_num < total_tiles_used) {
2203         DeleteObject(hBmp);
2204         raw_print("Number of tiles in the bitmap is less than required by "
2205                   "the game. Reverting back to default.");
2206         return FALSE;
2207     }
2208
2209     /* set the tile information */
2210     if (GetNHApp()->bmpMapTiles != GetNHApp()->bmpTiles) {
2211         DeleteObject(GetNHApp()->bmpMapTiles);
2212     }
2213
2214     GetNHApp()->bmpMapTiles = hBmp;
2215     GetNHApp()->mapTile_X = iflags.wc_tile_width;
2216     GetNHApp()->mapTile_Y = iflags.wc_tile_height;
2217     GetNHApp()->mapTilesPerLine = bm.bmWidth / iflags.wc_tile_width;
2218
2219     map_size.cx = GetNHApp()->mapTile_X * COLNO;
2220     map_size.cy = GetNHApp()->mapTile_Y * ROWNO;
2221     mswin_map_layout(mswin_hwnd_from_winid(WIN_MAP), &map_size);
2222     return TRUE;
2223 }
2224
2225 void
2226 mswin_popup_display(HWND hWnd, int *done_indicator)
2227 {
2228     MSG msg;
2229     HWND hChild;
2230     HMENU hMenu;
2231     int mi_count;
2232     int i;
2233
2234     /* activate the menu window */
2235     GetNHApp()->hPopupWnd = hWnd;
2236
2237     mswin_layout_main_window(hWnd);
2238
2239     /* disable game windows */
2240     for (hChild = GetWindow(GetNHApp()->hMainWnd, GW_CHILD); hChild;
2241          hChild = GetWindow(hChild, GW_HWNDNEXT)) {
2242         if (hChild != hWnd)
2243             EnableWindow(hChild, FALSE);
2244     }
2245
2246     /* disable menu */
2247     hMenu = GetMenu(GetNHApp()->hMainWnd);
2248     mi_count = GetMenuItemCount(hMenu);
2249     for (i = 0; i < mi_count; i++) {
2250         EnableMenuItem(hMenu, i, MF_BYPOSITION | MF_GRAYED);
2251     }
2252     DrawMenuBar(GetNHApp()->hMainWnd);
2253
2254     /* bring menu window on top */
2255     SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0,
2256                  SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
2257     SetFocus(hWnd);
2258
2259     /* go into message loop */
2260     while (IsWindow(hWnd) && (done_indicator == NULL || !*done_indicator)) {
2261         if (!iflags.debug_fuzzer || PeekMessage(&msg, NULL, 0, 0, FALSE)) {
2262             if(GetMessage(&msg, NULL, 0, 0) != 0) {
2263                 if (msg.message == WM_MSNH_COMMAND ||
2264                     !IsDialogMessage(hWnd, &msg)) {
2265                     if (!TranslateAccelerator(msg.hwnd,
2266                                               GetNHApp()->hAccelTable, &msg)) {
2267                         TranslateMessage(&msg);
2268                         DispatchMessage(&msg);
2269                     }
2270                 }
2271             } else {
2272                 /* WM_QUIT */
2273                 break;
2274             }
2275         } else {
2276             nhassert(iflags.debug_fuzzer);
2277             PostMessage(hWnd, WM_MSNH_COMMAND, MSNH_MSG_RANDOM_INPUT, 0);
2278         }
2279     }
2280 }
2281
2282 void
2283 mswin_popup_destroy(HWND hWnd)
2284 {
2285     HWND hChild;
2286     HMENU hMenu;
2287     int mi_count;
2288     int i;
2289
2290     /* enable game windows */
2291     for (hChild = GetWindow(GetNHApp()->hMainWnd, GW_CHILD); hChild;
2292          hChild = GetWindow(hChild, GW_HWNDNEXT)) {
2293         if (hChild != hWnd) {
2294             EnableWindow(hChild, TRUE);
2295         }
2296     }
2297
2298     /* enable menu */
2299     hMenu = GetMenu(GetNHApp()->hMainWnd);
2300     mi_count = GetMenuItemCount(hMenu);
2301     for (i = 0; i < mi_count; i++) {
2302         EnableMenuItem(hMenu, i, MF_BYPOSITION | MF_ENABLED);
2303     }
2304     DrawMenuBar(GetNHApp()->hMainWnd);
2305
2306     /* Don't hide the permanent inventory window ... leave it showing */
2307     if (!iflags.perm_invent || mswin_winid_from_handle(hWnd) != WIN_INVEN)
2308         ShowWindow(hWnd, SW_HIDE);
2309
2310     GetNHApp()->hPopupWnd = NULL;
2311
2312     mswin_layout_main_window(hWnd);
2313
2314     SetFocus(GetNHApp()->hMainWnd);
2315 }
2316
2317 #ifdef DEBUG
2318 # ifdef _DEBUG
2319 #include <stdarg.h>
2320
2321 void
2322 logDebug(const char *fmt, ...)
2323 {
2324     va_list args;
2325
2326     if (!showdebug(NHTRACE_LOG) || !_s_debugfp)
2327         return;
2328
2329     va_start(args, fmt);
2330     vfprintf(_s_debugfp, fmt, args);
2331     va_end(args);
2332     fflush(_s_debugfp);
2333 }
2334 # endif
2335 #endif
2336
2337 /* Reading and writing settings from the registry. */
2338 #define CATEGORYKEY "Software"
2339 #define COMPANYKEY "NetHack"
2340 #define PRODUCTKEY "NetHack 3.6.6"
2341 #define SETTINGSKEY "Settings"
2342 #define MAINSHOWSTATEKEY "MainShowState"
2343 #define MAINMINXKEY "MainMinX"
2344 #define MAINMINYKEY "MainMinY"
2345 #define MAINMAXXKEY "MainMaxX"
2346 #define MAINMAXYKEY "MainMaxY"
2347 #define MAINLEFTKEY "MainLeft"
2348 #define MAINRIGHTKEY "MainRight"
2349 #define MAINTOPKEY "MainTop"
2350 #define MAINBOTTOMKEY "MainBottom"
2351 #define MAINAUTOLAYOUT "AutoLayout"
2352 #define MAPLEFT "MapLeft"
2353 #define MAPRIGHT "MapRight"
2354 #define MAPTOP "MapTop"
2355 #define MAPBOTTOM "MapBottom"
2356 #define MSGLEFT "MsgLeft"
2357 #define MSGRIGHT "MsgRight"
2358 #define MSGTOP "MsgTop"
2359 #define MSGBOTTOM "MsgBottom"
2360 #define STATUSLEFT "StatusLeft"
2361 #define STATUSRIGHT "StatusRight"
2362 #define STATUSTOP "StatusTop"
2363 #define STATUSBOTTOM "StatusBottom"
2364 #define MENULEFT "MenuLeft"
2365 #define MENURIGHT "MenuRight"
2366 #define MENUTOP "MenuTop"
2367 #define MENUBOTTOM "MenuBottom"
2368 #define TEXTLEFT "TextLeft"
2369 #define TEXTRIGHT "TextRight"
2370 #define TEXTTOP "TextTop"
2371 #define TEXTBOTTOM "TextBottom"
2372 #define INVENTLEFT "InventLeft"
2373 #define INVENTRIGHT "InventRight"
2374 #define INVENTTOP "InventTop"
2375 #define INVENTBOTTOM "InventBottom"
2376
2377 /* #define all the subkeys here */
2378 #define INTFKEY "Interface"
2379
2380 void
2381 mswin_read_reg()
2382 {
2383     HKEY key;
2384     DWORD size;
2385     DWORD safe_buf;
2386     char keystring[MAX_PATH];
2387     int i;
2388     COLORREF default_mapcolors[CLR_MAX] = {
2389         RGB(0x55, 0x55, 0x55), /* CLR_BLACK */
2390         RGB(0xFF, 0x00, 0x00), /* CLR_RED */
2391         RGB(0x00, 0x80, 0x00), /* CLR_GREEN */
2392         RGB(0xA5, 0x2A, 0x2A), /* CLR_BROWN */
2393         RGB(0x00, 0x00, 0xFF), /* CLR_BLUE */
2394         RGB(0xFF, 0x00, 0xFF), /* CLR_MAGENTA */
2395         RGB(0x00, 0xFF, 0xFF), /* CLR_CYAN */
2396         RGB(0xC0, 0xC0, 0xC0), /* CLR_GRAY */
2397         RGB(0xFF, 0xFF, 0xFF), /* NO_COLOR */
2398         RGB(0xFF, 0xA5, 0x00), /* CLR_ORANGE */
2399         RGB(0x00, 0xFF, 0x00), /* CLR_BRIGHT_GREEN */
2400         RGB(0xFF, 0xFF, 0x00), /* CLR_YELLOW */
2401         RGB(0x00, 0xC0, 0xFF), /* CLR_BRIGHT_BLUE */
2402         RGB(0xFF, 0x80, 0xFF), /* CLR_BRIGHT_MAGENTA */
2403         RGB(0x80, 0xFF, 0xFF), /* CLR_BRIGHT_CYAN */
2404         RGB(0xFF, 0xFF, 0xFF)  /* CLR_WHITE */
2405     };
2406
2407     sprintf(keystring, "%s\\%s\\%s\\%s", CATEGORYKEY, COMPANYKEY, PRODUCTKEY,
2408             SETTINGSKEY);
2409
2410     /* Set the defaults here. The very first time the app is started, nothing
2411        is
2412        read from the registry, so these defaults apply. */
2413     GetNHApp()->saveRegistrySettings = 1; /* Normally, we always save */
2414     GetNHApp()->regNetHackMode = TRUE;
2415
2416     for (i = 0; i < CLR_MAX; i++)
2417         GetNHApp()->regMapColors[i] = default_mapcolors[i];
2418
2419     if (RegOpenKeyEx(HKEY_CURRENT_USER, keystring, 0, KEY_READ, &key)
2420         != ERROR_SUCCESS)
2421         return;
2422
2423     size = sizeof(DWORD);
2424
2425 #define NHGETREG_DWORD(name, val)                                       \
2426     RegQueryValueEx(key, (name), 0, NULL, (unsigned char *)(&safe_buf), \
2427                     &size);                                             \
2428     (val) = safe_buf;
2429
2430     /* read the keys here */
2431     NHGETREG_DWORD(INTFKEY, GetNHApp()->regNetHackMode);
2432
2433     /* read window placement */
2434     NHGETREG_DWORD(MAINSHOWSTATEKEY, GetNHApp()->regMainShowState);
2435     NHGETREG_DWORD(MAINMINXKEY, GetNHApp()->regMainMinX);
2436     NHGETREG_DWORD(MAINMINYKEY, GetNHApp()->regMainMinY);
2437     NHGETREG_DWORD(MAINMAXXKEY, GetNHApp()->regMainMaxX);
2438     NHGETREG_DWORD(MAINMAXYKEY, GetNHApp()->regMainMaxY);
2439     NHGETREG_DWORD(MAINLEFTKEY, GetNHApp()->regMainLeft);
2440     NHGETREG_DWORD(MAINRIGHTKEY, GetNHApp()->regMainRight);
2441     NHGETREG_DWORD(MAINTOPKEY, GetNHApp()->regMainTop);
2442     NHGETREG_DWORD(MAINBOTTOMKEY, GetNHApp()->regMainBottom);
2443
2444     NHGETREG_DWORD(MAINAUTOLAYOUT, GetNHApp()->bAutoLayout);
2445     NHGETREG_DWORD(MAPLEFT, GetNHApp()->rtMapWindow.left);
2446     NHGETREG_DWORD(MAPRIGHT, GetNHApp()->rtMapWindow.right);
2447     NHGETREG_DWORD(MAPTOP, GetNHApp()->rtMapWindow.top);
2448     NHGETREG_DWORD(MAPBOTTOM, GetNHApp()->rtMapWindow.bottom);
2449     NHGETREG_DWORD(MSGLEFT, GetNHApp()->rtMsgWindow.left);
2450     NHGETREG_DWORD(MSGRIGHT, GetNHApp()->rtMsgWindow.right);
2451     NHGETREG_DWORD(MSGTOP, GetNHApp()->rtMsgWindow.top);
2452     NHGETREG_DWORD(MSGBOTTOM, GetNHApp()->rtMsgWindow.bottom);
2453     NHGETREG_DWORD(STATUSLEFT, GetNHApp()->rtStatusWindow.left);
2454     NHGETREG_DWORD(STATUSRIGHT, GetNHApp()->rtStatusWindow.right);
2455     NHGETREG_DWORD(STATUSTOP, GetNHApp()->rtStatusWindow.top);
2456     NHGETREG_DWORD(STATUSBOTTOM, GetNHApp()->rtStatusWindow.bottom);
2457     NHGETREG_DWORD(MENULEFT, GetNHApp()->rtMenuWindow.left);
2458     NHGETREG_DWORD(MENURIGHT, GetNHApp()->rtMenuWindow.right);
2459     NHGETREG_DWORD(MENUTOP, GetNHApp()->rtMenuWindow.top);
2460     NHGETREG_DWORD(MENUBOTTOM, GetNHApp()->rtMenuWindow.bottom);
2461     NHGETREG_DWORD(TEXTLEFT, GetNHApp()->rtTextWindow.left);
2462     NHGETREG_DWORD(TEXTRIGHT, GetNHApp()->rtTextWindow.right);
2463     NHGETREG_DWORD(TEXTTOP, GetNHApp()->rtTextWindow.top);
2464     NHGETREG_DWORD(TEXTBOTTOM, GetNHApp()->rtTextWindow.bottom);
2465     NHGETREG_DWORD(INVENTLEFT, GetNHApp()->rtInvenWindow.left);
2466     NHGETREG_DWORD(INVENTRIGHT, GetNHApp()->rtInvenWindow.right);
2467     NHGETREG_DWORD(INVENTTOP, GetNHApp()->rtInvenWindow.top);
2468     NHGETREG_DWORD(INVENTBOTTOM, GetNHApp()->rtInvenWindow.bottom);
2469 #undef NHGETREG_DWORD
2470
2471     for (i = 0; i < CLR_MAX; i++) {
2472         COLORREF cl;
2473         char mapcolorkey[64];
2474         sprintf(mapcolorkey, "MapColor%02d", i);
2475         if (RegQueryValueEx(key, mapcolorkey, NULL, NULL, (BYTE *)&cl, &size) == ERROR_SUCCESS)
2476             GetNHApp()->regMapColors[i] = cl;
2477     }
2478
2479     RegCloseKey(key);
2480
2481     /* check the data for validity */
2482     if (IsRectEmpty(&GetNHApp()->rtMapWindow)
2483         || IsRectEmpty(&GetNHApp()->rtMsgWindow)
2484         || IsRectEmpty(&GetNHApp()->rtStatusWindow)
2485         || IsRectEmpty(&GetNHApp()->rtMenuWindow)
2486         || IsRectEmpty(&GetNHApp()->rtTextWindow)
2487         || IsRectEmpty(&GetNHApp()->rtInvenWindow)) {
2488         GetNHApp()->bAutoLayout = TRUE;
2489     }
2490 }
2491
2492 void
2493 mswin_write_reg()
2494 {
2495     HKEY key;
2496     DWORD disposition;
2497     int i;
2498
2499     if (GetNHApp()->saveRegistrySettings) {
2500         char keystring[MAX_PATH];
2501         DWORD safe_buf;
2502
2503         sprintf(keystring, "%s\\%s\\%s\\%s", CATEGORYKEY, COMPANYKEY,
2504                 PRODUCTKEY, SETTINGSKEY);
2505
2506         if (RegOpenKeyEx(HKEY_CURRENT_USER, keystring, 0, KEY_WRITE, &key)
2507             != ERROR_SUCCESS) {
2508             RegCreateKeyEx(HKEY_CURRENT_USER, keystring, 0, "",
2509                            REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
2510                            &key, &disposition);
2511         }
2512
2513 #define NHSETREG_DWORD(name, val)                                   \
2514     RegSetValueEx(key, (name), 0, REG_DWORD,                        \
2515                   (unsigned char *)((safe_buf = (val)), &safe_buf), \
2516                   sizeof(DWORD));
2517
2518         /* Write the keys here */
2519         NHSETREG_DWORD(INTFKEY, GetNHApp()->regNetHackMode);
2520
2521         /* Main window placement */
2522         NHSETREG_DWORD(MAINSHOWSTATEKEY, GetNHApp()->regMainShowState);
2523         NHSETREG_DWORD(MAINMINXKEY, GetNHApp()->regMainMinX);
2524         NHSETREG_DWORD(MAINMINYKEY, GetNHApp()->regMainMinY);
2525         NHSETREG_DWORD(MAINMAXXKEY, GetNHApp()->regMainMaxX);
2526         NHSETREG_DWORD(MAINMAXYKEY, GetNHApp()->regMainMaxY);
2527         NHSETREG_DWORD(MAINLEFTKEY, GetNHApp()->regMainLeft);
2528         NHSETREG_DWORD(MAINRIGHTKEY, GetNHApp()->regMainRight);
2529         NHSETREG_DWORD(MAINTOPKEY, GetNHApp()->regMainTop);
2530         NHSETREG_DWORD(MAINBOTTOMKEY, GetNHApp()->regMainBottom);
2531
2532         NHSETREG_DWORD(MAINAUTOLAYOUT, GetNHApp()->bAutoLayout);
2533         NHSETREG_DWORD(MAPLEFT, GetNHApp()->rtMapWindow.left);
2534         NHSETREG_DWORD(MAPRIGHT, GetNHApp()->rtMapWindow.right);
2535         NHSETREG_DWORD(MAPTOP, GetNHApp()->rtMapWindow.top);
2536         NHSETREG_DWORD(MAPBOTTOM, GetNHApp()->rtMapWindow.bottom);
2537         NHSETREG_DWORD(MSGLEFT, GetNHApp()->rtMsgWindow.left);
2538         NHSETREG_DWORD(MSGRIGHT, GetNHApp()->rtMsgWindow.right);
2539         NHSETREG_DWORD(MSGTOP, GetNHApp()->rtMsgWindow.top);
2540         NHSETREG_DWORD(MSGBOTTOM, GetNHApp()->rtMsgWindow.bottom);
2541         NHSETREG_DWORD(STATUSLEFT, GetNHApp()->rtStatusWindow.left);
2542         NHSETREG_DWORD(STATUSRIGHT, GetNHApp()->rtStatusWindow.right);
2543         NHSETREG_DWORD(STATUSTOP, GetNHApp()->rtStatusWindow.top);
2544         NHSETREG_DWORD(STATUSBOTTOM, GetNHApp()->rtStatusWindow.bottom);
2545         NHSETREG_DWORD(MENULEFT, GetNHApp()->rtMenuWindow.left);
2546         NHSETREG_DWORD(MENURIGHT, GetNHApp()->rtMenuWindow.right);
2547         NHSETREG_DWORD(MENUTOP, GetNHApp()->rtMenuWindow.top);
2548         NHSETREG_DWORD(MENUBOTTOM, GetNHApp()->rtMenuWindow.bottom);
2549         NHSETREG_DWORD(TEXTLEFT, GetNHApp()->rtTextWindow.left);
2550         NHSETREG_DWORD(TEXTRIGHT, GetNHApp()->rtTextWindow.right);
2551         NHSETREG_DWORD(TEXTTOP, GetNHApp()->rtTextWindow.top);
2552         NHSETREG_DWORD(TEXTBOTTOM, GetNHApp()->rtTextWindow.bottom);
2553         NHSETREG_DWORD(INVENTLEFT, GetNHApp()->rtInvenWindow.left);
2554         NHSETREG_DWORD(INVENTRIGHT, GetNHApp()->rtInvenWindow.right);
2555         NHSETREG_DWORD(INVENTTOP, GetNHApp()->rtInvenWindow.top);
2556         NHSETREG_DWORD(INVENTBOTTOM, GetNHApp()->rtInvenWindow.bottom);
2557 #undef NHSETREG_DWORD
2558
2559         for (i = 0; i < CLR_MAX; i++) {
2560             COLORREF cl = GetNHApp()->regMapColors[i];
2561             char mapcolorkey[64];
2562             sprintf(mapcolorkey, "MapColor%02d", i);
2563             RegSetValueEx(key, mapcolorkey, 0, REG_DWORD, (BYTE *)&cl, sizeof(DWORD));
2564         }
2565
2566         RegCloseKey(key);
2567     }
2568 }
2569
2570 void
2571 mswin_destroy_reg()
2572 {
2573     char keystring[MAX_PATH];
2574     HKEY key;
2575     DWORD nrsubkeys;
2576
2577     /* Delete keys one by one, as NT does not delete trees */
2578     sprintf(keystring, "%s\\%s\\%s\\%s", CATEGORYKEY, COMPANYKEY, PRODUCTKEY,
2579             SETTINGSKEY);
2580     RegDeleteKey(HKEY_CURRENT_USER, keystring);
2581     sprintf(keystring, "%s\\%s\\%s", CATEGORYKEY, COMPANYKEY, PRODUCTKEY);
2582     RegDeleteKey(HKEY_CURRENT_USER, keystring);
2583     /* The company key will also contain information about newer versions
2584        of nethack (e.g. a subkey called NetHack 4.0), so only delete that
2585        if it's empty now. */
2586     sprintf(keystring, "%s\\%s", CATEGORYKEY, COMPANYKEY);
2587     /* If we cannot open it, we probably cannot delete it either... Just
2588        go on and see what happens. */
2589     RegOpenKeyEx(HKEY_CURRENT_USER, keystring, 0, KEY_READ, &key);
2590     nrsubkeys = 0;
2591     RegQueryInfoKey(key, NULL, NULL, NULL, &nrsubkeys, NULL, NULL, NULL, NULL,
2592                     NULL, NULL, NULL);
2593     RegCloseKey(key);
2594     if (nrsubkeys == 0)
2595         RegDeleteKey(HKEY_CURRENT_USER, keystring);
2596
2597     /* Prevent saving on exit */
2598     GetNHApp()->saveRegistrySettings = 0;
2599 }
2600
2601 typedef struct ctv {
2602     const char *colorstring;
2603     COLORREF colorvalue;
2604 } color_table_value;
2605
2606 /*
2607  * The color list here is a combination of:
2608  * NetHack colors.  (See mhmap.c)
2609  * HTML colors. (See http://www.w3.org/TR/REC-html40/types.html#h-6.5 )
2610  */
2611
2612 static color_table_value color_table[] = {
2613     /* NetHack colors */
2614     { "black", RGB(0x55, 0x55, 0x55) },
2615     { "red", RGB(0xFF, 0x00, 0x00) },
2616     { "green", RGB(0x00, 0x80, 0x00) },
2617     { "brown", RGB(0xA5, 0x2A, 0x2A) },
2618     { "blue", RGB(0x00, 0x00, 0xFF) },
2619     { "magenta", RGB(0xFF, 0x00, 0xFF) },
2620     { "cyan", RGB(0x00, 0xFF, 0xFF) },
2621     { "orange", RGB(0xFF, 0xA5, 0x00) },
2622     { "brightgreen", RGB(0x00, 0xFF, 0x00) },
2623     { "yellow", RGB(0xFF, 0xFF, 0x00) },
2624     { "brightblue", RGB(0x00, 0xC0, 0xFF) },
2625     { "brightmagenta", RGB(0xFF, 0x80, 0xFF) },
2626     { "brightcyan", RGB(0x80, 0xFF, 0xFF) },
2627     { "white", RGB(0xFF, 0xFF, 0xFF) },
2628     /* Remaining HTML colors */
2629     { "trueblack", RGB(0x00, 0x00, 0x00) },
2630     { "gray", RGB(0x80, 0x80, 0x80) },
2631     { "grey", RGB(0x80, 0x80, 0x80) },
2632     { "purple", RGB(0x80, 0x00, 0x80) },
2633     { "silver", RGB(0xC0, 0xC0, 0xC0) },
2634     { "maroon", RGB(0x80, 0x00, 0x00) },
2635     { "fuchsia", RGB(0xFF, 0x00, 0xFF) }, /* = NetHack magenta */
2636     { "lime", RGB(0x00, 0xFF, 0x00) },    /* = NetHack bright green */
2637     { "olive", RGB(0x80, 0x80, 0x00) },
2638     { "navy", RGB(0x00, 0x00, 0x80) },
2639     { "teal", RGB(0x00, 0x80, 0x80) },
2640     { "aqua", RGB(0x00, 0xFF, 0xFF) }, /* = NetHack cyan */
2641     { "", RGB(0x00, 0x00, 0x00) },
2642 };
2643
2644 typedef struct ctbv {
2645     char *colorstring;
2646     int syscolorvalue;
2647 } color_table_brush_value;
2648
2649 static color_table_brush_value color_table_brush[] = {
2650     { "activeborder", COLOR_ACTIVEBORDER },
2651     { "activecaption", COLOR_ACTIVECAPTION },
2652     { "appworkspace", COLOR_APPWORKSPACE },
2653     { "background", COLOR_BACKGROUND },
2654     { "btnface", COLOR_BTNFACE },
2655     { "btnshadow", COLOR_BTNSHADOW },
2656     { "btntext", COLOR_BTNTEXT },
2657     { "captiontext", COLOR_CAPTIONTEXT },
2658     { "graytext", COLOR_GRAYTEXT },
2659     { "greytext", COLOR_GRAYTEXT },
2660     { "highlight", COLOR_HIGHLIGHT },
2661     { "highlighttext", COLOR_HIGHLIGHTTEXT },
2662     { "inactiveborder", COLOR_INACTIVEBORDER },
2663     { "inactivecaption", COLOR_INACTIVECAPTION },
2664     { "menu", COLOR_MENU },
2665     { "menutext", COLOR_MENUTEXT },
2666     { "scrollbar", COLOR_SCROLLBAR },
2667     { "window", COLOR_WINDOW },
2668     { "windowframe", COLOR_WINDOWFRAME },
2669     { "windowtext", COLOR_WINDOWTEXT },
2670     { "", -1 },
2671 };
2672
2673 static void
2674 mswin_color_from_string(char *colorstring, HBRUSH *brushptr,
2675                         COLORREF *colorptr)
2676 {
2677     color_table_value *ctv_ptr = color_table;
2678     color_table_brush_value *ctbv_ptr = color_table_brush;
2679     int red_value, blue_value, green_value;
2680     static char *hexadecimals = "0123456789abcdef";
2681
2682     if (colorstring == NULL)
2683         return;
2684     if (*colorstring == '#') {
2685         if (strlen(++colorstring) != 6)
2686             return;
2687
2688         red_value = (int) (index(hexadecimals, tolower((uchar) *colorstring))
2689                            - hexadecimals);
2690         ++colorstring;
2691         red_value *= 16;
2692         red_value += (int) (index(hexadecimals, tolower((uchar) *colorstring))
2693                             - hexadecimals);
2694         ++colorstring;
2695
2696         green_value = (int) (index(hexadecimals,
2697                                    tolower((uchar) *colorstring))
2698                              - hexadecimals);
2699         ++colorstring;
2700         green_value *= 16;
2701         green_value += (int) (index(hexadecimals,
2702                                     tolower((uchar) *colorstring))
2703                               - hexadecimals);
2704         ++colorstring;
2705
2706         blue_value = (int) (index(hexadecimals, tolower((uchar) *colorstring))
2707                             - hexadecimals);
2708         ++colorstring;
2709         blue_value *= 16;
2710         blue_value += (int) (index(hexadecimals,
2711                                    tolower((uchar) *colorstring))
2712                              - hexadecimals);
2713         ++colorstring;
2714
2715         *colorptr = RGB(red_value, green_value, blue_value);
2716     } else {
2717         while (*ctv_ptr->colorstring
2718                && stricmp(ctv_ptr->colorstring, colorstring))
2719             ++ctv_ptr;
2720         if (*ctv_ptr->colorstring) {
2721             *colorptr = ctv_ptr->colorvalue;
2722         } else {
2723             while (*ctbv_ptr->colorstring
2724                    && stricmp(ctbv_ptr->colorstring, colorstring))
2725                 ++ctbv_ptr;
2726             if (*ctbv_ptr->colorstring) {
2727                 *brushptr = SYSCLR_TO_BRUSH(ctbv_ptr->syscolorvalue);
2728                 *colorptr = GetSysColor(ctbv_ptr->syscolorvalue);
2729             }
2730         }
2731     }
2732     if (max_brush > TOTAL_BRUSHES)
2733         panic("Too many colors!");
2734     *brushptr = CreateSolidBrush(*colorptr);
2735     brush_table[max_brush++] = *brushptr;
2736 }
2737
2738 void
2739 mswin_get_window_placement(int type, LPRECT rt)
2740 {
2741     switch (type) {
2742     case NHW_MAP:
2743         *rt = GetNHApp()->rtMapWindow;
2744         break;
2745
2746     case NHW_MESSAGE:
2747         *rt = GetNHApp()->rtMsgWindow;
2748         break;
2749
2750     case NHW_STATUS:
2751         *rt = GetNHApp()->rtStatusWindow;
2752         break;
2753
2754     case NHW_MENU:
2755         *rt = GetNHApp()->rtMenuWindow;
2756         break;
2757
2758     case NHW_TEXT:
2759         *rt = GetNHApp()->rtTextWindow;
2760         break;
2761
2762     case NHW_INVEN:
2763         *rt = GetNHApp()->rtInvenWindow;
2764         break;
2765
2766     default:
2767         SetRect(rt, 0, 0, 0, 0);
2768         break;
2769     }
2770 }
2771
2772 void
2773 mswin_update_window_placement(int type, LPRECT rt)
2774 {
2775     LPRECT rt_conf = NULL;
2776
2777     switch (type) {
2778     case NHW_MAP:
2779         rt_conf = &GetNHApp()->rtMapWindow;
2780         break;
2781
2782     case NHW_MESSAGE:
2783         rt_conf = &GetNHApp()->rtMsgWindow;
2784         break;
2785
2786     case NHW_STATUS:
2787         rt_conf = &GetNHApp()->rtStatusWindow;
2788         break;
2789
2790     case NHW_MENU:
2791         rt_conf = &GetNHApp()->rtMenuWindow;
2792         break;
2793
2794     case NHW_TEXT:
2795         rt_conf = &GetNHApp()->rtTextWindow;
2796         break;
2797
2798     case NHW_INVEN:
2799         rt_conf = &GetNHApp()->rtInvenWindow;
2800         break;
2801     }
2802
2803     if (rt_conf && !IsRectEmpty(rt) && !EqualRect(rt_conf, rt)) {
2804         *rt_conf = *rt;
2805     }
2806 }
2807
2808 int
2809 NHMessageBox(HWND hWnd, LPCTSTR text, UINT type)
2810 {
2811     TCHAR title[MAX_LOADSTRING];
2812
2813     LoadString(GetNHApp()->hApp, IDS_APP_TITLE_SHORT, title, MAX_LOADSTRING);
2814
2815     return MessageBox(hWnd, text, title, type);
2816 }
2817
2818 static mswin_status_lines _status_lines;
2819 static mswin_status_string _status_strings[MAXBLSTATS];
2820 static mswin_status_string _condition_strings[BL_MASK_BITS];
2821 static mswin_status_field _status_fields[MAXBLSTATS];
2822
2823 static mswin_condition_field _condition_fields[BL_MASK_BITS] = {
2824     { BL_MASK_STONE, "Stone" },
2825     { BL_MASK_SLIME, "Slime" },
2826     { BL_MASK_STRNGL, "Strngl" },
2827     { BL_MASK_FOODPOIS, "FoodPois" },
2828     { BL_MASK_TERMILL, "TermIll" },
2829     { BL_MASK_BLIND, "Blind" },
2830     { BL_MASK_DEAF, "Deaf" },
2831     { BL_MASK_STUN, "Stun" },
2832     { BL_MASK_CONF, "Conf" },
2833     { BL_MASK_HALLU, "Hallu" },
2834     { BL_MASK_LEV, "Lev" },
2835     { BL_MASK_FLY, "Fly" },
2836     { BL_MASK_RIDE, "Ride" }
2837 };
2838
2839
2840 extern winid WIN_STATUS;
2841
2842 #ifdef STATUS_HILITES
2843 typedef struct hilite_data_struct {
2844     int thresholdtype;
2845     anything value;
2846     int behavior;
2847     int under;
2848     int over;
2849 } hilite_data_t;
2850 static hilite_data_t _status_hilites[MAXBLSTATS];
2851 #endif /* STATUS_HILITES */
2852 /*
2853 status_init()   -- core calls this to notify the window port that a status
2854                    display is required. The window port should perform
2855                    the necessary initialization in here, allocate memory, etc.
2856 */
2857 void
2858 mswin_status_init(void)
2859 {
2860     logDebug("mswin_status_init()\n");
2861
2862     for (int i = 0; i < SIZE(_status_fields); i++) {
2863         mswin_status_field * status_field = &_status_fields[i];
2864         status_field->field_index = i;
2865         status_field->enabled = FALSE;
2866     }
2867
2868     for (int i = 0; i < SIZE(_condition_fields); i++) {
2869         mswin_condition_field * condition_field = &_condition_fields[i];
2870         nhassert(condition_field->mask == (1 << i));
2871         condition_field->bit_position = i;
2872     }
2873
2874     for (int i = 0; i < SIZE(_status_strings); i++) {
2875         mswin_status_string * status_string = &_status_strings[i];
2876         status_string->str = NULL;
2877     }
2878
2879     for (int i = 0; i < SIZE(_condition_strings); i++) {
2880         mswin_status_string * status_string = &_condition_strings[i];
2881         status_string->str = NULL;
2882     }
2883
2884     for (int lineIndex = 0; lineIndex < SIZE(_status_lines.lines); lineIndex++) {
2885         mswin_status_line * line = &_status_lines.lines[lineIndex];
2886
2887         mswin_status_fields * status_fields = &line->status_fields;
2888         status_fields->count = 0;
2889
2890         mswin_status_strings * status_strings = &line->status_strings;
2891         status_strings->count = 0;
2892
2893         for (int i = 0; i < fieldcounts[lineIndex]; i++) {
2894             int field_index = fieldorders[lineIndex][i];
2895             nhassert(field_index <= SIZE(_status_fields));
2896
2897             nhassert(status_fields->count <= SIZE(status_fields->status_fields));
2898             status_fields->status_fields[status_fields->count++] = &_status_fields[field_index];
2899
2900             nhassert(status_strings->count <= SIZE(status_strings->status_strings));
2901             status_strings->status_strings[status_strings->count++] =
2902                 &_status_strings[field_index];
2903
2904             if (field_index == BL_CONDITION) {
2905                 for (int j = 0; j < BL_MASK_BITS; j++) {
2906                     nhassert(status_strings->count <= SIZE(status_strings->status_strings));
2907                     status_strings->status_strings[status_strings->count++] =
2908                         &_condition_strings[j];
2909                 }
2910             }
2911         }
2912     }
2913
2914
2915     for (int i = 0; i < MAXBLSTATS; ++i) {
2916 #ifdef STATUS_HILITES
2917         _status_hilites[i].thresholdtype = 0;
2918         _status_hilites[i].behavior = BL_TH_NONE;
2919         _status_hilites[i].under = BL_HILITE_NONE;
2920         _status_hilites[i].over = BL_HILITE_NONE;
2921 #endif /* STATUS_HILITES */
2922     }
2923     /* Use a window for the genl version; backward port compatibility */
2924     WIN_STATUS = create_nhwindow(NHW_STATUS);
2925     display_nhwindow(WIN_STATUS, FALSE);
2926 }
2927
2928 /*
2929 status_finish() -- called when it is time for the window port to tear down
2930                    the status display and free allocated memory, etc.
2931 */
2932 void
2933 mswin_status_finish(void)
2934 {
2935     logDebug("mswin_status_finish()\n");
2936 }
2937
2938 /*
2939 status_enablefield(int fldindex, char fldname, char fieldfmt, boolean enable)
2940                 -- notifies the window port which fields it is authorized to
2941                    display.
2942                 -- This may be called at any time, and is used
2943                    to disable as well as enable fields, depending on the
2944                    value of the final argument (TRUE = enable).
2945                 -- fldindex could be one of the following from botl.h:
2946                    BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
2947                    BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
2948                    BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
2949                    BL_LEVELDESC, BL_EXP, BL_CONDITION
2950                 -- There are MAXBLSTATS status fields (from botl.h)
2951 */
2952 void
2953 mswin_status_enablefield(int fieldidx, const char *nm, const char *fmt,
2954                          boolean enable)
2955 {
2956     logDebug("mswin_status_enablefield(%d, %s, %s, %d)\n", fieldidx, nm, fmt,
2957              (int) enable);
2958
2959     nhassert(fieldidx <= SIZE(_status_fields));
2960     mswin_status_field * field = &_status_fields[fieldidx];
2961
2962     nhassert(fieldidx <= SIZE(_status_strings));
2963     mswin_status_string * string = &_status_strings[fieldidx];
2964
2965     if (field != NULL) {
2966         field->format = fmt;
2967         field->space_in_front = (fmt[0] == ' ');
2968         if (field->space_in_front) field->format++;
2969         field->name = nm;
2970         field->enabled = enable;
2971
2972         string->str = (field->enabled ? field->string : NULL);
2973         string->space_in_front = field->space_in_front;
2974
2975         if (field->field_index == BL_CONDITION)
2976             string->str = NULL;
2977
2978         string->draw_bar = (field->enabled && field->field_index == BL_TITLE);
2979     }
2980 }
2981
2982 /* TODO: turn this into a commmon helper; multiple identical implementations */
2983 static int
2984 mswin_condcolor(bm, bmarray)
2985 long bm;
2986 unsigned long *bmarray;
2987 {
2988     int i;
2989
2990     if (bm && bmarray)
2991         for (i = 0; i < CLR_MAX; ++i) {
2992             if ((bm & bmarray[i]) != 0)
2993                 return i;
2994         }
2995     return NO_COLOR;
2996 }
2997
2998 static int
2999 mswin_condattr(bm, bmarray)
3000 long bm;
3001 unsigned long *bmarray;
3002 {
3003     if (bm && bmarray) {
3004         if (bm & bmarray[HL_ATTCLR_DIM]) return HL_DIM;
3005         if (bm & bmarray[HL_ATTCLR_BLINK]) return HL_BLINK;
3006         if (bm & bmarray[HL_ATTCLR_ULINE]) return HL_ULINE;
3007         if (bm & bmarray[HL_ATTCLR_INVERSE]) return HL_INVERSE;
3008         if (bm & bmarray[HL_ATTCLR_BOLD]) return HL_BOLD;
3009     }
3010
3011     return HL_NONE;
3012 }
3013
3014 /*
3015
3016 status_update(int fldindex, genericptr_t ptr, int chg, int percent, int color, unsigned long *colormasks)
3017                 -- update the value of a status field.
3018                 -- the fldindex identifies which field is changing and
3019                    is an integer index value from botl.h
3020                 -- fldindex could be any one of the following from botl.h:
3021                    BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
3022                    BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
3023                    BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
3024                    BL_LEVELDESC, BL_EXP, BL_CONDITION
3025                 -- fldindex could also be BL_FLUSH, which is not really
3026                    a field index, but is a special trigger to tell the 
3027                    windowport that it should output all changes received
3028                    to this point. It marks the end of a bot() cycle.
3029                 -- fldindex could also be BL_RESET, which is not really
3030                    a field index, but is a special advisory to to tell the 
3031                    windowport that it should redisplay all its status fields,
3032                    even if no changes have been presented to it.
3033                 -- ptr is usually a "char *", unless fldindex is BL_CONDITION.
3034                    If fldindex is BL_CONDITION, then ptr is a long value with
3035                    any or none of the following bits set (from botl.h):
3036                         BL_MASK_STONE           0x00000001L
3037                         BL_MASK_SLIME           0x00000002L
3038                         BL_MASK_STRNGL          0x00000004L
3039                         BL_MASK_FOODPOIS        0x00000008L
3040                         BL_MASK_TERMILL         0x00000010L
3041                         BL_MASK_BLIND           0x00000020L
3042                         BL_MASK_DEAF            0x00000040L
3043                         BL_MASK_STUN            0x00000080L
3044                         BL_MASK_CONF            0x00000100L
3045                         BL_MASK_HALLU           0x00000200L
3046                         BL_MASK_LEV             0x00000400L
3047                         BL_MASK_FLY             0x00000800L
3048                         BL_MASK_RIDE            0x00001000L
3049                 -- The value passed for BL_GOLD includes an encoded leading
3050                    symbol for GOLD "\GXXXXNNNN:nnn". If window port needs
3051                    textual gold amount without the leading "$:" the port will
3052                    have to skip past ':' in passed "ptr" for the BL_GOLD case.
3053                 -- color is the color that the NetHack core is telling you to
3054                    use to display the text.
3055                 -- condmasks is a pointer to a set of BL_ATTCLR_MAX unsigned
3056                    longs telling which conditions should be displayed in each
3057                    color and attriubte.
3058 */
3059 void
3060 mswin_status_update(int idx, genericptr_t ptr, int chg, int percent, int color, unsigned long *condmasks)
3061 {
3062     long cond, *condptr = (long *) ptr;
3063     char *text = (char *) ptr;
3064     MSNHMsgUpdateStatus update_cmd_data;
3065     int ocolor, ochar;
3066     unsigned ospecial;
3067
3068     logDebug("mswin_status_update(%d, %p, %d, %d, %x, %p)\n", idx, ptr, chg, percent, color, condmasks);
3069
3070     if (idx >= 0) {
3071
3072         nhassert(idx <= SIZE(_status_fields));
3073         mswin_status_field * status_field = &_status_fields[idx];
3074         nhassert(status_field->field_index == idx);
3075
3076         nhassert(idx <= SIZE(_status_strings));
3077         mswin_status_string * status_string = &_status_strings[idx];
3078
3079         if (!status_field->enabled) {
3080             nhassert(status_string->str == NULL);
3081             return;
3082         }
3083
3084         status_field->color = status_string->color = color & 0xff;
3085         status_field->attribute = status_string->attribute = (color >> 8) & 0xff;
3086
3087         switch (idx) {
3088         case BL_CONDITION: {
3089             mswin_condition_field * condition_field = _condition_fields;
3090
3091             nhassert(status_string->str == NULL);
3092
3093             cond = *condptr;
3094
3095             for (int i = 0; i < BL_MASK_BITS; i++, condition_field++) {
3096                 status_string = &_condition_strings[i];
3097
3098                 if (condition_field->mask & cond) {
3099                     status_string->str = condition_field->name;
3100                     status_string->space_in_front = TRUE;
3101                     status_string->color = mswin_condcolor(condition_field->mask, condmasks);
3102                     status_string->attribute = mswin_condattr(condition_field->mask, condmasks);
3103                 }
3104                 else
3105                     status_string->str = NULL;
3106             }
3107         } break;
3108         case BL_GOLD: {
3109             char buf[BUFSZ];
3110             char *p;
3111
3112             ZeroMemory(buf, sizeof(buf));
3113             if (iflags.invis_goldsym)
3114                 ochar = GOLD_SYM;
3115             else
3116                 mapglyph(objnum_to_glyph(GOLD_PIECE),
3117                          &ochar, &ocolor, &ospecial, 0, 0, 0);
3118             buf[0] = ochar;
3119             p = strchr(text, ':');
3120             if (p) {
3121                 strncpy(buf + 1, p, sizeof(buf) - 2);
3122             } else {
3123                 buf[1] = ':';
3124                 strncpy(buf + 2, text, sizeof(buf) - 3);
3125             }
3126             buf[sizeof buf - 1] = '\0';
3127             Sprintf(status_field->string,
3128                     status_field->format ? status_field->format : "%s", buf);
3129             nhassert(status_string->str == status_field->string);
3130         } break;
3131         default: {
3132             Sprintf(status_field->string,
3133                     status_field->format ? status_field->format : "%s", text);
3134             nhassert(status_string->str == status_field->string);
3135         } break;
3136         }
3137
3138         /* if we received an update for the hp field, we must update the
3139          * bar percent and bar color for the title string */
3140         if (idx == BL_HP) {
3141             mswin_status_string * title_string = &_status_strings[BL_TITLE];
3142
3143             title_string->bar_color = color & 0xff;
3144             title_string->bar_attribute = (color >> 8) & 0xff;
3145             title_string->bar_percent = percent;
3146
3147         }
3148
3149     }
3150
3151     if (idx == BL_FLUSH || idx == BL_RESET) {
3152         /* send command to status window to update */
3153         ZeroMemory(&update_cmd_data, sizeof(update_cmd_data));
3154         update_cmd_data.status_lines = &_status_lines;
3155         SendMessage(mswin_hwnd_from_winid(WIN_STATUS), WM_MSNH_COMMAND,
3156             (WPARAM)MSNH_MSG_UPDATE_STATUS, (LPARAM)&update_cmd_data);
3157     }
3158 }
3159