OSDN Git Service

[Refactor] struct player_type を class PlayerType に置換。
[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     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(PlayerType *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に影響があるのでPlayerTypeの追加は保留
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     *td = {};
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         *td = {};
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 (w_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(PlayerType *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 && w_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 && w_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     auto mc = any_bits(static_cast<ushort>(GetKeyState(VK_CONTROL)), 0x8000);
1936     auto ms = any_bits(static_cast<ushort>(GetKeyState(VK_SHIFT)), 0x8000);
1937     auto ma = any_bits(static_cast<ushort>(GetKeyState(VK_MENU)), 0x8000);
1938     term_no_press = ma;
1939     if (special_key[(byte)(wParam)] || (ma && !ignore_key[(byte)(wParam)])) {
1940         bool ext_key = any_bits(static_cast<ulong>(lParam), 0x1000000UL);
1941         bool numpad = false;
1942
1943         term_keypress(31);
1944         if (mc)
1945             term_keypress('C');
1946         if (ms)
1947             term_keypress('S');
1948         if (ma)
1949             term_keypress('A');
1950
1951         int i = LOBYTE(HIWORD(lParam));
1952         term_keypress('x');
1953         switch (wParam) {
1954         case VK_DIVIDE:
1955             term_no_press = true;
1956             [[fallthrough]]; /* Fall through */
1957         case VK_RETURN:
1958             numpad = ext_key;
1959             break;
1960         case VK_NUMPAD0:
1961         case VK_NUMPAD1:
1962         case VK_NUMPAD2:
1963         case VK_NUMPAD3:
1964         case VK_NUMPAD4:
1965         case VK_NUMPAD5:
1966         case VK_NUMPAD6:
1967         case VK_NUMPAD7:
1968         case VK_NUMPAD8:
1969         case VK_NUMPAD9:
1970         case VK_ADD:
1971         case VK_MULTIPLY:
1972         case VK_SUBTRACT:
1973         case VK_SEPARATOR:
1974         case VK_DECIMAL:
1975             term_no_press = true;
1976             [[fallthrough]]; /* Fall through */
1977         case VK_CLEAR:
1978         case VK_HOME:
1979         case VK_END:
1980         case VK_PRIOR:
1981         case VK_NEXT:
1982         case VK_INSERT:
1983         case VK_DELETE:
1984         case VK_UP:
1985         case VK_DOWN:
1986         case VK_LEFT:
1987         case VK_RIGHT:
1988             numpad = !ext_key;
1989         }
1990
1991         if (numpad)
1992             term_keypress('K');
1993
1994         term_keypress(hexsym[i / 16]);
1995         term_keypress(hexsym[i % 16]);
1996         term_keypress(13);
1997
1998         return 1;
1999     }
2000
2001     return 0;
2002 }
2003
2004 /*!
2005  * @brief ウィンドウのアクティブ/非アクティブのハンドラ
2006  */
2007 static void handle_app_active(HWND hWnd, UINT uMsg, WPARAM wParam, [[maybe_unused]] LPARAM lParam)
2008 {
2009     switch (uMsg) {
2010     case WM_ACTIVATEAPP: {
2011         if (wParam) {
2012             if (use_pause_music_inactive)
2013                 main_win_music::resume_music();
2014         } else {
2015             if (use_pause_music_inactive)
2016                 main_win_music::pause_music();
2017         }
2018         break;
2019     }
2020     case WM_WINDOWPOSCHANGING: {
2021         if (!IsIconic(hWnd))
2022             if (use_pause_music_inactive)
2023                 main_win_music::resume_music();
2024         break;
2025     }
2026     }
2027 }
2028
2029 /*!
2030  * @brief ターミナルのサイズをウインドウのサイズに合わせる
2031  * @param td term_dataのポインタ
2032  * @param recalc_window_size trueの場合に行列数からウインドウサイズを再計算し設定する
2033  */
2034 static void fit_term_size_to_window(term_data *td, bool recalc_window_size = false)
2035 {
2036     RECT rc;
2037     ::GetClientRect(td->w, &rc);
2038     int width = rc.right - rc.left;
2039     int height = rc.bottom - rc.top;
2040
2041     TERM_LEN cols = (width - td->size_ow1 - td->size_ow2) / td->tile_wid;
2042     TERM_LEN rows = (height - td->size_oh1 - td->size_oh2) / td->tile_hgt;
2043     if ((td->cols != cols) || (td->rows != rows)) {
2044         td->cols = cols;
2045         td->rows = rows;
2046         if (is_main_term(td) && !IsZoomed(td->w) && !IsIconic(td->w)) {
2047             normsize.x = td->cols;
2048             normsize.y = td->rows;
2049         }
2050
2051         rebuild_term(td, recalc_window_size);
2052
2053         if (!is_main_term(td)) {
2054             p_ptr->window_flags = PW_ALL;
2055             handle_stuff(p_ptr);
2056         }
2057     }
2058 }
2059
2060 /*!
2061  * @brief Windowのリサイズをハンドリング
2062  * @retval true ウインドウメッセージを処理した
2063  * @retval false ウインドウメッセージを処理していない
2064  */
2065 static bool handle_window_resize(term_data *td, UINT uMsg, WPARAM wParam, LPARAM lParam)
2066 {
2067     if (!td)
2068         return false;
2069     if (!td->w)
2070         return false;
2071
2072     switch (uMsg) {
2073     case WM_GETMINMAXINFO: {
2074         const bool is_main = is_main_term(td);
2075         const int min_cols = (is_main) ? 80 : 20;
2076         const int min_rows = (is_main) ? 24 : 3;
2077         const LONG w = min_cols * td->tile_wid + td->size_ow1 + td->size_ow2;
2078         const LONG h = min_rows * td->tile_hgt + td->size_oh1 + td->size_oh2 + 1;
2079         RECT rc{ 0, 0, w, h };
2080         AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle);
2081
2082         MINMAXINFO *lpmmi = (MINMAXINFO *)lParam;
2083         lpmmi->ptMinTrackSize.x = rc.right - rc.left;
2084         lpmmi->ptMinTrackSize.y = rc.bottom - rc.top;
2085
2086         return true;
2087     }
2088     case WM_EXITSIZEMOVE: {
2089         fit_term_size_to_window(td, true);
2090         return true;
2091     }
2092     case WM_WINDOWPOSCHANGED: {
2093         if (!td->size_hack) {
2094             WINDOWPOS *pos = (WINDOWPOS *)lParam;
2095             if ((pos->flags & (SWP_NOCOPYBITS | SWP_NOSIZE)) == 0) {
2096                 fit_term_size_to_window(td);
2097                 return true;
2098             }
2099         }
2100         break;
2101     }
2102     case WM_SIZE: {
2103         if (td->size_hack)
2104             break;
2105
2106         //!< @todo 二重のswitch文。後で分割する.
2107         switch (wParam) {
2108         case SIZE_MINIMIZED: {
2109             for (int i = 1; i < MAX_TERM_DATA; i++) {
2110                 if (data[i].visible)
2111                     ShowWindow(data[i].w, SW_HIDE);
2112             }
2113
2114             return true;
2115         }
2116         case SIZE_MAXIMIZED:
2117         case SIZE_RESTORED: {
2118             fit_term_size_to_window(td);
2119
2120             td->size_hack = true;
2121             for (int i = 1; i < MAX_TERM_DATA; i++) {
2122                 if (data[i].visible)
2123                     ShowWindow(data[i].w, SW_SHOWNA);
2124             }
2125
2126             td->size_hack = false;
2127
2128             return true;
2129         }
2130         }
2131
2132         break;
2133     }
2134     }
2135
2136     return false;
2137 }
2138
2139 /*!
2140  * @brief メインウインドウ用ウインドウプロシージャ
2141  */
2142 LRESULT PASCAL AngbandWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2143 {
2144     term_data *td = (term_data *)GetWindowLong(hWnd, 0);
2145
2146     handle_app_active(hWnd, uMsg, wParam, lParam);
2147     if (handle_window_resize(td, uMsg, wParam, lParam))
2148         return 0;
2149
2150     switch (uMsg) {
2151     case WM_NCCREATE: {
2152         SetWindowLong(hWnd, 0, (LONG)(my_td));
2153         break;
2154     }
2155     case WM_CREATE: {
2156         setup_mci(hWnd);
2157         return 0;
2158     }
2159     case WM_ERASEBKGND: {
2160         return 1;
2161     }
2162     case WM_PAINT: {
2163         PAINTSTRUCT ps;
2164         HDC hdc = BeginPaint(hWnd, &ps);
2165         if (td)
2166             if (!td->render(ps.rcPaint)) {
2167                 SetBkColor(hdc, RGB(0, 0, 0));
2168                 ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &ps.rcPaint, NULL, 0, NULL);
2169             }
2170         EndPaint(hWnd, &ps);
2171         ValidateRect(hWnd, NULL);
2172         return 0;
2173     }
2174     case MM_MCINOTIFY: {
2175         main_win_music::on_mci_notify(wParam, lParam);
2176
2177         return 0;
2178     }
2179     case WM_SYSKEYDOWN:
2180     case WM_KEYDOWN: {
2181         if (process_keydown(wParam, lParam))
2182             return 0;
2183         break;
2184     }
2185     case WM_CHAR: {
2186         // wParam is WCHAR because using RegisterClassW
2187         if (term_no_press)
2188             term_no_press = false;
2189         else {
2190             WCHAR wc[2] = { (WCHAR)wParam, '\0' };
2191             term_keypress(to_multibyte(wc).c_str());
2192         }
2193         return 0;
2194     }
2195     case WM_LBUTTONDOWN: {
2196         if (macro_running())
2197             return 0;
2198         mousex = std::min(LOWORD(lParam) / td->tile_wid, td->cols - 1);
2199         mousey = std::min(HIWORD(lParam) / td->tile_hgt, td->rows - 1);
2200         mouse_down = true;
2201         oldx = mousex;
2202         oldy = mousey;
2203         return 0;
2204     }
2205     case WM_LBUTTONUP: {
2206         if (!mouse_down)
2207             return 0;
2208         HGLOBAL hGlobal;
2209         LPSTR lpStr;
2210         TERM_LEN dx = abs(oldx - mousex) + 1;
2211         TERM_LEN dy = abs(oldy - mousey) + 1;
2212         TERM_LEN ox = (oldx > mousex) ? mousex : oldx;
2213         TERM_LEN oy = (oldy > mousey) ? mousey : oldy;
2214
2215         mouse_down = false;
2216         paint_rect = false;
2217
2218 #ifdef JP
2219         int sz = (dx + 3) * dy;
2220 #else
2221         int sz = (dx + 2) * dy;
2222 #endif
2223         hGlobal = GlobalAlloc(GHND, sz + 1);
2224         if (hGlobal == NULL)
2225             return 0;
2226         lpStr = (LPSTR)GlobalLock(hGlobal);
2227
2228         for (int i = 0; i < dy; i++) {
2229 #ifdef JP
2230             const auto &scr = data[0].t.scr->c;
2231
2232             std::vector<char> s(dx + 1);
2233             strncpy(s.data(), &scr[oy + i][ox], dx);
2234
2235             if (ox > 0) {
2236                 if (iskanji(scr[oy + i][ox - 1]))
2237                     s[0] = ' ';
2238             }
2239
2240             if (ox + dx < data[0].cols) {
2241                 if (iskanji(scr[oy + i][ox + dx - 1]))
2242                     s[dx - 1] = ' ';
2243             }
2244
2245             for (int j = 0; j < dx; j++) {
2246                 if (s[j] == 127)
2247                     s[j] = '#';
2248                 *lpStr++ = s[j];
2249             }
2250 #else
2251             for (int j = 0; j < dx; j++) {
2252                 *lpStr++ = data[0].t.scr->c[oy + i][ox + j];
2253             }
2254 #endif
2255             if (dy > 1) {
2256                 *lpStr++ = '\r';
2257                 *lpStr++ = '\n';
2258             }
2259         }
2260
2261         GlobalUnlock(hGlobal);
2262         if (OpenClipboard(hWnd) == 0) {
2263             GlobalFree(hGlobal);
2264             return 0;
2265         }
2266
2267         EmptyClipboard();
2268         SetClipboardData(CF_TEXT, hGlobal);
2269         CloseClipboard();
2270         term_redraw();
2271         return 0;
2272     }
2273     case WM_MOUSEMOVE: {
2274         if (!mouse_down)
2275             return 0;
2276
2277         int dx, dy;
2278         int cx = std::min(LOWORD(lParam) / td->tile_wid, td->cols - 1);
2279         int cy = std::min(HIWORD(lParam) / td->tile_hgt, td->rows - 1);
2280         int ox, oy;
2281
2282         if (paint_rect) {
2283             dx = abs(oldx - mousex) + 1;
2284             dy = abs(oldy - mousey) + 1;
2285             ox = (oldx > mousex) ? mousex : oldx;
2286             oy = (oldy > mousey) ? mousey : oldy;
2287             term_inversed_area(hWnd, ox, oy, dx, dy);
2288         } else {
2289             paint_rect = true;
2290         }
2291
2292         dx = abs(cx - mousex) + 1;
2293         dy = abs(cy - mousey) + 1;
2294         ox = (cx > mousex) ? mousex : cx;
2295         oy = (cy > mousey) ? mousey : cy;
2296         term_inversed_area(hWnd, ox, oy, dx, dy);
2297
2298         oldx = cx;
2299         oldy = cy;
2300         return 0;
2301     }
2302     case WM_INITMENU: {
2303         setup_menus();
2304         return 0;
2305     }
2306     case WM_CLOSE: {
2307         if (!game_in_progress || !w_ptr->character_generated) {
2308             quit(nullptr);
2309             return 0;
2310         }
2311
2312         if (!can_save) {
2313             plog(_("今は終了できません。", "You may not do that right now."));
2314             return 0;
2315         }
2316
2317         msg_flag = false;
2318         forget_lite(p_ptr->current_floor_ptr);
2319         forget_view(p_ptr->current_floor_ptr);
2320         clear_mon_lite(p_ptr->current_floor_ptr);
2321         term_key_push(SPECIAL_KEY_QUIT);
2322         return 0;
2323     }
2324     case WM_QUERYENDSESSION: {
2325         if (!game_in_progress || !w_ptr->character_generated) {
2326             quit(nullptr);
2327             return 0;
2328         }
2329
2330         msg_flag = false;
2331         if (p_ptr->chp < 0)
2332             p_ptr->is_dead = false;
2333         exe_write_diary(p_ptr, DIARY_GAMESTART, 0, _("----ゲーム中断----", "---- Save and Exit Game ----"));
2334
2335         p_ptr->panic_save = 1;
2336         signals_ignore_tstp();
2337         (void)strcpy(p_ptr->died_from, _("(緊急セーブ)", "(panic save)"));
2338         (void)save_player(p_ptr, SAVE_TYPE_CLOSE_GAME);
2339         quit(nullptr);
2340         return 0;
2341     }
2342     case WM_QUIT: {
2343         quit(nullptr);
2344         return 0;
2345     }
2346     case WM_COMMAND: {
2347         process_menus(p_ptr, LOWORD(wParam));
2348         return 0;
2349     }
2350     case WM_ACTIVATE: {
2351         if (!wParam || HIWORD(lParam))
2352             break;
2353
2354         for (int i = 1; i < MAX_TERM_DATA; i++) {
2355             if (!data[i].posfix)
2356                 term_window_pos(&data[i], hWnd);
2357         }
2358
2359         SetFocus(hWnd);
2360         return 0;
2361     }
2362     case WM_ACTIVATEAPP: {
2363         if (IsIconic(td->w))
2364             break;
2365
2366         for (int i = 1; i < MAX_TERM_DATA; i++) {
2367             if (data[i].visible) {
2368                 if (wParam == 1) {
2369                     ShowWindow(data[i].w, SW_SHOWNA);
2370                 } else {
2371                     ShowWindow(data[i].w, SW_HIDE);
2372                 }
2373             }
2374         }
2375     }
2376         [[fallthrough]]; /* Fall through */
2377     case WM_ENABLE: {
2378         if (wParam == FALSE && keep_subwindows) {
2379             for (int i = 1; i < MAX_TERM_DATA; i++) {
2380                 if (data[i].visible) {
2381                     ShowWindow(data[i].w, SW_SHOWNA);
2382                 }
2383             }
2384         }
2385     }
2386     }
2387
2388     return DefWindowProc(hWnd, uMsg, wParam, lParam);
2389 }
2390
2391 /*!
2392  * @brief サブウインドウ用ウインドウプロシージャ
2393  */
2394 LRESULT PASCAL AngbandListProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2395 {
2396     term_data *td = (term_data *)GetWindowLong(hWnd, 0);
2397     if (handle_window_resize(td, uMsg, wParam, lParam))
2398         return 0;
2399
2400     switch (uMsg) {
2401     case WM_NCCREATE: {
2402         SetWindowLong(hWnd, 0, (LONG)(my_td));
2403         break;
2404     }
2405     case WM_CREATE: {
2406         return 0;
2407     }
2408     case WM_ERASEBKGND: {
2409         return 1;
2410     }
2411     case WM_PAINT: {
2412         PAINTSTRUCT ps;
2413         HDC hdc = BeginPaint(hWnd, &ps);
2414         if (td)
2415             if (!td->render(ps.rcPaint)) {
2416                 SetBkColor(hdc, RGB(0, 0, 0));
2417                 ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &ps.rcPaint, NULL, 0, NULL);
2418             }
2419         EndPaint(hWnd, &ps);
2420         return 0;
2421     }
2422     case WM_SYSKEYDOWN:
2423     case WM_KEYDOWN: {
2424         if (process_keydown(wParam, lParam))
2425             return 0;
2426
2427         break;
2428     }
2429     case WM_CHAR: {
2430         // wParam is WCHAR because using RegisterClassW
2431         if (term_no_press)
2432             term_no_press = false;
2433         else {
2434             WCHAR wc[2] = { (WCHAR)wParam, '\0' };
2435             term_keypress(to_multibyte(wc).c_str());
2436         }
2437         return 0;
2438     }
2439     case WM_NCLBUTTONDOWN: {
2440         if (wParam == HTCLOSE)
2441             wParam = HTSYSMENU;
2442
2443         if (wParam == HTSYSMENU) {
2444             if (td->visible) {
2445                 td->visible = false;
2446                 ShowWindow(td->w, SW_HIDE);
2447             }
2448
2449             return 0;
2450         }
2451
2452         break;
2453     }
2454     }
2455
2456     return DefWindowProc(hWnd, uMsg, wParam, lParam);
2457 }
2458
2459 /*!
2460  * @brief Display warning message (see "z-util.c")
2461  */
2462 static void hook_plog(concptr str)
2463 {
2464     if (str) {
2465         MessageBoxW(data[0].w, to_wchar(str).wc_str(), _(L"警告!", L"Warning"), MB_ICONEXCLAMATION | MB_OK);
2466     }
2467 }
2468
2469 /*!
2470  * @brief Display error message and quit (see "z-util.c")
2471  */
2472 static void hook_quit(concptr str)
2473 {
2474     if (str) {
2475         MessageBoxW(data[0].w, to_wchar(str).wc_str(), _(L"エラー!", L"Error"), MB_ICONEXCLAMATION | MB_OK | MB_ICONSTOP);
2476     }
2477
2478     save_prefs();
2479     for (int i = MAX_TERM_DATA - 1; i >= 0; --i) {
2480         term_force_font(&data[i]);
2481         if (data[i].font_want)
2482             string_free(data[i].font_want);
2483         if (data[i].w)
2484             DestroyWindow(data[i].w);
2485         data[i].w = 0;
2486     }
2487
2488     DeleteObject(hbrYellow);
2489     finalize_bg();
2490     graphic.finalize();
2491     finalize_sound();
2492
2493     UnregisterClassW(AppName, hInstance);
2494     if (hIcon)
2495         DestroyIcon(hIcon);
2496
2497     exit(0);
2498 }
2499
2500 /*!
2501  * @brief Init some stuff
2502  */
2503 static void init_stuff(void)
2504 {
2505     char path[MAIN_WIN_MAX_PATH];
2506     DWORD path_len = GetModuleFileNameA(hInstance, path, MAIN_WIN_MAX_PATH);
2507     strcpy(path + path_len - 4, ".INI");
2508     ini_file = string_make(path);
2509
2510     int i = path_len;
2511     for (; i > 0; i--) {
2512         if (path[i] == '\\') {
2513             break;
2514         }
2515     }
2516
2517     strcpy(path + i + 1, "lib\\");
2518     validate_dir(path, true);
2519     init_file_paths(path, path);
2520     validate_dir(ANGBAND_DIR_APEX, false);
2521     validate_dir(ANGBAND_DIR_BONE, false);
2522     if (!check_dir(ANGBAND_DIR_EDIT)) {
2523         validate_dir(ANGBAND_DIR_DATA, true);
2524     } else {
2525         validate_dir(ANGBAND_DIR_DATA, false);
2526     }
2527
2528     validate_dir(ANGBAND_DIR_FILE, true);
2529     validate_dir(ANGBAND_DIR_HELP, false);
2530     validate_dir(ANGBAND_DIR_INFO, false);
2531     validate_dir(ANGBAND_DIR_PREF, true);
2532     validate_dir(ANGBAND_DIR_SAVE, false);
2533     validate_dir(ANGBAND_DIR_DEBUG_SAVE, false);
2534     validate_dir(ANGBAND_DIR_USER, true);
2535     validate_dir(ANGBAND_DIR_XTRA, true);
2536     path_build(path, sizeof(path), ANGBAND_DIR_FILE, _("news_j.txt", "news.txt"));
2537
2538     validate_file(path);
2539     path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "graf");
2540     ANGBAND_DIR_XTRA_GRAF = string_make(path);
2541     validate_dir(ANGBAND_DIR_XTRA_GRAF, true);
2542
2543     path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "sound");
2544     ANGBAND_DIR_XTRA_SOUND = string_make(path);
2545     validate_dir(ANGBAND_DIR_XTRA_SOUND, false);
2546
2547     path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "music");
2548     ANGBAND_DIR_XTRA_MUSIC = string_make(path);
2549     validate_dir(ANGBAND_DIR_XTRA_MUSIC, false);
2550
2551     for (i = 0; special_key_list[i]; ++i) {
2552         special_key[special_key_list[i]] = true;
2553     }
2554
2555     for (i = 0; ignore_key_list[i]; ++i) {
2556         ignore_key[ignore_key_list[i]] = true;
2557     }
2558
2559     ANGBAND_SYS = "win";
2560     if (7 != GetKeyboardType(0))
2561         ANGBAND_KEYBOARD = "0";
2562     else {
2563         switch (GetKeyboardType(1)) {
2564         case 0x0D01:
2565         case 0x0D02:
2566         case 0x0D03:
2567         case 0x0D04:
2568         case 0x0D05:
2569         case 0x0D06:
2570             /* NEC PC-98x1 */
2571             ANGBAND_KEYBOARD = "NEC98";
2572             break;
2573         default:
2574             /* PC/AT */
2575             ANGBAND_KEYBOARD = "JAPAN";
2576         }
2577     }
2578 }
2579
2580 /*!
2581  * @brief 全スポイラー出力を行う
2582  * Create Spoiler files
2583  * @details スポイラー出力処理の成功、失敗に関わらずプロセスを終了する。
2584  */
2585 void create_debug_spoiler(void)
2586 {
2587     init_stuff();
2588     init_angband(p_ptr, true);
2589
2590     switch (output_all_spoilers()) {
2591     case SpoilerOutputResultType::SPOILER_OUTPUT_SUCCESS:
2592         fprintf(stdout, "Successfully created a spoiler file.");
2593     case SpoilerOutputResultType::SPOILER_OUTPUT_FAIL_FOPEN:
2594         fprintf(stderr, "Cannot create spoiler file.");
2595     case SpoilerOutputResultType::SPOILER_OUTPUT_FAIL_FCLOSE:
2596         fprintf(stderr, "Cannot close spoiler file.");
2597     default:
2598         break;
2599     }
2600
2601     quit(nullptr);
2602 }
2603
2604 /*!
2605  * @brief メインウインドウ、サブウインドウのウインドウクラス登録
2606  */
2607 static void register_wndclass(void)
2608 {
2609     WNDCLASSW wc{};
2610     wc.style = CS_CLASSDC;
2611     wc.lpfnWndProc = AngbandWndProc;
2612     wc.cbClsExtra = 0;
2613     wc.cbWndExtra = 4;
2614     wc.hInstance = hInstance;
2615     wc.hIcon = hIcon = LoadIconW(hInstance, AppName);
2616     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
2617     wc.hbrBackground = NULL;
2618     wc.lpszMenuName = AppName;
2619     wc.lpszClassName = AppName;
2620
2621     if (!RegisterClassW(&wc))
2622         exit(1);
2623
2624     wc.lpfnWndProc = AngbandListProc;
2625     wc.lpszMenuName = NULL;
2626     wc.lpszClassName = AngList;
2627
2628     if (!RegisterClassW(&wc))
2629         exit(2);
2630 }
2631
2632 /*!
2633  * @brief (Windows固有)Windowsアプリケーションとしてのエントリポイント
2634  */
2635 int WINAPI WinMain(
2636     _In_ HINSTANCE hInst, [[maybe_unused]] _In_opt_ HINSTANCE hPrevInst, [[maybe_unused]] _In_ LPSTR lpCmdLine, [[maybe_unused]] _In_ int nCmdShow)
2637 {
2638     setlocale(LC_ALL, "ja_JP");
2639     hInstance = hInst;
2640     if (is_already_running()) {
2641         MessageBoxW(
2642             NULL, _(L"変愚蛮怒はすでに起動しています。", L"Hengband is already running."), _(L"エラー!", L"Error"), MB_ICONEXCLAMATION | MB_OK | MB_ICONSTOP);
2643         return 0;
2644     }
2645
2646     command_line.handle();
2647     register_wndclass();
2648
2649     // before term_data initialize
2650     plog_aux = [](concptr str) {
2651         if (str)
2652             MessageBoxW(NULL, to_wchar(str).wc_str(), _(L"警告!", L"Warning"), MB_ICONEXCLAMATION | MB_OK);
2653     };
2654     quit_aux = [](concptr str) {
2655         if (str) {
2656             MessageBoxW(NULL, to_wchar(str).wc_str(), _(L"エラー!", L"Error"), MB_ICONEXCLAMATION | MB_OK | MB_ICONSTOP);
2657         }
2658
2659         UnregisterClassW(AppName, hInstance);
2660         if (hIcon)
2661             DestroyIcon(hIcon);
2662
2663         exit(0);
2664     };
2665     core_aux = quit_aux;
2666
2667     init_stuff();
2668
2669     HDC hdc = GetDC(NULL);
2670     if (GetDeviceCaps(hdc, BITSPIXEL) <= 8) {
2671         quit(_("画面を16ビット以上のカラーモードにして下さい。", "Please switch to High Color (16-bit) or higher color mode."));
2672     }
2673     ReleaseDC(NULL, hdc);
2674
2675     refresh_color_table();
2676     init_windows();
2677     change_graphics_mode(static_cast<graphics_mode>(arg_graphics));
2678     change_bg_mode(current_bg_mode, true);
2679
2680     // after term_data initialize
2681     plog_aux = hook_plog;
2682     quit_aux = hook_quit;
2683     core_aux = hook_quit;
2684
2685     signals_init();
2686     term_activate(term_screen);
2687     init_angband(p_ptr, false);
2688     initialized = true;
2689
2690     check_for_save_file(command_line.get_savefile_option());
2691     prt(_("[ファイル] メニューの [新規] または [開く] を選択してください。", "[Choose 'New' or 'Open' from the 'File' menu]"), 23, _(8, 17));
2692     term_fresh();
2693
2694     change_sound_mode(arg_sound);
2695     use_music = arg_music;
2696     if (use_music) {
2697         init_music();
2698     }
2699
2700     // ユーザーがゲーム開始を選択するまで待つループ
2701     MSG msg;
2702     while (GetMessage(&msg, NULL, 0, 0)) {
2703         TranslateMessage(&msg);
2704         DispatchMessage(&msg);
2705         if (game_in_progress || movie_in_progress)
2706             break;
2707     }
2708
2709     term_flush();
2710     if (movie_in_progress) {
2711         // selected movie
2712         play_game(p_ptr, false, true);
2713     } else if (savefile[0] == '\0') {
2714         // new game
2715         play_game(p_ptr, true, false);
2716     } else {
2717         // selected savefile
2718         play_game(p_ptr, false, false);
2719     }
2720
2721     quit(nullptr);
2722     return 0;
2723 }
2724 #endif /* WINDOWS */