OSDN Git Service

Merge pull request #1000 from habu1010/feature/build-test-without-using-precompiled...
[hengbandforosx/hengbandosx.git] / src / main-win.cpp
1 /*!
2  * @file main-win.cpp
3  * @brief Windows版固有実装(メインエントリポイント含む)
4  * @date 2018/03/16
5  * @author Hengband Team
6  * @details
7  *
8  * <h3>概要</h3>
9  * Windows98かその前後の頃を起点としたAPI実装。
10  * 各種のゲームエンジンは無論、
11  * DirectXといった昨今描画に標準的となったライブラリも用いていない。
12  * タイルの描画処理などについては、現在動作の詳細を検証中。
13  *
14  * <h3>フォーク元の概要</h3>
15  * <p>
16  * Copyright (c) 1997 Ben Harrison, Skirmantas Kligys, and others
17  *
18  * This software may be copied and distributed for educational, research,
19  * and not for profit purposes provided that this copyright and statement
20  * are included in all such copies.
21  * </p>
22  * <p>
23  * This file helps Angband work with Windows computers.
24  *
25  * To use this file, use an appropriate "Makefile" or "Project File",
26  * make sure that "WINDOWS" and/or "WIN32" are defined somewhere, and
27  * make sure to obtain various extra files as described below.
28  *
29  * The official compilation uses the CodeWarrior Pro compiler, which
30  * includes a special project file and precompilable header file.
31  * </p>
32  *
33  * <p>
34  * The "lib/user/pref-win.prf" file contains keymaps, macro definitions,
35  * and/or color redefinitions.
36  * </p>
37  *
38  * <p>
39  * The "lib/user/font-win.prf" contains attr/char mappings for wall.bmp.
40  * </p>
41  *
42  * <p>
43  * The "lib/user/graf-win.prf" contains attr/char mappings for use with the
44  * special "lib/xtra/graf/*.bmp" bitmap files, which are activated by a menu
45  * item.
46  * </p>
47  *
48  * <p>
49  * Compiling this file, and using the resulting executable, requires
50  * several extra files not distributed with the standard Angband code.
51  * All of these extra files can be found in the "ext-win" archive.
52  * </p>
53  *
54  * <p>
55  * The "term_xtra_win_clear()" function should probably do a low-level
56  * clear of the current window, and redraw the borders and other things,
57  * if only for efficiency.
58  * </p>
59  *
60  * <p>
61  * A simpler method is needed for selecting the "tile size" for windows.
62  * </p>
63  *
64  * <p>
65  * The various "warning" messages assume the existance of the "screen.w"
66  * window, I think, and only a few calls actually check for its existance,
67  * this may be okay since "NULL" means "on top of all windows". (?)  The
68  * user must never be allowed to "hide" the main window, or the "menubar"
69  * will disappear.
70  * </p>
71  *
72  * <p>
73  * Initial framework (and most code) by Ben Harrison (benh@phial.com).
74  *
75  * Original code by Skirmantas Kligys (kligys@scf.usc.edu).
76  *
77  * Additional code by Ross E Becker (beckerr@cis.ohio-state.edu),
78  * and Chris R. Martin (crm7479@tam2000.tamu.edu).
79  * </p>
80  */
81
82 #ifdef WINDOWS
83
84 #include "cmd-io/cmd-save.h"
85 #include "cmd-visual/cmd-draw.h"
86 #include "core/game-play.h"
87 #include "core/player-processor.h"
88 #include "core/scores.h"
89 #include "core/special-internal-keys.h"
90 #include "core/stuff-handler.h"
91 #include "core/visuals-reseter.h"
92 #include "floor/floor-events.h"
93 #include "game-option/runtime-arguments.h"
94 #include "game-option/special-options.h"
95 #include "io/files-util.h"
96 #include "io/input-key-acceptor.h"
97 #include "io/record-play-movie.h"
98 #include "io/signal-handlers.h"
99 #include "io/write-diary.h"
100 #include "main-win/commandline-win.h"
101 #include "main-win/graphics-win.h"
102 #include "main-win/main-win-bg.h"
103 #include "main-win/main-win-file-utils.h"
104 #include "main-win/main-win-mci.h"
105 #include "main-win/main-win-menuitem.h"
106 #include "main-win/main-win-music.h"
107 #include "main-win/main-win-sound.h"
108 #include "main-win/main-win-utils.h"
109 #include "main/angband-initializer.h"
110 #include "main/sound-of-music.h"
111 #include "monster-floor/monster-lite.h"
112 #include "save/save.h"
113 #include "system/angband.h"
114 #include "system/player-type-definition.h"
115 #include "system/system-variables.h"
116 #include "term/gameterm.h"
117 #include "term/screen-processor.h"
118 #include "term/term-color-types.h"
119 #include "util/angband-files.h"
120 #include "util/int-char-converter.h"
121 #include "util/string-processor.h"
122 #include "view/display-messages.h"
123 #include "wizard/spoiler-util.h"
124 #include "wizard/wizard-spoiler.h"
125 #include "world/world.h"
126
127 #include <cstdlib>
128 #include <locale>
129 #include <string>
130
131 #include <commdlg.h>
132 #include <direct.h>
133
134 /*!
135  * @struct term_data
136  * @brief ターム情報構造体 / Extra "term" data
137  * @details
138  * <p>
139  * pos_x / pos_y は各タームの左上点座標を指す。
140  * </p>
141  * <p>
142  * tile_wid / tile_hgt は[ウィンドウ]メニューのタイルの幅/高さを~を
143  * 1ドットずつ調整するステータスを指す。
144  * また、フォントを変更すると都度自動調整される。
145  * </p>
146  * <p>
147  * Note the use of "font_want" for the names of the font file requested by
148  * the user.
149  * </p>
150  */
151 typedef struct {
152     term_type t;
153     concptr s;
154     HWND w;
155     DWORD dwStyle;
156     DWORD dwExStyle;
157
158     uint keys;
159     TERM_LEN rows; /* int -> uint */
160     TERM_LEN cols;
161
162     uint pos_x; //!< タームの左上X座標
163     uint pos_y; //!< タームの左上Y座標
164     uint size_wid;
165     uint size_hgt;
166     uint size_ow1;
167     uint size_oh1;
168     uint size_ow2;
169     uint size_oh2;
170
171     bool size_hack;
172     bool xtra_hack;
173     bool visible;
174     concptr font_want;
175     HFONT font_id;
176     int font_wid; //!< フォント横幅
177     int font_hgt; //!< フォント縦幅
178     int tile_wid; //!< タイル横幅
179     int tile_hgt; //!< タイル縦幅
180
181     LOGFONT lf;
182
183     bool posfix;
184 } term_data;
185
186 #define MAX_TERM_DATA 8 //!< Maximum number of windows
187
188 static term_data data[MAX_TERM_DATA]; //!< An array of term_data's
189 static term_data *my_td; //!< Hack -- global "window creation" pointer
190 POINT normsize; //!< Remember normal size of main window when maxmized
191
192 /*
193  * was main window maximized on previous playing
194  */
195 bool win_maximized = FALSE;
196
197 /*
198  * game in progress
199  */
200 bool game_in_progress = FALSE;
201
202 /*
203  * note when "open"/"new" become valid
204  */
205 bool initialized = FALSE;
206
207 /*
208  * Saved instance handle
209  */
210 static HINSTANCE hInstance;
211
212 /*
213  * Yellow brush for the cursor
214  */
215 static HBRUSH hbrYellow;
216
217 /*
218  * An icon
219  */
220 static HICON hIcon;
221
222 /* bg */
223 bg_mode current_bg_mode = bg_mode::BG_NONE;
224 #define DEFAULT_BG_FILENAME "bg.bmp"
225 char wallpaper_file[MAIN_WIN_MAX_PATH] = ""; //!< 壁紙ファイル名。
226
227 /*
228  * Show sub-windows even when Hengband is not in focus
229  */
230 static bool keep_subwindows = TRUE;
231
232 /*
233  * Full path to ANGBAND.INI
234  */
235 static concptr ini_file = NULL;
236
237 /*
238  * Name of application
239  */
240 static concptr AppName = "ANGBAND";
241
242 /*
243  * Name of sub-window type
244  */
245 static concptr AngList = "AngList";
246
247 /*
248  * The "complex" color values
249  */
250 static COLORREF win_clr[256];
251
252 /*
253  * Flag for macro trigger with dump ASCII
254  */
255 static bool term_no_press = FALSE;
256
257 /*
258  * Copy and paste
259  */
260 static bool mouse_down = FALSE;
261 static bool paint_rect = FALSE;
262 static TERM_LEN mousex = 0, mousey = 0;
263 static TERM_LEN oldx, oldy;
264
265 /*
266  * Hack -- define which keys are "special"
267  */
268 static bool special_key[256];
269 static bool ignore_key[256];
270
271 /*
272  * Hack -- initialization list for "special_key"
273  */
274 static byte special_key_list[] = {
275     VK_CLEAR, VK_PAUSE, VK_CAPITAL, VK_KANA, VK_JUNJA, VK_FINAL, VK_KANJI, VK_CONVERT, VK_NONCONVERT, VK_ACCEPT, VK_MODECHANGE, VK_PRIOR, VK_NEXT, VK_END,
276     VK_HOME, VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN, VK_SELECT, VK_PRINT, VK_EXECUTE, VK_SNAPSHOT, VK_INSERT, VK_DELETE, VK_HELP, VK_APPS, VK_NUMPAD0, VK_NUMPAD1,
277     VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, VK_NUMPAD8, VK_NUMPAD9, VK_MULTIPLY, VK_ADD, VK_SEPARATOR, VK_SUBTRACT, VK_DECIMAL,
278     VK_DIVIDE, VK_F1, VK_F2, VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_F10, VK_F11, VK_F12, VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18, VK_F19,
279     VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, VK_NUMLOCK, VK_SCROLL, VK_ATTN, VK_CRSEL, VK_EXSEL, VK_EREOF, VK_PLAY, VK_ZOOM, VK_NONAME, VK_PA1,
280     0 /* End of List */
281 };
282
283 static byte ignore_key_list[] = {
284     VK_ESCAPE, VK_TAB, VK_SPACE, 'F', 'W', 'O', /*'H',*/ /* these are menu characters.*/
285     VK_SHIFT, VK_CONTROL, VK_MENU, VK_LWIN, VK_RWIN, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, VK_RCONTROL, VK_LMENU, VK_RMENU, 0 /* End of List */
286 };
287
288 /*
289  * Validate a file
290  */
291 static void validate_file(concptr s)
292 {
293     if (check_file(s))
294         return;
295
296     quit_fmt(_("必要なファイル[%s]が見あたりません。", "Cannot find required file:\n%s"), s);
297 }
298
299 /*
300  * Validate a directory
301  */
302 static void validate_dir(concptr s, bool vital)
303 {
304     if (check_dir(s))
305         return;
306
307     if (vital) {
308         quit_fmt(_("必要なディレクトリ[%s]が見あたりません。", "Cannot find required directory:\n%s"), s);
309     } else if (mkdir(s)) {
310         quit_fmt("Unable to create directory:\n%s", s);
311     }
312 }
313
314 /*!
315  * @brief (Windows版固有実装)Get the "size" for a window
316  */
317 static void term_getsize(term_data *td)
318 {
319     if (td->cols < 1)
320         td->cols = 1;
321     if (td->rows < 1)
322         td->rows = 1;
323
324     TERM_LEN wid = td->cols * td->tile_wid + td->size_ow1 + td->size_ow2;
325     TERM_LEN hgt = td->rows * td->tile_hgt + td->size_oh1 + td->size_oh2;
326
327     RECT rw, rc;
328     if (td->w) {
329         GetWindowRect(td->w, &rw);
330         GetClientRect(td->w, &rc);
331
332         td->size_wid = (rw.right - rw.left) - (rc.right - rc.left) + wid;
333         td->size_hgt = (rw.bottom - rw.top) - (rc.bottom - rc.top) + hgt;
334
335         td->pos_x = rw.left;
336         td->pos_y = rw.top;
337     } else {
338         /* Tempolary calculation */
339         rc.left = 0;
340         rc.right = wid;
341         rc.top = 0;
342         rc.bottom = hgt;
343         AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle);
344         td->size_wid = rc.right - rc.left;
345         td->size_hgt = rc.bottom - rc.top;
346     }
347 }
348
349 /*
350  * Write the "prefs" for a single term
351  */
352 static void save_prefs_aux(int i)
353 {
354     term_data *td = &data[i];
355     GAME_TEXT sec_name[128];
356     char buf[1024];
357
358     if (!td->w)
359         return;
360
361     sprintf(sec_name, "Term-%d", i);
362
363     if (i > 0) {
364         strcpy(buf, td->visible ? "1" : "0");
365         WritePrivateProfileString(sec_name, "Visible", buf, ini_file);
366     }
367
368 #ifdef JP
369     strcpy(buf, td->lf.lfFaceName[0] != '\0' ? td->lf.lfFaceName : "MS ゴシック");
370 #else
371     strcpy(buf, td->lf.lfFaceName[0] != '\0' ? td->lf.lfFaceName : "Courier");
372 #endif
373
374     WritePrivateProfileString(sec_name, "Font", buf, ini_file);
375
376     wsprintf(buf, "%d", td->lf.lfWidth);
377     WritePrivateProfileString(sec_name, "FontWid", buf, ini_file);
378     wsprintf(buf, "%d", td->lf.lfHeight);
379     WritePrivateProfileString(sec_name, "FontHgt", buf, ini_file);
380     wsprintf(buf, "%d", td->lf.lfWeight);
381     WritePrivateProfileString(sec_name, "FontWgt", buf, ini_file);
382
383     wsprintf(buf, "%d", td->tile_wid);
384     WritePrivateProfileString(sec_name, "TileWid", buf, ini_file);
385
386     wsprintf(buf, "%d", td->tile_hgt);
387     WritePrivateProfileString(sec_name, "TileHgt", buf, ini_file);
388
389     WINDOWPLACEMENT lpwndpl;
390     lpwndpl.length = sizeof(WINDOWPLACEMENT);
391     GetWindowPlacement(td->w, &lpwndpl);
392
393     RECT rc = lpwndpl.rcNormalPosition;
394     if (i == 0)
395         wsprintf(buf, "%d", normsize.x);
396     else
397         wsprintf(buf, "%d", td->cols);
398
399     WritePrivateProfileString(sec_name, "NumCols", buf, ini_file);
400
401     if (i == 0)
402         wsprintf(buf, "%d", normsize.y);
403     else
404         wsprintf(buf, "%d", td->rows);
405
406     WritePrivateProfileString(sec_name, "NumRows", buf, ini_file);
407     if (i == 0) {
408         strcpy(buf, IsZoomed(td->w) ? "1" : "0");
409         WritePrivateProfileString(sec_name, "Maximized", buf, ini_file);
410     }
411
412     GetWindowRect(td->w, &rc);
413     wsprintf(buf, "%d", rc.left);
414     WritePrivateProfileString(sec_name, "PositionX", buf, ini_file);
415
416     wsprintf(buf, "%d", rc.top);
417     WritePrivateProfileString(sec_name, "PositionY", buf, ini_file);
418     if (i > 0) {
419         strcpy(buf, td->posfix ? "1" : "0");
420         WritePrivateProfileString(sec_name, "PositionFix", buf, ini_file);
421     }
422 }
423
424 /*
425  * Write the "prefs"
426  * We assume that the windows have all been initialized
427  */
428 static void save_prefs(void)
429 {
430     char buf[128];
431     sprintf(buf, "%d", arg_graphics);
432     WritePrivateProfileString("Angband", "Graphics", buf, ini_file);
433
434     strcpy(buf, arg_bigtile ? "1" : "0");
435     WritePrivateProfileString("Angband", "Bigtile", buf, ini_file);
436
437     strcpy(buf, arg_sound ? "1" : "0");
438     WritePrivateProfileString("Angband", "Sound", buf, ini_file);
439
440     strcpy(buf, arg_music ? "1" : "0");
441     WritePrivateProfileString("Angband", "Music", buf, ini_file);
442     strcpy(buf, use_pause_music_inactive ? "1" : "0");
443     WritePrivateProfileString("Angband", "MusicPauseInactive", buf, ini_file);
444
445     sprintf(buf, "%d", current_bg_mode);
446     WritePrivateProfileString("Angband", "BackGround", buf, ini_file);
447     WritePrivateProfileString("Angband", "BackGroundBitmap", wallpaper_file[0] != '\0' ? wallpaper_file : DEFAULT_BG_FILENAME, ini_file);
448
449     int path_length = strlen(ANGBAND_DIR) - 4; /* \libの4文字分を削除 */
450     char tmp[1024] = "";
451     strncat(tmp, ANGBAND_DIR, path_length);
452
453     int n = strncmp(tmp, savefile, path_length);
454     if (n == 0) {
455         char relative_path[1024] = "";
456         snprintf(relative_path, sizeof(relative_path), ".\\%s", (savefile + path_length));
457         WritePrivateProfileString("Angband", "SaveFile", relative_path, ini_file);
458     } else {
459         WritePrivateProfileString("Angband", "SaveFile", savefile, ini_file);
460     }
461
462     strcpy(buf, keep_subwindows ? "1" : "0");
463     WritePrivateProfileString("Angband", "KeepSubwindows", buf, ini_file);
464
465     for (int i = 0; i < MAX_TERM_DATA; ++i) {
466         save_prefs_aux(i);
467     }
468 }
469
470 /*
471  * callback for EnumDisplayMonitors API
472  */
473 BOOL CALLBACK monitorenumproc([[maybe_unused]] HMONITOR hMon, [[maybe_unused]] HDC hdcMon, [[maybe_unused]] LPRECT lpMon, LPARAM dwDate)
474 {
475     bool *result = (bool *)dwDate;
476     *result = true;
477     return FALSE;
478 }
479
480 /*
481  * Load the "prefs" for a single term
482  */
483 static void load_prefs_aux(int i)
484 {
485     term_data *td = &data[i];
486     GAME_TEXT sec_name[128];
487     char tmp[1024];
488
489     sprintf(sec_name, "Term-%d", i);
490     sprintf(sec_name, "Term-%d", i);
491     if (i > 0) {
492         td->visible = (GetPrivateProfileInt(sec_name, "Visible", td->visible, ini_file) != 0);
493     }
494
495 #ifdef JP
496     GetPrivateProfileString(sec_name, "Font", "MS ゴシック", tmp, 127, ini_file);
497 #else
498     GetPrivateProfileString(sec_name, "Font", "Courier", tmp, 127, ini_file);
499 #endif
500
501     td->font_want = string_make(tmp);
502     int hgt = 15;
503     int wid = 0;
504     td->lf.lfWidth = GetPrivateProfileInt(sec_name, "FontWid", wid, ini_file);
505     td->lf.lfHeight = GetPrivateProfileInt(sec_name, "FontHgt", hgt, ini_file);
506     td->lf.lfWeight = GetPrivateProfileInt(sec_name, "FontWgt", 0, ini_file);
507
508     td->tile_wid = GetPrivateProfileInt(sec_name, "TileWid", td->lf.lfWidth, ini_file);
509     td->tile_hgt = GetPrivateProfileInt(sec_name, "TileHgt", td->lf.lfHeight, ini_file);
510
511     td->cols = GetPrivateProfileInt(sec_name, "NumCols", td->cols, ini_file);
512     td->rows = GetPrivateProfileInt(sec_name, "NumRows", td->rows, ini_file);
513     normsize.x = td->cols;
514     normsize.y = td->rows;
515
516     if (i == 0) {
517         win_maximized = (GetPrivateProfileInt(sec_name, "Maximized", win_maximized, ini_file) != 0);
518     }
519
520     int posx = GetPrivateProfileInt(sec_name, "PositionX", 0, ini_file);
521     int posy = GetPrivateProfileInt(sec_name, "PositionY", 0, ini_file);
522     // 保存座標がモニタ内の領域にあるかチェック
523     RECT rect = { posx, posy, posx + 128, posy + 128 };
524     bool in_any_monitor = false;
525     ::EnumDisplayMonitors(NULL, &rect, monitorenumproc, (LPARAM)&in_any_monitor);
526     if (in_any_monitor) {
527         // いずれかのモニタに表示可能、ウインドウ位置を復元
528         td->pos_x = posx;
529         td->pos_y = posy;
530     }
531
532     if (i > 0) {
533         td->posfix = (GetPrivateProfileInt(sec_name, "PositionFix", td->posfix, ini_file) != 0);
534     }
535 }
536
537 /*
538  * Load the "prefs"
539  */
540 static void load_prefs(void)
541 {
542     arg_graphics = (byte)GetPrivateProfileInt("Angband", "Graphics", static_cast<byte>(graphics_mode::GRAPHICS_NONE), ini_file);
543     arg_bigtile = (GetPrivateProfileInt("Angband", "Bigtile", FALSE, ini_file) != 0);
544     use_bigtile = arg_bigtile;
545     arg_sound = (GetPrivateProfileInt("Angband", "Sound", 0, ini_file) != 0);
546     arg_music = (GetPrivateProfileInt("Angband", "Music", 0, ini_file) != 0);
547     use_pause_music_inactive = (GetPrivateProfileInt("Angband", "MusicPauseInactive", 0, ini_file) != 0);
548     current_bg_mode = static_cast<bg_mode>(GetPrivateProfileInt("Angband", "BackGround", 0, ini_file));
549     GetPrivateProfileString("Angband", "BackGroundBitmap", DEFAULT_BG_FILENAME, wallpaper_file, 1023, ini_file);
550     GetPrivateProfileString("Angband", "SaveFile", "", savefile, 1023, ini_file);
551
552     int n = strncmp(".\\", savefile, 2);
553     if (n == 0) {
554         int path_length = strlen(ANGBAND_DIR) - 4; /* \libの4文字分を削除 */
555         char tmp[1024] = "";
556         strncat(tmp, ANGBAND_DIR, path_length);
557         strncat(tmp, savefile + 2, strlen(savefile) - 2 + path_length);
558         strncpy(savefile, tmp, strlen(tmp));
559     }
560
561     keep_subwindows = (GetPrivateProfileInt("Angband", "KeepSubwindows", 0, ini_file) != 0);
562     for (int i = 0; i < MAX_TERM_DATA; ++i) {
563         load_prefs_aux(i);
564     }
565 }
566
567 /*
568  * Initialize music
569  */
570 static void init_music(void)
571 {
572     // Flag set once "music" has been initialized
573     static bool can_use_music = FALSE;
574
575     if (!can_use_music) {
576         main_win_music::load_music_prefs();
577         can_use_music = TRUE;
578     }
579 }
580
581 /*
582  * Initialize sound
583  */
584 static void init_sound(void)
585 {
586     // Flag set once "sound" has been initialized
587     static bool can_use_sound = FALSE;
588
589     if (!can_use_sound) {
590         load_sound_prefs();
591         can_use_sound = TRUE;
592     }
593 }
594
595 /*!
596  * @brief Change sound mode
597  * @param new_mode bool
598  */
599 static void change_sound_mode(bool new_mode)
600 {
601     use_sound = new_mode;
602     if (use_sound) {
603         init_sound();
604     }
605 }
606
607 /*
608  * Initialize background
609  */
610 static void init_background(void)
611 {
612     // Flag set once "background" has been initialized
613     static bool can_use_background = FALSE;
614
615     if (!can_use_background) {
616         load_bg_prefs();
617         can_use_background = TRUE;
618     }
619 }
620
621 /*!
622  * @brief Change background mode
623  * @param new_mode bg_mode
624  * @param show_error trueに設定した場合のみ、エラーダイアログを表示する
625  * @param force_redraw trueの場合、モード変更に関わらずウインドウを再描画する
626  * @retval true success
627  * @retval false failed
628  */
629 static bool change_bg_mode(bg_mode new_mode, bool show_error = false, bool force_redraw = false)
630 {
631     bg_mode old_bg_mode = current_bg_mode;
632     current_bg_mode = new_mode;
633     if (current_bg_mode != bg_mode::BG_NONE) {
634         init_background();
635         if (!load_bg(wallpaper_file)) {
636             current_bg_mode = bg_mode::BG_NONE;
637             if (show_error)
638                 plog_fmt(_("壁紙用ファイル '%s' を読み込めません。", "Can't load the image file '%s'."), wallpaper_file);
639         }
640     } else {
641         delete_bg();
642     }
643
644     const bool mode_changed = (current_bg_mode != old_bg_mode);
645     if (mode_changed || force_redraw) {
646         // 全ウインドウ再描画
647         for (int i = 0; i < MAX_TERM_DATA; i++) {
648             term_data *td = &data[i];
649             if (td->visible)
650                 InvalidateRect(td->w, NULL, FALSE);
651         }
652     }
653
654     return (current_bg_mode == new_mode);
655 }
656
657 /*!
658  * @brief Resize a window
659  */
660 static void term_window_resize(term_data *td)
661 {
662     if (!td->w)
663         return;
664
665     SetWindowPos(td->w, 0, 0, 0, td->size_wid, td->size_hgt, SWP_NOMOVE | SWP_NOZORDER);
666     InvalidateRect(td->w, NULL, TRUE);
667 }
668
669 /*!
670  * @brief Force the use of a new font for a term_data.
671  * This function may be called before the "window" is ready.
672  * This function returns zero only if everything succeeds.
673  * @note that the "font name" must be capitalized!!!
674  */
675 static errr term_force_font(term_data *td)
676 {
677     if (td->font_id)
678         DeleteObject(td->font_id);
679
680     td->font_id = CreateFontIndirect(&(td->lf));
681     int wid = td->lf.lfWidth;
682     int hgt = td->lf.lfHeight;
683     if (!td->font_id)
684         return 1;
685
686     if (!wid || !hgt) {
687         HDC hdcDesktop;
688         HFONT hfOld;
689         TEXTMETRIC tm;
690
691         hdcDesktop = GetDC(HWND_DESKTOP);
692         hfOld = static_cast<HFONT>(SelectObject(hdcDesktop, td->font_id));
693         GetTextMetrics(hdcDesktop, &tm);
694         SelectObject(hdcDesktop, hfOld);
695         ReleaseDC(HWND_DESKTOP, hdcDesktop);
696
697         wid = tm.tmAveCharWidth;
698         hgt = tm.tmHeight;
699     }
700
701     td->font_wid = wid;
702     td->font_hgt = hgt;
703
704     return 0;
705 }
706
707 /*
708  * Allow the user to change the font for this window.
709  */
710 static void term_change_font(term_data *td)
711 {
712     CHOOSEFONT cf;
713     memset(&cf, 0, sizeof(cf));
714     cf.lStructSize = sizeof(cf);
715     cf.Flags = CF_SCREENFONTS | CF_FIXEDPITCHONLY | CF_NOVERTFONTS | CF_INITTOLOGFONTSTRUCT;
716     cf.lpLogFont = &(td->lf);
717
718     if (!ChooseFont(&cf))
719         return;
720
721     term_force_font(td);
722     td->tile_wid = td->font_wid;
723     td->tile_hgt = td->font_hgt;
724     term_getsize(td);
725     term_window_resize(td);
726 }
727
728 /*
729  * Allow the user to lock this window.
730  */
731 static void term_window_pos(term_data *td, HWND hWnd)
732 {
733     SetWindowPos(td->w, hWnd, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
734 }
735
736 /*
737  * Hack -- redraw a term_data
738  */
739 static void term_data_redraw(term_data *td)
740 {
741     term_activate(&td->t);
742     term_redraw();
743     term_activate(term_screen);
744 }
745
746 void term_inversed_area(HWND hWnd, int x, int y, int w, int h)
747 {
748     term_data *td = (term_data *)GetWindowLong(hWnd, 0);
749     int tx = td->size_ow1 + x * td->tile_wid;
750     int ty = td->size_oh1 + y * td->tile_hgt;
751     int tw = w * td->tile_wid - 1;
752     int th = h * td->tile_hgt - 1;
753
754     HDC hdc = GetDC(hWnd);
755     HBRUSH myBrush = CreateSolidBrush(RGB(255, 255, 255));
756     HBRUSH oldBrush = static_cast<HBRUSH>(SelectObject(hdc, myBrush));
757     HPEN oldPen = static_cast<HPEN>(SelectObject(hdc, GetStockObject(NULL_PEN)));
758
759     PatBlt(hdc, tx, ty, tw, th, PATINVERT);
760
761     SelectObject(hdc, oldBrush);
762     SelectObject(hdc, oldPen);
763 }
764
765 /*!
766  * @brief //!< Windows版ユーザ設定項目実装部(実装必須) /Interact with the User
767  */
768 static errr term_user_win(int n)
769 {
770     (void)n;
771     return 0;
772 }
773
774 static void refresh_color_table()
775 {
776     for (int i = 0; i < 256; i++) {
777         byte rv = angband_color_table[i][1];
778         byte gv = angband_color_table[i][2];
779         byte bv = angband_color_table[i][3];
780         win_clr[i] = PALETTERGB(rv, gv, bv);
781     }
782 }
783
784 /*
785  * React to global changes
786  */
787 static errr term_xtra_win_react(player_type *player_ptr)
788 {
789     refresh_color_table();
790
791     const byte old_graphics = arg_graphics;
792     arg_graphics = static_cast<byte>(graphic.change_graphics(static_cast<graphics_mode>(arg_graphics)));
793     if (old_graphics != arg_graphics) {
794         plog(_("グラフィックスを初期化できません!", "Cannot initialize graphics!"));
795     }
796     use_graphics = (arg_graphics > 0);
797     reset_visuals(player_ptr);
798
799     for (int i = 0; i < MAX_TERM_DATA; i++) {
800         term_type *old = Term;
801         term_data *td = &data[i];
802         if ((td->cols != td->t.wid) || (td->rows != td->t.hgt)) {
803             term_activate(&td->t);
804             term_resize(td->cols, td->rows);
805             term_redraw();
806             term_activate(old);
807         }
808     }
809
810     return 0;
811 }
812
813 /*
814  * Process at least one event
815  */
816 static errr term_xtra_win_event(int v)
817 {
818     MSG msg;
819     if (v) {
820         if (GetMessage(&msg, NULL, 0, 0)) {
821             TranslateMessage(&msg);
822             DispatchMessage(&msg);
823         }
824     } else {
825         if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
826             TranslateMessage(&msg);
827             DispatchMessage(&msg);
828         }
829     }
830
831     return 0;
832 }
833
834 /*
835  * Process all pending events
836  */
837 static errr term_xtra_win_flush(void)
838 {
839     MSG msg;
840     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
841         TranslateMessage(&msg);
842         DispatchMessage(&msg);
843     }
844
845     return 0;
846 }
847
848 /*
849  * Hack -- clear the screen
850  *
851  * Make this more efficient
852  */
853 static errr term_xtra_win_clear(void)
854 {
855     term_data *td = (term_data *)(Term->data);
856
857     RECT rc;
858     rc.left = td->size_ow1;
859     rc.right = rc.left + td->cols * td->tile_wid;
860     rc.top = td->size_oh1;
861     rc.bottom = rc.top + td->rows * td->tile_hgt;
862
863     HDC hdc = GetDC(td->w);
864     SetBkColor(hdc, RGB(0, 0, 0));
865     SelectObject(hdc, td->font_id);
866     ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
867
868     if (current_bg_mode != bg_mode::BG_NONE) {
869         rc.left = 0;
870         rc.top = 0;
871         draw_bg(hdc, &rc);
872     }
873
874     ReleaseDC(td->w, hdc);
875     return 0;
876 }
877
878 /*
879  * Hack -- make a noise
880  */
881 static errr term_xtra_win_noise(void)
882 {
883     MessageBeep(MB_ICONASTERISK);
884     return 0;
885 }
886
887 /*
888  * Hack -- make a sound
889  */
890 static errr term_xtra_win_sound(int v)
891 {
892     if (!use_sound)
893         return 1;
894     return play_sound(v);
895 }
896
897 /*
898  * Hack -- play a music
899  */
900 static errr term_xtra_win_music(int n, int v)
901 {
902     if (!use_music) {
903         return 1;
904     }
905
906     return main_win_music::play_music(n, v);
907 }
908
909 /*
910  * Hack -- play a music matches a situation
911  */
912 static errr term_xtra_win_scene(int v)
913 {
914     // TODO 場面に合った壁紙変更対応
915     if (!use_music) {
916         return 1;
917     }
918
919     return main_win_music::play_music_scene(v);
920 }
921
922 /*
923  * Delay for "x" milliseconds
924  */
925 static int term_xtra_win_delay(int v)
926 {
927     Sleep(v);
928     return 0;
929 }
930
931 /*!
932  * @brief Do a "special thing"
933  * @todo z-termに影響があるのでplayer_typeの追加は保留
934  */
935 static errr term_xtra_win(int n, int v)
936 {
937     switch (n) {
938     case TERM_XTRA_NOISE: {
939         return (term_xtra_win_noise());
940     }
941     case TERM_XTRA_MUSIC_BASIC:
942     case TERM_XTRA_MUSIC_DUNGEON:
943     case TERM_XTRA_MUSIC_QUEST:
944     case TERM_XTRA_MUSIC_TOWN:
945     case TERM_XTRA_MUSIC_MONSTER: {
946         return term_xtra_win_music(n, v);
947     }
948     case TERM_XTRA_MUSIC_MUTE: {
949         return main_win_music::stop_music();
950     }
951     case TERM_XTRA_SCENE: {
952         return term_xtra_win_scene(v);
953     }
954     case TERM_XTRA_SOUND: {
955         return (term_xtra_win_sound(v));
956     }
957     case TERM_XTRA_BORED: {
958         return (term_xtra_win_event(0));
959     }
960     case TERM_XTRA_EVENT: {
961         return (term_xtra_win_event(v));
962     }
963     case TERM_XTRA_FLUSH: {
964         return (term_xtra_win_flush());
965     }
966     case TERM_XTRA_CLEAR: {
967         return (term_xtra_win_clear());
968     }
969     case TERM_XTRA_REACT: {
970         return (term_xtra_win_react(p_ptr));
971     }
972     case TERM_XTRA_DELAY: {
973         return (term_xtra_win_delay(v));
974     }
975     }
976
977     return 1;
978 }
979
980 /*
981  * Low level graphics (Assumes valid input).
982  *
983  * Draw a "cursor" at (x,y), using a "yellow box".
984  */
985 static errr term_curs_win(int x, int y)
986 {
987     term_data *td = (term_data *)(Term->data);
988     int tile_wid, tile_hgt;
989     tile_wid = td->tile_wid;
990     tile_hgt = td->tile_hgt;
991
992     RECT rc;
993     rc.left = x * tile_wid + td->size_ow1;
994     rc.right = rc.left + tile_wid;
995     rc.top = y * tile_hgt + td->size_oh1;
996     rc.bottom = rc.top + tile_hgt;
997
998     HDC hdc = GetDC(td->w);
999     FrameRect(hdc, &rc, hbrYellow);
1000     ReleaseDC(td->w, hdc);
1001     return 0;
1002 }
1003
1004 /*
1005  * Low level graphics (Assumes valid input).
1006  *
1007  * Draw a "big cursor" at (x,y), using a "yellow box".
1008  */
1009 static errr term_bigcurs_win(int x, int y)
1010 {
1011     term_data *td = (term_data *)(Term->data);
1012     int tile_wid, tile_hgt;
1013     tile_wid = td->tile_wid;
1014     tile_hgt = td->tile_hgt;
1015
1016     RECT rc;
1017     rc.left = x * tile_wid + td->size_ow1;
1018     rc.right = rc.left + 2 * tile_wid;
1019     rc.top = y * tile_hgt + td->size_oh1;
1020     rc.bottom = rc.top + tile_hgt;
1021
1022     HDC hdc = GetDC(td->w);
1023     FrameRect(hdc, &rc, hbrYellow);
1024     ReleaseDC(td->w, hdc);
1025     return 0;
1026 }
1027
1028 /*
1029  * Low level graphics (Assumes valid input).
1030  *
1031  * Erase a "block" of "n" characters starting at (x,y).
1032  */
1033 static errr term_wipe_win(int x, int y, int n)
1034 {
1035     term_data *td = (term_data *)(Term->data);
1036     RECT rc;
1037     rc.left = x * td->tile_wid + td->size_ow1;
1038     rc.right = rc.left + n * td->tile_wid;
1039     rc.top = y * td->tile_hgt + td->size_oh1;
1040     rc.bottom = rc.top + td->tile_hgt;
1041
1042     HDC hdc = GetDC(td->w);
1043     SetBkColor(hdc, RGB(0, 0, 0));
1044     SelectObject(hdc, td->font_id);
1045     if (current_bg_mode != bg_mode::BG_NONE)
1046         draw_bg(hdc, &rc);
1047     else
1048         ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
1049
1050     ReleaseDC(td->w, hdc);
1051     return 0;
1052 }
1053
1054 /*
1055  * Low level graphics.  Assumes valid input.
1056  *
1057  * Draw several ("n") chars, with an attr, at a given location.
1058  *
1059  * All "graphic" data is handled by "term_pict_win()", below.
1060  *
1061  * One would think there is a more efficient method for telling a window
1062  * what color it should be using to draw with, but perhaps simply changing
1063  * it every time is not too inefficient.
1064  */
1065 static errr term_text_win(int x, int y, int n, TERM_COLOR a, concptr s)
1066 {
1067     term_data *td = (term_data *)(Term->data);
1068     static HBITMAP WALL;
1069     static HBRUSH myBrush, oldBrush;
1070     static HPEN oldPen;
1071     static bool init_done = FALSE;
1072
1073     if (!init_done) {
1074         WALL = LoadBitmap(hInstance, AppName);
1075         myBrush = CreatePatternBrush(WALL);
1076         init_done = TRUE;
1077     }
1078
1079     RECT rc{ static_cast<LONG>(x * td->tile_wid + td->size_ow1), static_cast<LONG>(y * td->tile_hgt + td->size_oh1),
1080         static_cast<LONG>(rc.left + n * td->tile_wid), static_cast<LONG>(rc.top + td->tile_hgt) };
1081
1082     HDC hdc = GetDC(td->w);
1083     SetBkColor(hdc, RGB(0, 0, 0));
1084     SetTextColor(hdc, win_clr[a]);
1085
1086     SelectObject(hdc, td->font_id);
1087     if (current_bg_mode != bg_mode::BG_NONE)
1088         SetBkMode(hdc, TRANSPARENT);
1089
1090     ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
1091     if (current_bg_mode != bg_mode::BG_NONE)
1092         draw_bg(hdc, &rc);
1093
1094     rc.left += ((td->tile_wid - td->font_wid) / 2);
1095     rc.right = rc.left + td->font_wid;
1096     rc.top += ((td->tile_hgt - td->font_hgt) / 2);
1097     rc.bottom = rc.top + td->font_hgt;
1098
1099     for (int i = 0; i < n; i++) {
1100 #ifdef JP
1101         if (use_bigtile && *(s + i) == "■"[0] && *(s + i + 1) == "■"[1]) {
1102             rc.right += td->font_wid;
1103             oldBrush = static_cast<HBRUSH>(SelectObject(hdc, myBrush));
1104             oldPen = static_cast<HPEN>(SelectObject(hdc, GetStockObject(NULL_PEN)));
1105             Rectangle(hdc, rc.left, rc.top, rc.right + 1, rc.bottom + 1);
1106             SelectObject(hdc, oldBrush);
1107             SelectObject(hdc, oldPen);
1108             rc.right -= td->font_wid;
1109             i++;
1110             rc.left += 2 * td->tile_wid;
1111             rc.right += 2 * td->tile_wid;
1112         } else if (iskanji(*(s + i))) /* 2バイト文字 */
1113         {
1114             rc.right += td->font_wid;
1115             ExtTextOut(hdc, rc.left, rc.top, ETO_CLIPPED, &rc, s + i, 2, NULL);
1116             rc.right -= td->font_wid;
1117             i++;
1118             rc.left += 2 * td->tile_wid;
1119             rc.right += 2 * td->tile_wid;
1120         } else if (*(s + i) == 127) {
1121             oldBrush = static_cast<HBRUSH>(SelectObject(hdc, myBrush));
1122             oldPen = static_cast<HPEN>(SelectObject(hdc, GetStockObject(NULL_PEN)));
1123             Rectangle(hdc, rc.left, rc.top, rc.right + 1, rc.bottom + 1);
1124             SelectObject(hdc, oldBrush);
1125             SelectObject(hdc, oldPen);
1126             rc.left += td->tile_wid;
1127             rc.right += td->tile_wid;
1128         } else {
1129             ExtTextOut(hdc, rc.left, rc.top, ETO_CLIPPED, &rc, s + i, 1, NULL);
1130             rc.left += td->tile_wid;
1131             rc.right += td->tile_wid;
1132         }
1133 #else
1134         if (*(s + i) == 127) {
1135             oldBrush = static_cast<HBRUSH>(SelectObject(hdc, myBrush));
1136             oldPen = static_cast<HPEN>(SelectObject(hdc, GetStockObject(NULL_PEN)));
1137             Rectangle(hdc, rc.left, rc.top, rc.right + 1, rc.bottom + 1);
1138             SelectObject(hdc, oldBrush);
1139             SelectObject(hdc, oldPen);
1140             rc.left += td->tile_wid;
1141             rc.right += td->tile_wid;
1142         } else {
1143             ExtTextOut(hdc, rc.left, rc.top, ETO_CLIPPED, &rc, s + i, 1, NULL);
1144             rc.left += td->tile_wid;
1145             rc.right += td->tile_wid;
1146         }
1147 #endif
1148     }
1149
1150     ReleaseDC(td->w, hdc);
1151     return 0;
1152 }
1153
1154 /*
1155  * Low level graphics.  Assumes valid input.
1156  *
1157  * Draw an array of "special" attr/char pairs at the given location.
1158  *
1159  * We use the "term_pict_win()" function for "graphic" data, which are
1160  * encoded by setting the "high-bits" of both the "attr" and the "char"
1161  * data.  We use the "attr" to represent the "row" of the main bitmap,
1162  * and the "char" to represent the "col" of the main bitmap.  The use
1163  * of this function is induced by the "higher_pict" flag.
1164  *
1165  * If "graphics" is not available, we simply "wipe" the given grids.
1166  */
1167 static errr term_pict_win(TERM_LEN x, TERM_LEN y, int n, const TERM_COLOR *ap, concptr cp, const TERM_COLOR *tap, concptr tcp)
1168 {
1169     term_data *td = (term_data *)(Term->data);
1170     int i;
1171     HDC hdcMask = NULL;
1172     if (!use_graphics) {
1173         return (term_wipe_win(x, y, n));
1174     }
1175
1176     const tile_info &infGraph = graphic.get_tile_info();
1177     const bool has_mask = (infGraph.hBitmapMask != NULL);
1178     TERM_LEN w1 = infGraph.CellWidth;
1179     TERM_LEN h1 = infGraph.CellHeight;
1180     TERM_LEN tw1 = infGraph.TileWidth;
1181     TERM_LEN th1 = infGraph.TileHeight;
1182     TERM_LEN w2, h2, tw2 = 0;
1183     w2 = td->tile_wid;
1184     h2 = td->tile_hgt;
1185     tw2 = w2;
1186     if (use_bigtile)
1187         tw2 *= 2;
1188
1189     TERM_LEN x2 = x * w2 + td->size_ow1 + infGraph.OffsetX;
1190     TERM_LEN y2 = y * h2 + td->size_oh1 + infGraph.OffsetY;
1191     HDC hdc = GetDC(td->w);
1192     HDC hdcSrc = CreateCompatibleDC(hdc);
1193     HBITMAP hbmSrcOld = static_cast<HBITMAP>(SelectObject(hdcSrc, infGraph.hBitmap));
1194
1195     if (has_mask) {
1196         hdcMask = CreateCompatibleDC(hdc);
1197         SelectObject(hdcMask, infGraph.hBitmapMask);
1198     }
1199
1200     for (i = 0; i < n; i++, x2 += w2) {
1201         TERM_COLOR a = ap[i];
1202         char c = cp[i];
1203         int row = (a & 0x7F);
1204         int col = (c & 0x7F);
1205         TERM_LEN x1 = col * w1;
1206         TERM_LEN y1 = row * h1;
1207
1208         if (has_mask) {
1209             TERM_LEN x3 = (tcp[i] & 0x7F) * w1;
1210             TERM_LEN y3 = (tap[i] & 0x7F) * h1;
1211             tw2 = tw2 * w1 / tw1;
1212             h2 = h2 * h1 / th1;
1213             if ((tw1 == tw2) && (th1 == h2)) {
1214                 BitBlt(hdc, x2, y2, tw2, h2, hdcSrc, x3, y3, SRCCOPY);
1215                 BitBlt(hdc, x2, y2, tw2, h2, hdcMask, x1, y1, SRCAND);
1216                 BitBlt(hdc, x2, y2, tw2, h2, hdcSrc, x1, y1, SRCPAINT);
1217                 continue;
1218             }
1219
1220             SetStretchBltMode(hdc, COLORONCOLOR);
1221             StretchBlt(hdc, x2, y2, tw2, h2, hdcMask, x3, y3, w1, h1, SRCAND);
1222             StretchBlt(hdc, x2, y2, tw2, h2, hdcSrc, x3, y3, w1, h1, SRCPAINT);
1223             if ((x1 != x3) || (y1 != y3)) {
1224                 StretchBlt(hdc, x2, y2, tw2, h2, hdcMask, x1, y1, w1, h1, SRCAND);
1225                 StretchBlt(hdc, x2, y2, tw2, h2, hdcSrc, x1, y1, w1, h1, SRCPAINT);
1226             }
1227
1228             continue;
1229         }
1230
1231         if ((w1 == tw2) && (h1 == h2)) {
1232             BitBlt(hdc, x2, y2, tw2, h2, hdcSrc, x1, y1, SRCCOPY);
1233             continue;
1234         }
1235
1236         SetStretchBltMode(hdc, COLORONCOLOR);
1237         StretchBlt(hdc, x2, y2, tw2, h2, hdcSrc, x1, y1, w1, h1, SRCCOPY);
1238     }
1239
1240     SelectObject(hdcSrc, hbmSrcOld);
1241     DeleteDC(hdcSrc);
1242     if (has_mask) {
1243         SelectObject(hdcMask, hbmSrcOld);
1244         DeleteDC(hdcMask);
1245     }
1246
1247     ReleaseDC(td->w, hdc);
1248     return 0;
1249 }
1250
1251 /*
1252  * Create and initialize a "term_data" given a title
1253  */
1254 static void term_data_link(term_data *td)
1255 {
1256     term_type *t = &td->t;
1257     term_init(t, td->cols, td->rows, FILE_READ_BUFF_SIZE);
1258     t->soft_cursor = TRUE;
1259     t->higher_pict = TRUE;
1260     t->attr_blank = TERM_WHITE;
1261     t->char_blank = ' ';
1262     t->user_hook = term_user_win;
1263     t->xtra_hook = term_xtra_win;
1264     t->curs_hook = term_curs_win;
1265     t->bigcurs_hook = term_bigcurs_win;
1266     t->wipe_hook = term_wipe_win;
1267     t->text_hook = term_text_win;
1268     t->pict_hook = term_pict_win;
1269     t->data = (vptr)(td);
1270 }
1271
1272 /*
1273  * Create the windows
1274  *
1275  * First, instantiate the "default" values, then read the "ini_file"
1276  * to over-ride selected values, then create the windows, and fonts.
1277  *
1278  * Must use SW_SHOW not SW_SHOWNA, since on 256 color display
1279  * must make active to realize the palette.
1280  */
1281 static void init_windows(void)
1282 {
1283     term_data *td;
1284     td = &data[0];
1285     WIPE(td, term_data);
1286 #ifdef JP
1287     td->s = "変愚蛮怒";
1288 #else
1289     td->s = angband_term_name[0];
1290 #endif
1291
1292     td->keys = 1024;
1293     td->rows = 24;
1294     td->cols = 80;
1295     td->visible = TRUE;
1296     td->size_ow1 = 2;
1297     td->size_ow2 = 2;
1298     td->size_oh1 = 2;
1299     td->size_oh2 = 2;
1300     td->pos_x = 7 * 30;
1301     td->pos_y = 7 * 20;
1302     td->posfix = FALSE;
1303
1304     for (int i = 1; i < MAX_TERM_DATA; i++) {
1305         td = &data[i];
1306         WIPE(td, term_data);
1307         td->s = angband_term_name[i];
1308         td->keys = 16;
1309         td->rows = 24;
1310         td->cols = 80;
1311         td->visible = FALSE;
1312         td->size_ow1 = 1;
1313         td->size_ow2 = 1;
1314         td->size_oh1 = 1;
1315         td->size_oh2 = 1;
1316         td->pos_x = (7 - i) * 30;
1317         td->pos_y = (7 - i) * 20;
1318         td->posfix = FALSE;
1319     }
1320
1321     load_prefs();
1322
1323     /* Atrributes of main window */
1324     td = &data[0];
1325     td->dwStyle = (WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CAPTION | WS_VISIBLE);
1326     td->dwExStyle = 0;
1327     td->visible = TRUE;
1328
1329     /* Attributes of sub windows */
1330     for (int i = 1; i < MAX_TERM_DATA; i++) {
1331         td = &data[i];
1332         td->dwStyle = (WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU);
1333         td->dwExStyle = (WS_EX_TOOLWINDOW);
1334     }
1335
1336     /* Font of each window */
1337     for (int i = 0; i < MAX_TERM_DATA; i++) {
1338         td = &data[i];
1339         strncpy(td->lf.lfFaceName, td->font_want, LF_FACESIZE);
1340         td->lf.lfCharSet = DEFAULT_CHARSET;
1341         td->lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
1342         term_force_font(td);
1343         if (!td->tile_wid)
1344             td->tile_wid = td->font_wid;
1345         if (!td->tile_hgt)
1346             td->tile_hgt = td->font_hgt;
1347         term_getsize(td);
1348         term_window_resize(td);
1349     }
1350
1351     /* Create sub windows */
1352     for (int i = MAX_TERM_DATA - 1; i >= 1; --i) {
1353         td = &data[i];
1354
1355         my_td = td;
1356         td->w
1357             = CreateWindowEx(td->dwExStyle, AngList, td->s, td->dwStyle, td->pos_x, td->pos_y, td->size_wid, td->size_hgt, HWND_DESKTOP, NULL, hInstance, NULL);
1358         my_td = NULL;
1359
1360         if (!td->w)
1361             quit(_("サブウィンドウに作成に失敗しました", "Failed to create sub-window"));
1362
1363         td->size_hack = TRUE;
1364         term_getsize(td);
1365         term_window_resize(td);
1366
1367         if (td->visible) {
1368             ShowWindow(td->w, SW_SHOW);
1369         }
1370         td->size_hack = FALSE;
1371
1372         term_data_link(td);
1373         angband_term[i] = &td->t;
1374
1375         if (td->visible) {
1376             /* Activate the window */
1377             SetActiveWindow(td->w);
1378         }
1379
1380         if (td->posfix) {
1381             term_window_pos(td, HWND_TOPMOST);
1382         } else {
1383             term_window_pos(td, td->w);
1384         }
1385     }
1386
1387     /* Create main window */
1388     td = &data[0];
1389     my_td = td;
1390     td->w = CreateWindowEx(td->dwExStyle, AppName, td->s, td->dwStyle, td->pos_x, td->pos_y, td->size_wid, td->size_hgt, HWND_DESKTOP, NULL, hInstance, NULL);
1391     my_td = NULL;
1392
1393     if (!td->w)
1394         quit(_("メインウィンドウの作成に失敗しました", "Failed to create Angband window"));
1395
1396     /* Resize */
1397     td->size_hack = TRUE;
1398     term_getsize(td);
1399     term_window_resize(td);
1400     td->size_hack = FALSE;
1401
1402     term_data_link(td);
1403     angband_term[0] = &td->t;
1404     normsize.x = td->cols;
1405     normsize.y = td->rows;
1406
1407     if (win_maximized)
1408         ShowWindow(td->w, SW_SHOWMAXIMIZED);
1409     else
1410         ShowWindow(td->w, SW_SHOW);
1411
1412     SetWindowPos(td->w, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
1413     hbrYellow = CreateSolidBrush(win_clr[TERM_YELLOW]);
1414     (void)term_xtra_win_flush();
1415 }
1416
1417 /*
1418  * Prepare the menus
1419  */
1420 static void setup_menus(void)
1421 {
1422     HMENU hm = GetMenu(data[0].w);
1423
1424     if (current_world_ptr->character_generated) {
1425         EnableMenuItem(hm, IDM_FILE_NEW, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1426         EnableMenuItem(hm, IDM_FILE_OPEN, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1427         EnableMenuItem(hm, IDM_FILE_SAVE, MF_BYCOMMAND | MF_ENABLED);
1428     } else {
1429         EnableMenuItem(hm, IDM_FILE_NEW, MF_BYCOMMAND | MF_ENABLED);
1430         EnableMenuItem(hm, IDM_FILE_OPEN, MF_BYCOMMAND | MF_ENABLED);
1431         EnableMenuItem(hm, IDM_FILE_SAVE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1432     }
1433
1434     for (int i = 0; i < MAX_TERM_DATA; i++) {
1435         EnableMenuItem(hm, IDM_WINDOW_VIS_0 + i, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1436         CheckMenuItem(hm, IDM_WINDOW_VIS_0 + i, (data[i].visible ? MF_CHECKED : MF_UNCHECKED));
1437         EnableMenuItem(hm, IDM_WINDOW_VIS_0 + i, MF_BYCOMMAND | MF_ENABLED);
1438     }
1439
1440     for (int i = 0; i < MAX_TERM_DATA; i++) {
1441         EnableMenuItem(hm, IDM_WINDOW_FONT_0 + i, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1442
1443         if (data[i].visible) {
1444             EnableMenuItem(hm, IDM_WINDOW_FONT_0 + i, MF_BYCOMMAND | MF_ENABLED);
1445         }
1446     }
1447
1448     for (int i = 0; i < MAX_TERM_DATA; i++) {
1449         EnableMenuItem(hm, IDM_WINDOW_POS_0 + i, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1450         CheckMenuItem(hm, IDM_WINDOW_POS_0 + i, (data[i].posfix ? MF_CHECKED : MF_UNCHECKED));
1451         if (data[i].visible) {
1452             EnableMenuItem(hm, IDM_WINDOW_POS_0 + i, MF_BYCOMMAND | MF_ENABLED);
1453         }
1454     }
1455
1456     for (int i = 0; i < MAX_TERM_DATA; i++) {
1457         EnableMenuItem(hm, IDM_WINDOW_I_WID_0 + i, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1458         if (data[i].visible) {
1459             EnableMenuItem(hm, IDM_WINDOW_I_WID_0 + i, MF_BYCOMMAND | MF_ENABLED);
1460         }
1461     }
1462
1463     for (int i = 0; i < MAX_TERM_DATA; i++) {
1464         EnableMenuItem(hm, IDM_WINDOW_D_WID_0 + i, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1465         if (data[i].visible) {
1466             EnableMenuItem(hm, IDM_WINDOW_D_WID_0 + i, MF_BYCOMMAND | MF_ENABLED);
1467         }
1468     }
1469
1470     for (int i = 0; i < MAX_TERM_DATA; i++) {
1471         EnableMenuItem(hm, IDM_WINDOW_I_HGT_0 + i, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1472         if (data[i].visible) {
1473             EnableMenuItem(hm, IDM_WINDOW_I_HGT_0 + i, MF_BYCOMMAND | MF_ENABLED);
1474         }
1475     }
1476
1477     for (int i = 0; i < MAX_TERM_DATA; i++) {
1478         EnableMenuItem(hm, IDM_WINDOW_D_HGT_0 + i, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1479
1480         if (data[i].visible) {
1481             EnableMenuItem(hm, IDM_WINDOW_D_HGT_0 + i, MF_BYCOMMAND | MF_ENABLED);
1482         }
1483     }
1484     CheckMenuItem(hm, IDM_WINDOW_KEEP_SUBWINDOWS, (keep_subwindows ? MF_CHECKED : MF_UNCHECKED));
1485
1486     CheckMenuItem(hm, IDM_OPTIONS_NO_GRAPHICS, (arg_graphics == static_cast<byte>(graphics_mode::GRAPHICS_NONE) ? MF_CHECKED : MF_UNCHECKED));
1487     CheckMenuItem(hm, IDM_OPTIONS_OLD_GRAPHICS, (arg_graphics == static_cast<byte>(graphics_mode::GRAPHICS_ORIGINAL) ? MF_CHECKED : MF_UNCHECKED));
1488     CheckMenuItem(hm, IDM_OPTIONS_NEW_GRAPHICS, (arg_graphics == static_cast<byte>(graphics_mode::GRAPHICS_ADAM_BOLT) ? MF_CHECKED : MF_UNCHECKED));
1489     CheckMenuItem(hm, IDM_OPTIONS_NEW2_GRAPHICS, (arg_graphics == static_cast<byte>(graphics_mode::GRAPHICS_HENGBAND) ? MF_CHECKED : MF_UNCHECKED));
1490     CheckMenuItem(hm, IDM_OPTIONS_BIGTILE, (arg_bigtile ? MF_CHECKED : MF_UNCHECKED));
1491     CheckMenuItem(hm, IDM_OPTIONS_MUSIC, (arg_music ? MF_CHECKED : MF_UNCHECKED));
1492     CheckMenuItem(hm, IDM_OPTIONS_MUSIC_PAUSE_INACTIVE, (use_pause_music_inactive ? MF_CHECKED : MF_UNCHECKED));
1493     CheckMenuItem(hm, IDM_OPTIONS_SOUND, (arg_sound ? MF_CHECKED : MF_UNCHECKED));
1494     CheckMenuItem(hm, IDM_OPTIONS_NO_BG, ((current_bg_mode == bg_mode::BG_NONE) ? MF_CHECKED : MF_UNCHECKED));
1495     CheckMenuItem(hm, IDM_OPTIONS_BG, ((current_bg_mode == bg_mode::BG_ONE) ? MF_CHECKED : MF_UNCHECKED));
1496     CheckMenuItem(hm, IDM_OPTIONS_PRESET_BG, ((current_bg_mode == bg_mode::BG_PRESET) ? MF_CHECKED : MF_UNCHECKED));
1497     // TODO IDM_OPTIONS_PRESET_BG を有効にする
1498     EnableMenuItem(hm, IDM_OPTIONS_PRESET_BG, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1499 }
1500
1501 /*
1502  * @brief Check for double clicked (or dragged) savefile
1503  * @details
1504  * Apparently, Windows copies the entire filename into the first
1505  * piece of the "command line string".  Perhaps we should extract
1506  * the "basename" of that filename and append it to the "save" dir.
1507  * @param player_ptr pointer of player_type
1508  * @param savefile_option savefile path
1509  */
1510 static void check_for_save_file(player_type *player_ptr, const std::string &savefile_option)
1511 {
1512     if (savefile_option.empty())
1513         return;
1514
1515     strcpy(savefile, savefile_option.c_str());
1516     validate_file(savefile);
1517     game_in_progress = TRUE;
1518     play_game(player_ptr, FALSE, FALSE);
1519 }
1520
1521 /*
1522  * Process a menu command
1523  */
1524 static void process_menus(player_type *player_ptr, WORD wCmd)
1525 {
1526     if (!initialized) {
1527         plog(_("まだ初期化中です...", "You cannot do that yet..."));
1528         return;
1529     }
1530
1531     term_data *td;
1532     OPENFILENAME ofn;
1533     switch (wCmd) {
1534     case IDM_FILE_NEW: {
1535         if (game_in_progress) {
1536             plog(_("プレイ中は新しいゲームを始めることができません!", "You can't start a new game while you're still playing!"));
1537         } else {
1538             game_in_progress = TRUE;
1539             term_flush();
1540             strcpy(savefile, "");
1541             play_game(player_ptr, TRUE, FALSE);
1542             quit(NULL);
1543         }
1544
1545         break;
1546     }
1547     case IDM_FILE_OPEN: {
1548         if (game_in_progress) {
1549             plog(_("プレイ中はゲームをロードすることができません!", "You can't open a new game while you're still playing!"));
1550         } else {
1551             memset(&ofn, 0, sizeof(ofn));
1552             ofn.lStructSize = sizeof(ofn);
1553             ofn.hwndOwner = data[0].w;
1554             ofn.lpstrFilter = "Save Files (*.)\0*\0";
1555             ofn.nFilterIndex = 1;
1556             ofn.lpstrFile = savefile;
1557             ofn.nMaxFile = 1024;
1558             ofn.lpstrInitialDir = ANGBAND_DIR_SAVE;
1559             ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_HIDEREADONLY;
1560
1561             if (GetOpenFileName(&ofn)) {
1562                 validate_file(savefile);
1563                 game_in_progress = TRUE;
1564                 term_flush();
1565                 play_game(player_ptr, FALSE, FALSE);
1566                 quit(NULL);
1567             }
1568         }
1569
1570         break;
1571     }
1572     case IDM_FILE_SAVE: {
1573         if (game_in_progress && current_world_ptr->character_generated) {
1574             if (!can_save) {
1575                 plog(_("今はセーブすることは出来ません。", "You may not do that right now."));
1576                 break;
1577             }
1578
1579             msg_flag = FALSE;
1580             do_cmd_save_game(player_ptr, FALSE);
1581         } else {
1582             plog(_("今、セーブすることは出来ません。", "You may not do that right now."));
1583         }
1584
1585         break;
1586     }
1587     case IDM_FILE_EXIT: {
1588         if (game_in_progress && current_world_ptr->character_generated) {
1589             if (!can_save) {
1590                 plog(_("今は終了できません。", "You may not do that right now."));
1591                 break;
1592             }
1593
1594             msg_flag = FALSE;
1595             forget_lite(player_ptr->current_floor_ptr);
1596             forget_view(player_ptr->current_floor_ptr);
1597             clear_mon_lite(player_ptr->current_floor_ptr);
1598
1599             term_key_push(SPECIAL_KEY_QUIT);
1600             break;
1601         }
1602
1603         quit(NULL);
1604         break;
1605     }
1606     case IDM_FILE_SCORE: {
1607         char buf[1024];
1608         path_build(buf, sizeof(buf), ANGBAND_DIR_APEX, "scores.raw");
1609         highscore_fd = fd_open(buf, O_RDONLY);
1610         if (highscore_fd < 0) {
1611             msg_print("Score file unavailable.");
1612         } else {
1613             screen_save();
1614             term_clear();
1615             display_scores_aux(0, MAX_HISCORES, -1, NULL);
1616             (void)fd_close(highscore_fd);
1617             highscore_fd = -1;
1618             screen_load();
1619             term_fresh();
1620         }
1621
1622         break;
1623     }
1624     case IDM_FILE_MOVIE: {
1625         if (game_in_progress) {
1626             plog(_("プレイ中はムービーをロードすることができません!", "You can't open a movie while you're playing!"));
1627         } else {
1628             memset(&ofn, 0, sizeof(ofn));
1629             ofn.lStructSize = sizeof(ofn);
1630             ofn.hwndOwner = data[0].w;
1631             ofn.lpstrFilter = "Angband Movie Files (*.amv)\0*.amv\0";
1632             ofn.nFilterIndex = 1;
1633             ofn.lpstrFile = savefile;
1634             ofn.nMaxFile = 1024;
1635             ofn.lpstrInitialDir = ANGBAND_DIR_USER;
1636             ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
1637
1638             if (GetOpenFileName(&ofn)) {
1639                 prepare_browse_movie_without_path_build(savefile);
1640                 play_game(player_ptr, FALSE, TRUE);
1641                 quit(NULL);
1642                 return;
1643             }
1644         }
1645
1646         break;
1647     }
1648     case IDM_WINDOW_VIS_0: {
1649         plog(_("メインウィンドウは非表示にできません!", "You are not allowed to do that!"));
1650         break;
1651     }
1652     case IDM_WINDOW_VIS_1:
1653     case IDM_WINDOW_VIS_2:
1654     case IDM_WINDOW_VIS_3:
1655     case IDM_WINDOW_VIS_4:
1656     case IDM_WINDOW_VIS_5:
1657     case IDM_WINDOW_VIS_6:
1658     case IDM_WINDOW_VIS_7: {
1659         int i = wCmd - IDM_WINDOW_VIS_0;
1660         if ((i < 0) || (i >= MAX_TERM_DATA))
1661             break;
1662
1663         td = &data[i];
1664         if (!td->visible) {
1665             td->visible = TRUE;
1666             ShowWindow(td->w, SW_SHOW);
1667             term_data_redraw(td);
1668         } else {
1669             td->visible = FALSE;
1670             td->posfix = FALSE;
1671             ShowWindow(td->w, SW_HIDE);
1672         }
1673
1674         break;
1675     }
1676     case IDM_WINDOW_FONT_0:
1677     case IDM_WINDOW_FONT_1:
1678     case IDM_WINDOW_FONT_2:
1679     case IDM_WINDOW_FONT_3:
1680     case IDM_WINDOW_FONT_4:
1681     case IDM_WINDOW_FONT_5:
1682     case IDM_WINDOW_FONT_6:
1683     case IDM_WINDOW_FONT_7: {
1684         int i = wCmd - IDM_WINDOW_FONT_0;
1685         if ((i < 0) || (i >= MAX_TERM_DATA))
1686             break;
1687
1688         td = &data[i];
1689         term_change_font(td);
1690         break;
1691     }
1692     case IDM_WINDOW_POS_1:
1693     case IDM_WINDOW_POS_2:
1694     case IDM_WINDOW_POS_3:
1695     case IDM_WINDOW_POS_4:
1696     case IDM_WINDOW_POS_5:
1697     case IDM_WINDOW_POS_6:
1698     case IDM_WINDOW_POS_7: {
1699         int i = wCmd - IDM_WINDOW_POS_0;
1700         if ((i < 0) || (i >= MAX_TERM_DATA))
1701             break;
1702
1703         td = &data[i];
1704         if (!td->posfix && td->visible) {
1705             td->posfix = TRUE;
1706             term_window_pos(td, HWND_TOPMOST);
1707         } else {
1708             td->posfix = FALSE;
1709             term_window_pos(td, data[0].w);
1710         }
1711
1712         break;
1713     }
1714     case IDM_WINDOW_I_WID_0:
1715     case IDM_WINDOW_I_WID_1:
1716     case IDM_WINDOW_I_WID_2:
1717     case IDM_WINDOW_I_WID_3:
1718     case IDM_WINDOW_I_WID_4:
1719     case IDM_WINDOW_I_WID_5:
1720     case IDM_WINDOW_I_WID_6:
1721     case IDM_WINDOW_I_WID_7: {
1722         int i = wCmd - IDM_WINDOW_I_WID_0;
1723         if ((i < 0) || (i >= MAX_TERM_DATA))
1724             break;
1725
1726         td = &data[i];
1727         td->tile_wid += 1;
1728         term_getsize(td);
1729         term_window_resize(td);
1730         break;
1731     }
1732     case IDM_WINDOW_D_WID_0:
1733     case IDM_WINDOW_D_WID_1:
1734     case IDM_WINDOW_D_WID_2:
1735     case IDM_WINDOW_D_WID_3:
1736     case IDM_WINDOW_D_WID_4:
1737     case IDM_WINDOW_D_WID_5:
1738     case IDM_WINDOW_D_WID_6:
1739     case IDM_WINDOW_D_WID_7: {
1740         int i = wCmd - IDM_WINDOW_D_WID_0;
1741         if ((i < 0) || (i >= MAX_TERM_DATA))
1742             break;
1743
1744         td = &data[i];
1745         td->tile_wid -= 1;
1746         term_getsize(td);
1747         term_window_resize(td);
1748         break;
1749     }
1750     case IDM_WINDOW_I_HGT_0:
1751     case IDM_WINDOW_I_HGT_1:
1752     case IDM_WINDOW_I_HGT_2:
1753     case IDM_WINDOW_I_HGT_3:
1754     case IDM_WINDOW_I_HGT_4:
1755     case IDM_WINDOW_I_HGT_5:
1756     case IDM_WINDOW_I_HGT_6:
1757     case IDM_WINDOW_I_HGT_7: {
1758         int i = wCmd - IDM_WINDOW_I_HGT_0;
1759         if ((i < 0) || (i >= MAX_TERM_DATA))
1760             break;
1761
1762         td = &data[i];
1763         td->tile_hgt += 1;
1764         term_getsize(td);
1765         term_window_resize(td);
1766         break;
1767     }
1768     case IDM_WINDOW_D_HGT_0:
1769     case IDM_WINDOW_D_HGT_1:
1770     case IDM_WINDOW_D_HGT_2:
1771     case IDM_WINDOW_D_HGT_3:
1772     case IDM_WINDOW_D_HGT_4:
1773     case IDM_WINDOW_D_HGT_5:
1774     case IDM_WINDOW_D_HGT_6:
1775     case IDM_WINDOW_D_HGT_7: {
1776         int i = wCmd - IDM_WINDOW_D_HGT_0;
1777         if ((i < 0) || (i >= MAX_TERM_DATA))
1778             break;
1779
1780         td = &data[i];
1781         td->tile_hgt -= 1;
1782         term_getsize(td);
1783         term_window_resize(td);
1784         break;
1785     }
1786     case IDM_WINDOW_KEEP_SUBWINDOWS: {
1787         keep_subwindows = !keep_subwindows;
1788         break;
1789     }
1790     case IDM_OPTIONS_NO_GRAPHICS: {
1791         if (arg_graphics != static_cast<byte>(graphics_mode::GRAPHICS_NONE)) {
1792             arg_graphics = static_cast<byte>(graphics_mode::GRAPHICS_NONE);
1793             if (game_in_progress)
1794                 do_cmd_redraw(player_ptr);
1795         }
1796         break;
1797     }
1798     case IDM_OPTIONS_OLD_GRAPHICS: {
1799         if (arg_graphics != static_cast<byte>(graphics_mode::GRAPHICS_ORIGINAL)) {
1800             arg_graphics = static_cast<byte>(graphics_mode::GRAPHICS_ORIGINAL);
1801             if (game_in_progress)
1802                 do_cmd_redraw(player_ptr);
1803         }
1804
1805         break;
1806     }
1807     case IDM_OPTIONS_NEW_GRAPHICS: {
1808         if (arg_graphics != static_cast<byte>(graphics_mode::GRAPHICS_ADAM_BOLT)) {
1809             arg_graphics = static_cast<byte>(graphics_mode::GRAPHICS_ADAM_BOLT);
1810             if (game_in_progress)
1811                 do_cmd_redraw(player_ptr);
1812         }
1813
1814         break;
1815     }
1816     case IDM_OPTIONS_NEW2_GRAPHICS: {
1817         if (arg_graphics != static_cast<byte>(graphics_mode::GRAPHICS_HENGBAND)) {
1818             arg_graphics = static_cast<byte>(graphics_mode::GRAPHICS_HENGBAND);
1819             if (game_in_progress)
1820                 do_cmd_redraw(player_ptr);
1821         }
1822
1823         break;
1824     }
1825     case IDM_OPTIONS_BIGTILE: {
1826         td = &data[0];
1827         arg_bigtile = !arg_bigtile;
1828         term_activate(&td->t);
1829         term_resize(td->cols, td->rows);
1830         InvalidateRect(td->w, NULL, TRUE);
1831         break;
1832     }
1833     case IDM_OPTIONS_MUSIC: {
1834         arg_music = !arg_music;
1835         use_music = arg_music;
1836         if (use_music) {
1837             init_music();
1838             if (game_in_progress)
1839                 select_floor_music(player_ptr);
1840         } else {
1841             main_win_music::stop_music();
1842         }
1843         break;
1844     }
1845     case IDM_OPTIONS_MUSIC_PAUSE_INACTIVE: {
1846         use_pause_music_inactive = !use_pause_music_inactive;
1847         break;
1848     }
1849     case IDM_OPTIONS_OPEN_MUSIC_DIR: {
1850         std::vector<char> buf(MAIN_WIN_MAX_PATH);
1851         path_build(&buf[0], MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_MUSIC, "music.cfg");
1852         open_dir_in_explorer(&buf[0]);
1853         break;
1854     }
1855     case IDM_OPTIONS_SOUND: {
1856         arg_sound = !arg_sound;
1857         change_sound_mode(arg_sound);
1858         break;
1859     }
1860     case IDM_OPTIONS_OPEN_SOUND_DIR: {
1861         std::vector<char> buf(MAIN_WIN_MAX_PATH);
1862         path_build(&buf[0], MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_SOUND, "sound.cfg");
1863         open_dir_in_explorer(&buf[0]);
1864         break;
1865     }
1866     case IDM_OPTIONS_NO_BG: {
1867         change_bg_mode(bg_mode::BG_NONE);
1868         break;
1869     }
1870     case IDM_OPTIONS_PRESET_BG: {
1871         change_bg_mode(bg_mode::BG_PRESET);
1872         break;
1873     }
1874     case IDM_OPTIONS_BG: {
1875         if (change_bg_mode(bg_mode::BG_ONE))
1876             break;
1877         // 壁紙の設定に失敗した(ファイルが存在しない等)場合、壁紙に使うファイルを選択させる
1878     }
1879         [[fallthrough]]; /* Fall through */
1880     case IDM_OPTIONS_OPEN_BG: {
1881         memset(&ofn, 0, sizeof(ofn));
1882         ofn.lStructSize = sizeof(ofn);
1883         ofn.hwndOwner = data[0].w;
1884         ofn.lpstrFilter = "Image Files (*.bmp;*.png;*.jpg;*.jpeg;)\0*.bmp;*.png;*.jpg;*.jpeg;\0";
1885         ofn.nFilterIndex = 1;
1886         ofn.lpstrFile = wallpaper_file;
1887         ofn.nMaxFile = 1023;
1888         ofn.lpstrInitialDir = NULL;
1889         ofn.lpstrTitle = _("壁紙を選んでね。", "Choose wall paper.");
1890         ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
1891
1892         if (GetOpenFileName(&ofn)) {
1893             change_bg_mode(bg_mode::BG_ONE, true, true);
1894         }
1895         break;
1896     }
1897     case IDM_DUMP_SCREEN_HTML: {
1898         save_screen_as_html(data[0].w);
1899         break;
1900     }
1901     }
1902 }
1903
1904 /*
1905  * Add a keypress to the "queue"
1906  */
1907 static errr term_keypress(int k)
1908 {
1909     /* Refuse to enqueue non-keys */
1910     if (!k)
1911         return -1;
1912
1913     /* Store the char, advance the queue */
1914     Term->key_queue[Term->key_head++] = (char)k;
1915
1916     /* Circular queue, handle wrap */
1917     if (Term->key_head == Term->key_size)
1918         Term->key_head = 0;
1919
1920     if (Term->key_head != Term->key_tail)
1921         return 0;
1922
1923     return 1;
1924 }
1925
1926 static bool process_keydown(WPARAM wParam, LPARAM lParam)
1927 {
1928     bool mc = FALSE;
1929     bool ms = FALSE;
1930     bool ma = FALSE;
1931
1932     if (GetKeyState(VK_CONTROL) & 0x8000)
1933         mc = TRUE;
1934     if (GetKeyState(VK_SHIFT) & 0x8000)
1935         ms = TRUE;
1936     if (GetKeyState(VK_MENU) & 0x8000)
1937         ma = TRUE;
1938
1939     term_no_press = (ma) ? TRUE : FALSE;
1940     if (special_key[(byte)(wParam)] || (ma && !ignore_key[(byte)(wParam)])) {
1941         bool ext_key = (lParam & 0x1000000L) ? TRUE : FALSE;
1942         bool numpad = FALSE;
1943
1944         term_keypress(31);
1945         if (mc)
1946             term_keypress('C');
1947         if (ms)
1948             term_keypress('S');
1949         if (ma)
1950             term_keypress('A');
1951
1952         int i = LOBYTE(HIWORD(lParam));
1953         term_keypress('x');
1954         switch (wParam) {
1955         case VK_DIVIDE:
1956             term_no_press = TRUE;
1957             [[fallthrough]]; /* Fall through */
1958         case VK_RETURN:
1959             numpad = ext_key;
1960             break;
1961         case VK_NUMPAD0:
1962         case VK_NUMPAD1:
1963         case VK_NUMPAD2:
1964         case VK_NUMPAD3:
1965         case VK_NUMPAD4:
1966         case VK_NUMPAD5:
1967         case VK_NUMPAD6:
1968         case VK_NUMPAD7:
1969         case VK_NUMPAD8:
1970         case VK_NUMPAD9:
1971         case VK_ADD:
1972         case VK_MULTIPLY:
1973         case VK_SUBTRACT:
1974         case VK_SEPARATOR:
1975         case VK_DECIMAL:
1976             term_no_press = TRUE;
1977             [[fallthrough]]; /* Fall through */
1978         case VK_CLEAR:
1979         case VK_HOME:
1980         case VK_END:
1981         case VK_PRIOR:
1982         case VK_NEXT:
1983         case VK_INSERT:
1984         case VK_DELETE:
1985         case VK_UP:
1986         case VK_DOWN:
1987         case VK_LEFT:
1988         case VK_RIGHT:
1989             numpad = !ext_key;
1990         }
1991
1992         if (numpad)
1993             term_keypress('K');
1994
1995         term_keypress(hexsym[i / 16]);
1996         term_keypress(hexsym[i % 16]);
1997         term_keypress(13);
1998
1999         return 1;
2000     }
2001
2002     return 0;
2003 }
2004
2005 static void handle_app_active(HWND hWnd, UINT uMsg, WPARAM wParam, [[maybe_unused]] LPARAM lParam)
2006 {
2007     switch (uMsg) {
2008     case WM_ACTIVATEAPP: {
2009         if (wParam) {
2010             if (use_pause_music_inactive)
2011                 main_win_music::resume_music();
2012         } else {
2013             if (use_pause_music_inactive)
2014                 main_win_music::pause_music();
2015         }
2016         break;
2017     }
2018     case WM_WINDOWPOSCHANGING: {
2019         if (!IsIconic(hWnd))
2020             if (use_pause_music_inactive)
2021                 main_win_music::resume_music();
2022         break;
2023     }
2024     }
2025 }
2026
2027 /*!
2028  * @brief メインウインドウ用ウインドウプロシージャ
2029  */
2030 LRESULT PASCAL AngbandWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2031 {
2032     PAINTSTRUCT ps;
2033     term_data *td;
2034     td = (term_data *)GetWindowLong(hWnd, 0);
2035
2036     handle_app_active(hWnd, uMsg, wParam, lParam);
2037
2038     switch (uMsg) {
2039     case WM_NCCREATE: {
2040         SetWindowLong(hWnd, 0, (LONG)(my_td));
2041         break;
2042     }
2043     case WM_CREATE: {
2044         setup_mci(hWnd);
2045         return 0;
2046     }
2047     case WM_GETMINMAXINFO: {
2048         MINMAXINFO *lpmmi;
2049         RECT rc;
2050
2051         lpmmi = (MINMAXINFO *)lParam;
2052         if (!td)
2053             return 1;
2054
2055         rc.left = rc.top = 0;
2056         rc.right = rc.left + 80 * td->tile_wid + td->size_ow1 + td->size_ow2;
2057         rc.bottom = rc.top + 24 * td->tile_hgt + td->size_oh1 + td->size_oh2 + 1;
2058
2059         AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle);
2060
2061         lpmmi->ptMinTrackSize.x = rc.right - rc.left;
2062         lpmmi->ptMinTrackSize.y = rc.bottom - rc.top;
2063
2064         return 0;
2065     }
2066     case WM_ERASEBKGND: {
2067         return 1;
2068     }
2069     case WM_PAINT: {
2070         BeginPaint(hWnd, &ps);
2071         if (td)
2072             term_data_redraw(td);
2073         EndPaint(hWnd, &ps);
2074         ValidateRect(hWnd, NULL);
2075         return 0;
2076     }
2077     case MM_MCINOTIFY: {
2078         main_win_music::on_mci_notify(wParam, lParam);
2079
2080         return 0;
2081     }
2082     case WM_SYSKEYDOWN:
2083     case WM_KEYDOWN: {
2084         if (process_keydown(wParam, lParam))
2085             return 0;
2086         break;
2087     }
2088     case WM_CHAR: {
2089         if (term_no_press)
2090             term_no_press = FALSE;
2091         else
2092             term_keypress(wParam);
2093         return 0;
2094     }
2095     case WM_LBUTTONDOWN: {
2096         mousex = MIN(LOWORD(lParam) / td->tile_wid, td->cols - 1);
2097         mousey = MIN(HIWORD(lParam) / td->tile_hgt, td->rows - 1);
2098         mouse_down = TRUE;
2099         oldx = mousex;
2100         oldy = mousey;
2101         return 0;
2102     }
2103     case WM_LBUTTONUP: {
2104         HGLOBAL hGlobal;
2105         LPSTR lpStr;
2106         TERM_LEN dx = abs(oldx - mousex) + 1;
2107         TERM_LEN dy = abs(oldy - mousey) + 1;
2108         TERM_LEN ox = (oldx > mousex) ? mousex : oldx;
2109         TERM_LEN oy = (oldy > mousey) ? mousey : oldy;
2110
2111         mouse_down = FALSE;
2112         paint_rect = FALSE;
2113
2114 #ifdef JP
2115         int sz = (dx + 3) * dy;
2116 #else
2117         int sz = (dx + 2) * dy;
2118 #endif
2119         hGlobal = GlobalAlloc(GHND, sz + 1);
2120         if (hGlobal == NULL)
2121             return 0;
2122         lpStr = (LPSTR)GlobalLock(hGlobal);
2123
2124         for (int i = 0; i < dy; i++) {
2125 #ifdef JP
2126             char *s;
2127             const auto &scr = data[0].t.scr->c;
2128
2129             C_MAKE(s, (dx + 1), char);
2130             strncpy(s, &scr[oy + i][ox], dx);
2131
2132             if (ox > 0) {
2133                 if (iskanji(scr[oy + i][ox - 1]))
2134                     s[0] = ' ';
2135             }
2136
2137             if (ox + dx < data[0].cols) {
2138                 if (iskanji(scr[oy + i][ox + dx - 1]))
2139                     s[dx - 1] = ' ';
2140             }
2141
2142             for (int j = 0; j < dx; j++) {
2143                 if (s[j] == 127)
2144                     s[j] = '#';
2145                 *lpStr++ = s[j];
2146             }
2147 #else
2148             for (int j = 0; j < dx; j++) {
2149                 *lpStr++ = data[0].t.scr->c[oy + i][ox + j];
2150             }
2151 #endif
2152             if (dy > 1) {
2153                 *lpStr++ = '\r';
2154                 *lpStr++ = '\n';
2155             }
2156         }
2157
2158         GlobalUnlock(hGlobal);
2159         if (OpenClipboard(hWnd) == 0) {
2160             GlobalFree(hGlobal);
2161             return 0;
2162         }
2163
2164         EmptyClipboard();
2165         SetClipboardData(CF_TEXT, hGlobal);
2166         CloseClipboard();
2167         term_redraw();
2168         return 0;
2169     }
2170     case WM_MOUSEMOVE: {
2171         if (!mouse_down)
2172             return 0;
2173
2174         int dx, dy;
2175         int cx = MIN(LOWORD(lParam) / td->tile_wid, td->cols - 1);
2176         int cy = MIN(HIWORD(lParam) / td->tile_hgt, td->rows - 1);
2177         int ox, oy;
2178
2179         if (paint_rect) {
2180             dx = abs(oldx - mousex) + 1;
2181             dy = abs(oldy - mousey) + 1;
2182             ox = (oldx > mousex) ? mousex : oldx;
2183             oy = (oldy > mousey) ? mousey : oldy;
2184             term_inversed_area(hWnd, ox, oy, dx, dy);
2185         } else {
2186             paint_rect = TRUE;
2187         }
2188
2189         dx = abs(cx - mousex) + 1;
2190         dy = abs(cy - mousey) + 1;
2191         ox = (cx > mousex) ? mousex : cx;
2192         oy = (cy > mousey) ? mousey : cy;
2193         term_inversed_area(hWnd, ox, oy, dx, dy);
2194
2195         oldx = cx;
2196         oldy = cy;
2197         return 0;
2198     }
2199     case WM_INITMENU: {
2200         setup_menus();
2201         return 0;
2202     }
2203     case WM_CLOSE: {
2204         if (!game_in_progress || !current_world_ptr->character_generated) {
2205             quit(NULL);
2206             return 0;
2207         }
2208
2209         if (!can_save) {
2210             plog(_("今は終了できません。", "You may not do that right now."));
2211             return 0;
2212         }
2213
2214         msg_flag = FALSE;
2215         forget_lite(p_ptr->current_floor_ptr);
2216         forget_view(p_ptr->current_floor_ptr);
2217         clear_mon_lite(p_ptr->current_floor_ptr);
2218         term_key_push(SPECIAL_KEY_QUIT);
2219         return 0;
2220     }
2221     case WM_QUERYENDSESSION: {
2222         if (!game_in_progress || !current_world_ptr->character_generated) {
2223             quit(NULL);
2224             return 0;
2225         }
2226
2227         msg_flag = FALSE;
2228         if (p_ptr->chp < 0)
2229             p_ptr->is_dead = FALSE;
2230         exe_write_diary(p_ptr, DIARY_GAMESTART, 0, _("----ゲーム中断----", "---- Save and Exit Game ----"));
2231
2232         p_ptr->panic_save = 1;
2233         signals_ignore_tstp();
2234         (void)strcpy(p_ptr->died_from, _("(緊急セーブ)", "(panic save)"));
2235         (void)save_player(p_ptr, SAVE_TYPE_CLOSE_GAME);
2236         quit(NULL);
2237         return 0;
2238     }
2239     case WM_QUIT: {
2240         quit(NULL);
2241         return 0;
2242     }
2243     case WM_COMMAND: {
2244         process_menus(p_ptr, LOWORD(wParam));
2245         return 0;
2246     }
2247     case WM_SIZE: {
2248         if (!td)
2249             return 1;
2250         if (!td->w)
2251             return 1;
2252         if (td->size_hack)
2253             return 1;
2254
2255         //!< @todo 二重のswitch文。後で分割する.
2256         switch (wParam) {
2257         case SIZE_MINIMIZED: {
2258             for (int i = 1; i < MAX_TERM_DATA; i++) {
2259                 if (data[i].visible)
2260                     ShowWindow(data[i].w, SW_HIDE);
2261             }
2262
2263             return 0;
2264         }
2265         case SIZE_MAXIMIZED:
2266         case SIZE_RESTORED: {
2267             TERM_LEN cols = (LOWORD(lParam) - td->size_ow1) / td->tile_wid;
2268             TERM_LEN rows = (HIWORD(lParam) - td->size_oh1) / td->tile_hgt;
2269             if ((td->cols != cols) || (td->rows != rows)) {
2270                 td->cols = cols;
2271                 td->rows = rows;
2272                 if (!IsZoomed(td->w) && !IsIconic(td->w)) {
2273                     normsize.x = td->cols;
2274                     normsize.y = td->rows;
2275                 }
2276
2277                 term_activate(&td->t);
2278                 term_resize(td->cols, td->rows);
2279                 InvalidateRect(td->w, NULL, TRUE);
2280             }
2281
2282             td->size_hack = TRUE;
2283             for (int i = 1; i < MAX_TERM_DATA; i++) {
2284                 if (data[i].visible)
2285                     ShowWindow(data[i].w, SW_SHOW);
2286             }
2287
2288             td->size_hack = FALSE;
2289
2290             return 0;
2291         }
2292         }
2293
2294         break;
2295     }
2296     case WM_ACTIVATE: {
2297         if (!wParam || HIWORD(lParam))
2298             break;
2299
2300         for (int i = 1; i < MAX_TERM_DATA; i++) {
2301             if (!data[i].posfix)
2302                 term_window_pos(&data[i], hWnd);
2303         }
2304
2305         SetFocus(hWnd);
2306         return 0;
2307     }
2308     case WM_ACTIVATEAPP: {
2309         if (IsIconic(td->w))
2310             break;
2311
2312         for (int i = 1; i < MAX_TERM_DATA; i++) {
2313             if (data[i].visible) {
2314                 if (wParam == TRUE) {
2315                     ShowWindow(data[i].w, SW_SHOW);
2316                 } else {
2317                     ShowWindow(data[i].w, SW_HIDE);
2318                 }
2319             }
2320         }
2321     }
2322         [[fallthrough]]; /* Fall through */
2323     case WM_ENABLE: {
2324         if (wParam == FALSE && keep_subwindows) {
2325             for (int i = 0; i < MAX_TERM_DATA; i++) {
2326                 if (data[i].visible) {
2327                     ShowWindow(data[i].w, SW_SHOW);
2328                 }
2329             }
2330         }
2331     }
2332     }
2333
2334     return DefWindowProc(hWnd, uMsg, wParam, lParam);
2335 }
2336
2337 /*!
2338  * @brief サブウインドウ用ウインドウプロシージャ
2339  */
2340 LRESULT PASCAL AngbandListProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2341 {
2342     term_data *td;
2343     PAINTSTRUCT ps;
2344     td = (term_data *)GetWindowLong(hWnd, 0);
2345
2346     switch (uMsg) {
2347     case WM_NCCREATE: {
2348         SetWindowLong(hWnd, 0, (LONG)(my_td));
2349         break;
2350     }
2351     case WM_CREATE: {
2352         return 0;
2353     }
2354     case WM_GETMINMAXINFO: {
2355         MINMAXINFO *lpmmi;
2356         RECT rc;
2357
2358         lpmmi = (MINMAXINFO *)lParam;
2359         if (!td)
2360             return 1;
2361
2362         rc.left = rc.top = 0;
2363         rc.right = rc.left + 20 * td->tile_wid + td->size_ow1 + td->size_ow2;
2364         rc.bottom = rc.top + 3 * td->tile_hgt + td->size_oh1 + td->size_oh2 + 1;
2365
2366         AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle);
2367         lpmmi->ptMinTrackSize.x = rc.right - rc.left;
2368         lpmmi->ptMinTrackSize.y = rc.bottom - rc.top;
2369         return 0;
2370     }
2371     case WM_SIZE: {
2372         if (!td)
2373             return 1;
2374         if (!td->w)
2375             return 1;
2376         if (td->size_hack)
2377             return 1;
2378
2379         td->size_hack = TRUE;
2380
2381         TERM_LEN cols = (LOWORD(lParam) - td->size_ow1) / td->tile_wid;
2382         TERM_LEN rows = (HIWORD(lParam) - td->size_oh1) / td->tile_hgt;
2383         if ((td->cols != cols) || (td->rows != rows)) {
2384             term_type *old_term = Term;
2385             td->cols = cols;
2386             td->rows = rows;
2387             term_activate(&td->t);
2388             term_resize(td->cols, td->rows);
2389             term_activate(old_term);
2390             InvalidateRect(td->w, NULL, TRUE);
2391             p_ptr->window_flags = 0xFFFFFFFF;
2392             handle_stuff(p_ptr);
2393         }
2394
2395         td->size_hack = FALSE;
2396         return 0;
2397     }
2398     case WM_ERASEBKGND: {
2399         return 1;
2400     }
2401     case WM_PAINT: {
2402         BeginPaint(hWnd, &ps);
2403         if (td)
2404             term_data_redraw(td);
2405         EndPaint(hWnd, &ps);
2406         return 0;
2407     }
2408     case WM_SYSKEYDOWN:
2409     case WM_KEYDOWN: {
2410         if (process_keydown(wParam, lParam))
2411             return 0;
2412
2413         break;
2414     }
2415     case WM_CHAR: {
2416         if (term_no_press)
2417             term_no_press = FALSE;
2418         else
2419             term_keypress(wParam);
2420         return 0;
2421     }
2422     case WM_NCLBUTTONDOWN: {
2423         if (wParam == HTCLOSE)
2424             wParam = HTSYSMENU;
2425
2426         if (wParam == HTSYSMENU) {
2427             if (td->visible) {
2428                 td->visible = FALSE;
2429                 ShowWindow(td->w, SW_HIDE);
2430             }
2431
2432             return 0;
2433         }
2434
2435         break;
2436     }
2437     }
2438
2439     return DefWindowProc(hWnd, uMsg, wParam, lParam);
2440 }
2441
2442 /*
2443  * Display warning message (see "z-util.c")
2444  */
2445 static void hook_plog(concptr str)
2446 {
2447     if (str) {
2448         MessageBoxW(data[0].w, to_wchar(str).wc_str(), _(L"警告!", L"Warning"), MB_ICONEXCLAMATION | MB_OK);
2449     }
2450 }
2451
2452 /*
2453  * Display error message and quit (see "z-util.c")
2454  */
2455 static void hook_quit(concptr str)
2456 {
2457     if (str) {
2458         MessageBoxW(data[0].w, to_wchar(str).wc_str(), _(L"エラー!", L"Error"), MB_ICONEXCLAMATION | MB_OK | MB_ICONSTOP);
2459     }
2460
2461     save_prefs();
2462     for (int i = MAX_TERM_DATA - 1; i >= 0; --i) {
2463         term_force_font(&data[i]);
2464         if (data[i].font_want)
2465             string_free(data[i].font_want);
2466         if (data[i].w)
2467             DestroyWindow(data[i].w);
2468         data[i].w = 0;
2469     }
2470
2471     DeleteObject(hbrYellow);
2472     finalize_bg();
2473     graphic.finalize();
2474
2475     UnregisterClass(AppName, hInstance);
2476     if (hIcon)
2477         DestroyIcon(hIcon);
2478
2479     exit(0);
2480 }
2481
2482 /*
2483  * Init some stuff
2484  */
2485 static void init_stuff(void)
2486 {
2487     char path[MAIN_WIN_MAX_PATH];
2488     DWORD path_len = GetModuleFileName(hInstance, path, MAIN_WIN_MAX_PATH);
2489     strcpy(path + path_len - 4, ".INI");
2490     ini_file = string_make(path);
2491
2492     int i = path_len;
2493     for (; i > 0; i--) {
2494         if (path[i] == '\\') {
2495             break;
2496         }
2497     }
2498
2499     strcpy(path + i + 1, "lib\\");
2500     validate_dir(path, TRUE);
2501     init_file_paths(path, path);
2502     validate_dir(ANGBAND_DIR_APEX, FALSE);
2503     validate_dir(ANGBAND_DIR_BONE, FALSE);
2504     if (!check_dir(ANGBAND_DIR_EDIT)) {
2505         validate_dir(ANGBAND_DIR_DATA, TRUE);
2506     } else {
2507         validate_dir(ANGBAND_DIR_DATA, FALSE);
2508     }
2509
2510     validate_dir(ANGBAND_DIR_FILE, TRUE);
2511     validate_dir(ANGBAND_DIR_HELP, FALSE);
2512     validate_dir(ANGBAND_DIR_INFO, FALSE);
2513     validate_dir(ANGBAND_DIR_PREF, TRUE);
2514     validate_dir(ANGBAND_DIR_SAVE, FALSE);
2515     validate_dir(ANGBAND_DIR_DEBUG_SAVE, FALSE);
2516     validate_dir(ANGBAND_DIR_USER, TRUE);
2517     validate_dir(ANGBAND_DIR_XTRA, TRUE);
2518     path_build(path, sizeof(path), ANGBAND_DIR_FILE, _("news_j.txt", "news.txt"));
2519
2520     validate_file(path);
2521     path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "graf");
2522     ANGBAND_DIR_XTRA_GRAF = string_make(path);
2523     validate_dir(ANGBAND_DIR_XTRA_GRAF, TRUE);
2524
2525     path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "sound");
2526     ANGBAND_DIR_XTRA_SOUND = string_make(path);
2527     validate_dir(ANGBAND_DIR_XTRA_SOUND, FALSE);
2528
2529     path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "music");
2530     ANGBAND_DIR_XTRA_MUSIC = string_make(path);
2531     validate_dir(ANGBAND_DIR_XTRA_MUSIC, FALSE);
2532
2533     for (i = 0; special_key_list[i]; ++i) {
2534         special_key[special_key_list[i]] = TRUE;
2535     }
2536
2537     for (i = 0; ignore_key_list[i]; ++i) {
2538         ignore_key[ignore_key_list[i]] = TRUE;
2539     }
2540
2541     ANGBAND_SYS = "win";
2542     if (7 != GetKeyboardType(0))
2543         ANGBAND_KEYBOARD = "0";
2544     else {
2545         switch (GetKeyboardType(1)) {
2546         case 0x0D01:
2547         case 0x0D02:
2548         case 0x0D03:
2549         case 0x0D04:
2550         case 0x0D05:
2551         case 0x0D06:
2552             /* NEC PC-98x1 */
2553             ANGBAND_KEYBOARD = "NEC98";
2554             break;
2555         default:
2556             /* PC/AT */
2557             ANGBAND_KEYBOARD = "JAPAN";
2558         }
2559     }
2560 }
2561
2562 /*!
2563  * @brief 全スポイラー出力を行う
2564  * Create Spoiler files
2565  * @details スポイラー出力処理の成功、失敗に関わらずプロセスを終了する。
2566  */
2567 void create_debug_spoiler(void)
2568 {
2569     init_stuff();
2570     init_angband(p_ptr, TRUE);
2571
2572     switch (output_all_spoilers()) {
2573     case SPOILER_OUTPUT_SUCCESS:
2574         fprintf(stdout, "Successfully created a spoiler file.");
2575     case SPOILER_OUTPUT_FAIL_FOPEN:
2576         fprintf(stderr, "Cannot create spoiler file.");
2577     case SPOILER_OUTPUT_FAIL_FCLOSE:
2578         fprintf(stderr, "Cannot close spoiler file.");
2579     default:
2580         break;
2581     }
2582
2583     quit(NULL);
2584 }
2585
2586 /*!
2587  * @brief メインウインドウ、サブウインドウのウインドウクラス登録
2588  */
2589 static void register_wndclass(void)
2590 {
2591     WNDCLASS wc{};
2592     wc.style = CS_CLASSDC;
2593     wc.lpfnWndProc = AngbandWndProc;
2594     wc.cbClsExtra = 0;
2595     wc.cbWndExtra = 4;
2596     wc.hInstance = hInstance;
2597     wc.hIcon = hIcon = LoadIcon(hInstance, AppName);
2598     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
2599     wc.hbrBackground = NULL;
2600     wc.lpszMenuName = AppName;
2601     wc.lpszClassName = AppName;
2602
2603     if (!RegisterClass(&wc))
2604         exit(1);
2605
2606     wc.lpfnWndProc = AngbandListProc;
2607     wc.lpszMenuName = NULL;
2608     wc.lpszClassName = AngList;
2609
2610     if (!RegisterClass(&wc))
2611         exit(2);
2612 }
2613
2614 /*!
2615  * @brief (Windows固有)Windowsアプリケーションとしてのエントリポイント
2616  */
2617 int WINAPI WinMain(_In_ HINSTANCE hInst, [[maybe_unused]] _In_opt_ HINSTANCE hPrevInst, _In_ LPSTR lpCmdLine, [[maybe_unused]] _In_ int nCmdShow)
2618 {
2619     setlocale(LC_ALL, "ja_JP");
2620     hInstance = hInst;
2621     if (is_already_running()) {
2622         MessageBoxW(
2623             NULL, _(L"変愚蛮怒はすでに起動しています。", L"Hengband is already running."), _(L"エラー!", L"Error"), MB_ICONEXCLAMATION | MB_OK | MB_ICONSTOP);
2624         return FALSE;
2625     }
2626
2627     command_line.handle();
2628     register_wndclass();
2629
2630     // before term_data initialize
2631     plog_aux = [](concptr str) {
2632         if (str)
2633             MessageBoxW(NULL, to_wchar(str).wc_str(), _(L"警告!", L"Warning"), MB_ICONEXCLAMATION | MB_OK);
2634     };
2635     quit_aux = [](concptr str) {
2636         if (str) {
2637             MessageBoxW(NULL, to_wchar(str).wc_str(), _(L"エラー!", L"Error"), MB_ICONEXCLAMATION | MB_OK | MB_ICONSTOP);
2638         }
2639
2640         UnregisterClass(AppName, hInstance);
2641         if (hIcon)
2642             DestroyIcon(hIcon);
2643
2644         exit(0);
2645     };
2646     core_aux = quit_aux;
2647
2648     init_stuff();
2649
2650     HDC hdc = GetDC(NULL);
2651     if (GetDeviceCaps(hdc, BITSPIXEL) <= 8) {
2652         quit(_("画面を16ビット以上のカラーモードにして下さい。", "Please switch to High Color (16-bit) or higher color mode."));
2653     }
2654     ReleaseDC(NULL, hdc);
2655
2656     refresh_color_table();
2657     init_windows();
2658     change_bg_mode(current_bg_mode, true);
2659
2660     // after term_data initialize
2661     plog_aux = hook_plog;
2662     quit_aux = hook_quit;
2663     core_aux = hook_quit;
2664
2665     signals_init();
2666     term_activate(term_screen);
2667     init_angband(p_ptr, FALSE);
2668     initialized = TRUE;
2669     check_for_save_file(p_ptr, command_line.get_savefile_option());
2670     prt(_("[ファイル] メニューの [新規] または [開く] を選択してください。", "[Choose 'New' or 'Open' from the 'File' menu]"), 23, _(8, 17));
2671     term_fresh();
2672
2673     change_sound_mode(arg_sound);
2674     use_music = arg_music;
2675     if (use_music) {
2676         init_music();
2677     }
2678
2679     MSG msg;
2680     while (GetMessage(&msg, NULL, 0, 0)) {
2681         TranslateMessage(&msg);
2682         DispatchMessage(&msg);
2683     }
2684
2685     quit(NULL);
2686     return 0;
2687 }
2688 #endif /* WINDOWS */