OSDN Git Service

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