OSDN Git Service

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