OSDN Git Service

e2166c3e434ae56239f939e88668a3f268b0e42e
[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 "NULL" means "on top of all windows". (?)  The
68  * user must never be allowed to "hide" the main window, or the "menubar"
69  * will disappear.
70  * </p>
71  *
72  * <p>
73  * Initial framework (and most code) by Ben Harrison (benh@phial.com).
74  *
75  * Original code by Skirmantas Kligys (kligys@scf.usc.edu).
76  *
77  * Additional code by Ross E Becker (beckerr@cis.ohio-state.edu),
78  * and Chris R. Martin (crm7479@tam2000.tamu.edu).
79  * </p>
80  */
81
82 #ifdef WINDOWS
83
84 #include "cmd-io/cmd-save.h"
85 #include "cmd-visual/cmd-draw.h"
86 #include "core/game-play.h"
87 #include "core/player-processor.h"
88 #include "core/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/int-char-converter.h"
124 #include "util/string-processor.h"
125 #include "view/display-messages.h"
126 #include "view/display-scores.h"
127 #include "wizard/spoiler-util.h"
128 #include "wizard/wizard-spoiler.h"
129 #include "world/world.h"
130
131 #include <cstdlib>
132 #include <locale>
133 #include <string>
134
135 #include <commdlg.h>
136 #include <direct.h>
137
138 /*
139  * Window names
140  */
141 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" };
142
143 #define MAX_TERM_DATA 8 //!< Maximum number of windows
144
145 static term_data data[MAX_TERM_DATA]; //!< An array of term_data's
146 static bool is_main_term(term_data *td)
147 {
148     return (td == &data[0]);
149 }
150 static term_data *my_td; //!< Hack -- global "window creation" pointer
151 POINT normsize; //!< Remember normal size of main window when maxmized
152
153 /*
154  * was main window maximized on previous playing
155  */
156 bool win_maximized = false;
157
158 /*
159  * game in progress
160  */
161 bool game_in_progress = false;
162
163 /*
164  * movie in progress
165  */
166 bool movie_in_progress = false;
167
168 /*
169  * note when "open"/"new" become valid
170  */
171 bool initialized = false;
172
173 /*
174  * Saved instance handle
175  */
176 static HINSTANCE hInstance;
177
178 /*
179  * Yellow brush for the cursor
180  */
181 static HBRUSH hbrYellow;
182
183 /*
184  * An icon
185  */
186 static HICON hIcon;
187
188 /* bg */
189 bg_mode current_bg_mode = bg_mode::BG_NONE;
190 #define DEFAULT_BG_FILENAME "bg.bmp"
191 char wallpaper_file[MAIN_WIN_MAX_PATH] = ""; //!< 壁紙ファイル名。
192
193 /*
194  * Show sub-windows even when Hengband is not in focus
195  */
196 static bool keep_subwindows = true;
197
198 /*
199  * Full path to ANGBAND.INI
200  */
201 static concptr ini_file = NULL;
202
203 /*
204  * Name of application
205  */
206 static LPCWSTR AppName = L"ANGBAND";
207
208 /*
209  * Name of sub-window type
210  */
211 static LPCWSTR AngList = L"AngList";
212
213 /*
214  * The "complex" color values
215  */
216 static COLORREF win_clr[256];
217
218 /*
219  * Flag for macro trigger with dump ASCII
220  */
221 static bool term_no_press = false;
222
223 /*
224  * Copy and paste
225  */
226 static bool mouse_down = false;
227 static bool paint_rect = false;
228 static TERM_LEN mousex = 0, mousey = 0;
229 static TERM_LEN oldx, oldy;
230
231 /*
232  * Hack -- define which keys are "special"
233  */
234 static bool special_key[256];
235 static bool ignore_key[256];
236
237 /*
238  * Hack -- initialization list for "special_key"
239  */
240 static byte special_key_list[] = {
241     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,
242     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,
243     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,
244     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,
245     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,
246     0 /* End of List */
247 };
248
249 static byte ignore_key_list[] = {
250     VK_ESCAPE, VK_TAB, VK_SPACE, 'F', 'W', 'O', /*'H',*/ /* these are menu characters.*/
251     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 */
252 };
253
254 /*!
255  * @brief Validate a file
256  */
257 static void validate_file(concptr s)
258 {
259     if (check_file(s))
260         return;
261
262     quit_fmt(_("必要なファイル[%s]が見あたりません。", "Cannot find required file:\n%s"), s);
263 }
264
265 /*!
266  * @brief Validate a directory
267  */
268 static void validate_dir(concptr s, bool vital)
269 {
270     if (check_dir(s))
271         return;
272
273     if (vital) {
274         quit_fmt(_("必要なディレクトリ[%s]が見あたりません。", "Cannot find required directory:\n%s"), s);
275     } else if (mkdir(s)) {
276         quit_fmt("Unable to create directory:\n%s", s);
277     }
278 }
279
280 /*!
281  * @brief (Windows版固有実装)Get the "size" for a window
282  */
283 static void term_getsize(term_data *td)
284 {
285     if (td->cols < 1)
286         td->cols = 1;
287     if (td->rows < 1)
288         td->rows = 1;
289
290     TERM_LEN wid = td->cols * td->tile_wid + td->size_ow1 + td->size_ow2;
291     TERM_LEN hgt = td->rows * td->tile_hgt + td->size_oh1 + td->size_oh2;
292
293     RECT rw, rc;
294     if (td->w) {
295         GetWindowRect(td->w, &rw);
296         GetClientRect(td->w, &rc);
297
298         td->size_wid = (rw.right - rw.left) - (rc.right - rc.left) + wid;
299         td->size_hgt = (rw.bottom - rw.top) - (rc.bottom - rc.top) + hgt;
300
301         td->pos_x = rw.left;
302         td->pos_y = rw.top;
303     } else {
304         /* Tempolary calculation */
305         rc.left = 0;
306         rc.right = wid;
307         rc.top = 0;
308         rc.bottom = hgt;
309         AdjustWindowRectEx(&rc, td->dwStyle, TRUE, td->dwExStyle);
310         td->size_wid = rc.right - rc.left;
311         td->size_hgt = rc.bottom - rc.top;
312     }
313 }
314
315 /*!
316  * @brief Write the "prefs" for a single term
317  */
318 static void save_prefs_aux(int i)
319 {
320     term_data *td = &data[i];
321     GAME_TEXT sec_name[128];
322     char buf[1024];
323
324     if (!td->w)
325         return;
326
327     wsprintfA(sec_name, "Term-%d", i);
328
329     if (i > 0) {
330         strcpy(buf, td->visible ? "1" : "0");
331         WritePrivateProfileStringA(sec_name, "Visible", buf, ini_file);
332     }
333
334     auto pwchar = td->lf.lfFaceName[0] != '\0' ? td->lf.lfFaceName : _(L"MS ゴシック", L"Courier");
335     WritePrivateProfileStringA(sec_name, "Font", to_multibyte(pwchar).c_str(), ini_file);
336
337     wsprintfA(buf, "%d", td->lf.lfWidth);
338     WritePrivateProfileStringA(sec_name, "FontWid", buf, ini_file);
339     wsprintfA(buf, "%d", td->lf.lfHeight);
340     WritePrivateProfileStringA(sec_name, "FontHgt", buf, ini_file);
341     wsprintfA(buf, "%d", td->lf.lfWeight);
342     WritePrivateProfileStringA(sec_name, "FontWgt", buf, ini_file);
343
344     wsprintfA(buf, "%d", td->tile_wid);
345     WritePrivateProfileStringA(sec_name, "TileWid", buf, ini_file);
346
347     wsprintfA(buf, "%d", td->tile_hgt);
348     WritePrivateProfileStringA(sec_name, "TileHgt", buf, ini_file);
349
350     WINDOWPLACEMENT lpwndpl;
351     lpwndpl.length = sizeof(WINDOWPLACEMENT);
352     GetWindowPlacement(td->w, &lpwndpl);
353
354     RECT rc = lpwndpl.rcNormalPosition;
355     if (i == 0)
356         wsprintfA(buf, "%d", normsize.x);
357     else
358         wsprintfA(buf, "%d", td->cols);
359
360     WritePrivateProfileStringA(sec_name, "NumCols", buf, ini_file);
361
362     if (i == 0)
363         wsprintfA(buf, "%d", normsize.y);
364     else
365         wsprintfA(buf, "%d", td->rows);
366
367     WritePrivateProfileStringA(sec_name, "NumRows", buf, ini_file);
368     if (i == 0) {
369         strcpy(buf, IsZoomed(td->w) ? "1" : "0");
370         WritePrivateProfileStringA(sec_name, "Maximized", buf, ini_file);
371     }
372
373     GetWindowRect(td->w, &rc);
374     wsprintfA(buf, "%d", rc.left);
375     WritePrivateProfileStringA(sec_name, "PositionX", buf, ini_file);
376
377     wsprintfA(buf, "%d", rc.top);
378     WritePrivateProfileStringA(sec_name, "PositionY", buf, ini_file);
379     if (i > 0) {
380         strcpy(buf, td->posfix ? "1" : "0");
381         WritePrivateProfileStringA(sec_name, "PositionFix", buf, ini_file);
382     }
383 }
384
385 /*!
386  * @brief Write the "prefs"
387  * We assume that the windows have all been initialized
388  */
389 static void save_prefs(void)
390 {
391     char buf[128];
392     wsprintfA(buf, "%d", arg_graphics);
393     WritePrivateProfileStringA("Angband", "Graphics", buf, ini_file);
394
395     strcpy(buf, arg_bigtile ? "1" : "0");
396     WritePrivateProfileStringA("Angband", "Bigtile", buf, ini_file);
397
398     strcpy(buf, arg_sound ? "1" : "0");
399     WritePrivateProfileStringA("Angband", "Sound", buf, ini_file);
400
401     strcpy(buf, arg_music ? "1" : "0");
402     WritePrivateProfileStringA("Angband", "Music", buf, ini_file);
403     strcpy(buf, use_pause_music_inactive ? "1" : "0");
404     WritePrivateProfileStringA("Angband", "MusicPauseInactive", buf, ini_file);
405
406     wsprintfA(buf, "%d", current_bg_mode);
407     WritePrivateProfileStringA("Angband", "BackGround", buf, ini_file);
408     WritePrivateProfileStringA("Angband", "BackGroundBitmap", wallpaper_file[0] != '\0' ? wallpaper_file : DEFAULT_BG_FILENAME, ini_file);
409
410     int path_length = strlen(ANGBAND_DIR) - 4; /* \libの4文字分を削除 */
411     char tmp[1024] = "";
412     strncat(tmp, ANGBAND_DIR, path_length);
413
414     int n = strncmp(tmp, savefile, path_length);
415     if (n == 0) {
416         char relative_path[1024] = "";
417         snprintf(relative_path, sizeof(relative_path), ".\\%s", (savefile + path_length));
418         WritePrivateProfileStringA("Angband", "SaveFile", relative_path, ini_file);
419     } else {
420         WritePrivateProfileStringA("Angband", "SaveFile", savefile, ini_file);
421     }
422
423     strcpy(buf, keep_subwindows ? "1" : "0");
424     WritePrivateProfileStringA("Angband", "KeepSubwindows", buf, ini_file);
425
426     for (int i = 0; i < MAX_TERM_DATA; ++i) {
427         save_prefs_aux(i);
428     }
429 }
430
431 /*!
432  * @brief callback for EnumDisplayMonitors API
433  */
434 BOOL CALLBACK monitorenumproc([[maybe_unused]] HMONITOR hMon, [[maybe_unused]] HDC hdcMon, [[maybe_unused]] LPRECT lpMon, LPARAM dwDate)
435 {
436     bool *result = (bool *)dwDate;
437     *result = true;
438     return false;
439 }
440
441 /*!
442  * @brief Load the "prefs" for a single term
443  */
444 static void load_prefs_aux(int i)
445 {
446     term_data *td = &data[i];
447     GAME_TEXT sec_name[128];
448     char tmp[1024];
449
450     wsprintfA(sec_name, "Term-%d", i);
451     if (i > 0) {
452         td->visible = (GetPrivateProfileIntA(sec_name, "Visible", td->visible, ini_file) != 0);
453     }
454
455     GetPrivateProfileStringA(sec_name, "Font", _("MS ゴシック", "Courier"), tmp, 127, ini_file);
456
457     td->font_want = string_make(tmp);
458     int hgt = 15;
459     int wid = 0;
460     td->lf.lfWidth = GetPrivateProfileIntA(sec_name, "FontWid", wid, ini_file);
461     td->lf.lfHeight = GetPrivateProfileIntA(sec_name, "FontHgt", hgt, ini_file);
462     td->lf.lfWeight = GetPrivateProfileIntA(sec_name, "FontWgt", 0, ini_file);
463
464     td->tile_wid = GetPrivateProfileIntA(sec_name, "TileWid", td->lf.lfWidth, ini_file);
465     td->tile_hgt = GetPrivateProfileIntA(sec_name, "TileHgt", td->lf.lfHeight, ini_file);
466
467     td->cols = GetPrivateProfileIntA(sec_name, "NumCols", td->cols, ini_file);
468     td->rows = GetPrivateProfileIntA(sec_name, "NumRows", td->rows, ini_file);
469     normsize.x = td->cols;
470     normsize.y = td->rows;
471
472     if (i == 0) {
473         win_maximized = (GetPrivateProfileIntA(sec_name, "Maximized", win_maximized, ini_file) != 0);
474     }
475
476     int posx = GetPrivateProfileIntA(sec_name, "PositionX", 0, ini_file);
477     int posy = GetPrivateProfileIntA(sec_name, "PositionY", 0, ini_file);
478     // 保存座標がモニタ内の領域にあるかチェック
479     RECT rect = { posx, posy, posx + 128, posy + 128 };
480     bool in_any_monitor = false;
481     ::EnumDisplayMonitors(NULL, &rect, monitorenumproc, (LPARAM)&in_any_monitor);
482     if (in_any_monitor) {
483         // いずれかのモニタに表示可能、ウインドウ位置を復元
484         td->pos_x = posx;
485         td->pos_y = posy;
486     }
487
488     if (i > 0) {
489         td->posfix = (GetPrivateProfileIntA(sec_name, "PositionFix", td->posfix, ini_file) != 0);
490     }
491 }
492
493 /*!
494  * @brief Load the "prefs"
495  */
496 static void load_prefs(void)
497 {
498     arg_graphics = (byte)GetPrivateProfileIntA("Angband", "Graphics", static_cast<byte>(graphics_mode::GRAPHICS_NONE), ini_file);
499     arg_bigtile = (GetPrivateProfileIntA("Angband", "Bigtile", false, ini_file) != 0);
500     use_bigtile = arg_bigtile;
501     arg_sound = (GetPrivateProfileIntA("Angband", "Sound", 0, ini_file) != 0);
502     arg_music = (GetPrivateProfileIntA("Angband", "Music", 0, ini_file) != 0);
503     use_pause_music_inactive = (GetPrivateProfileIntA("Angband", "MusicPauseInactive", 0, ini_file) != 0);
504     current_bg_mode = static_cast<bg_mode>(GetPrivateProfileIntA("Angband", "BackGround", 0, ini_file));
505     GetPrivateProfileStringA("Angband", "BackGroundBitmap", DEFAULT_BG_FILENAME, wallpaper_file, 1023, ini_file);
506     GetPrivateProfileStringA("Angband", "SaveFile", "", savefile, 1023, ini_file);
507
508     int n = strncmp(".\\", savefile, 2);
509     if (n == 0) {
510         int path_length = strlen(ANGBAND_DIR) - 4; /* \libの4文字分を削除 */
511         char tmp[1024] = "";
512         strncat(tmp, ANGBAND_DIR, path_length);
513         strncat(tmp, savefile + 2, strlen(savefile) - 2 + path_length);
514         strncpy(savefile, tmp, strlen(tmp));
515     }
516
517     keep_subwindows = (GetPrivateProfileIntA("Angband", "KeepSubwindows", 0, ini_file) != 0);
518     for (int i = 0; i < MAX_TERM_DATA; ++i) {
519         load_prefs_aux(i);
520     }
521 }
522
523 /*!
524  * @brief Initialize music
525  */
526 static void init_music(void)
527 {
528     // Flag set once "music" has been initialized
529     static bool can_use_music = false;
530
531     if (!can_use_music) {
532         main_win_music::load_music_prefs();
533         can_use_music = true;
534     }
535 }
536
537 /*!
538  * @brief Initialize sound
539  */
540 static void init_sound(void)
541 {
542     // Flag set once "sound" has been initialized
543     static bool can_use_sound = false;
544
545     if (!can_use_sound) {
546         load_sound_prefs();
547         can_use_sound = true;
548     }
549 }
550
551 /*!
552  * @brief Change sound mode
553  * @param new_mode bool
554  */
555 static void change_sound_mode(bool new_mode)
556 {
557     use_sound = new_mode;
558     if (use_sound) {
559         init_sound();
560     }
561 }
562
563 /*!
564  * @brief Initialize background
565  */
566 static void init_background(void)
567 {
568     // Flag set once "background" has been initialized
569     static bool can_use_background = false;
570
571     if (!can_use_background) {
572         load_bg_prefs();
573         can_use_background = true;
574     }
575 }
576
577 /*!
578  * @brief Change background mode
579  * @param new_mode bg_mode
580  * @param show_error trueに設定した場合のみ、エラーダイアログを表示する
581  * @param force_redraw trueの場合、モード変更に関わらずウインドウを再描画する
582  * @retval true success
583  * @retval false failed
584  */
585 static bool change_bg_mode(bg_mode new_mode, bool show_error = false, bool force_redraw = false)
586 {
587     bg_mode old_bg_mode = current_bg_mode;
588     current_bg_mode = new_mode;
589     if (current_bg_mode != bg_mode::BG_NONE) {
590         init_background();
591         if (!load_bg(wallpaper_file)) {
592             current_bg_mode = bg_mode::BG_NONE;
593             if (show_error)
594                 plog_fmt(_("壁紙用ファイル '%s' を読み込めません。", "Can't load the image file '%s'."), wallpaper_file);
595         }
596     } else {
597         delete_bg();
598     }
599
600     const bool mode_changed = (current_bg_mode != old_bg_mode);
601     if (mode_changed || force_redraw) {
602         // 全ウインドウ再描画
603         term_type *old = Term;
604         for (int i = 0; i < MAX_TERM_DATA; i++) {
605             term_data *td = &data[i];
606             if (td->visible) {
607                 term_activate(&td->t);
608                 term_redraw();
609             }
610         }
611         term_activate(old);
612     }
613
614     return (current_bg_mode == new_mode);
615 }
616
617 /*!
618  * @brief Resize a window
619  */
620 static void term_window_resize(term_data *td)
621 {
622     if (!td->w)
623         return;
624
625     SetWindowPos(td->w, 0, 0, 0, td->size_wid, td->size_hgt, SWP_NOMOVE | SWP_NOZORDER);
626     if (!td->size_hack) {
627         td->dispose_offscreen();
628         term_activate(&td->t);
629         term_redraw();
630     }
631 }
632
633 /*!
634  * @brief Force the use of a new font for a term_data.
635  * This function may be called before the "window" is ready.
636  * This function returns zero only if everything succeeds.
637  * @note that the "font name" must be capitalized!!!
638  */
639 static errr term_force_font(term_data *td)
640 {
641     if (td->font_id)
642         DeleteObject(td->font_id);
643
644     td->font_id = CreateFontIndirectW(&(td->lf));
645     int wid = td->lf.lfWidth;
646     int hgt = td->lf.lfHeight;
647     if (!td->font_id)
648         return 1;
649
650     if (!wid || !hgt) {
651         HDC hdcDesktop;
652         HFONT hfOld;
653         TEXTMETRIC tm;
654
655         hdcDesktop = GetDC(HWND_DESKTOP);
656         hfOld = static_cast<HFONT>(SelectObject(hdcDesktop, td->font_id));
657         GetTextMetrics(hdcDesktop, &tm);
658         SelectObject(hdcDesktop, hfOld);
659         ReleaseDC(HWND_DESKTOP, hdcDesktop);
660
661         wid = tm.tmAveCharWidth;
662         hgt = tm.tmHeight;
663     }
664
665     td->font_wid = wid;
666     td->font_hgt = hgt;
667
668     return 0;
669 }
670
671 /*!
672  * @brief Allow the user to change the font for this window.
673  */
674 static void term_change_font(term_data *td)
675 {
676     CHOOSEFONTW cf;
677     memset(&cf, 0, sizeof(cf));
678     cf.lStructSize = sizeof(cf);
679     cf.Flags = CF_SCREENFONTS | CF_FIXEDPITCHONLY | CF_NOVERTFONTS | CF_INITTOLOGFONTSTRUCT;
680     cf.lpLogFont = &(td->lf);
681
682     if (!ChooseFontW(&cf))
683         return;
684
685     term_force_font(td);
686     td->tile_wid = td->font_wid;
687     td->tile_hgt = td->font_hgt;
688     term_getsize(td);
689     term_window_resize(td);
690 }
691
692 /*!
693  * @brief Allow the user to lock this window.
694  */
695 static void term_window_pos(term_data *td, HWND hWnd)
696 {
697     SetWindowPos(td->w, hWnd, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
698 }
699
700 /*!
701  * @brief Hack -- redraw a term_data
702  */
703 static void term_data_redraw(term_data *td)
704 {
705     term_activate(&td->t);
706     term_redraw();
707     term_activate(term_screen);
708 }
709
710 /*!
711  * @brief termの反転色表示
712  */
713 void term_inversed_area(HWND hWnd, int x, int y, int w, int h)
714 {
715     term_data *td = (term_data *)GetWindowLong(hWnd, 0);
716     int tx = td->size_ow1 + x * td->tile_wid;
717     int ty = td->size_oh1 + y * td->tile_hgt;
718     int tw = w * td->tile_wid - 1;
719     int th = h * td->tile_hgt - 1;
720
721     HDC hdc = td->get_hdc();
722     HBRUSH myBrush = CreateSolidBrush(RGB(255, 255, 255));
723     HBRUSH oldBrush = static_cast<HBRUSH>(SelectObject(hdc, myBrush));
724     HPEN oldPen = static_cast<HPEN>(SelectObject(hdc, GetStockObject(NULL_PEN)));
725
726     PatBlt(hdc, tx, ty, tw, th, PATINVERT);
727
728     SelectObject(hdc, oldBrush);
729     SelectObject(hdc, oldPen);
730
731     RECT rect{ tx, ty, tx + tw, ty + th };
732     td->refresh(&rect);
733 }
734
735 /*!
736  * @brief Windows版ユーザ設定項目実装部(実装必須) /Interact with the User
737  */
738 static errr term_user_win(int n)
739 {
740     (void)n;
741     return 0;
742 }
743
744 /*!
745  * @brief カラーパレットの変更?
746  */
747 static void refresh_color_table()
748 {
749     for (int i = 0; i < 256; i++) {
750         byte rv = angband_color_table[i][1];
751         byte gv = angband_color_table[i][2];
752         byte bv = angband_color_table[i][3];
753         win_clr[i] = PALETTERGB(rv, gv, bv);
754     }
755 }
756
757 /*!
758  * @brief グラフィクスのモード変更
759  */
760 static void change_graphics_mode(graphics_mode mode)
761 {
762     graphics_mode ret = graphic.change_graphics(mode);
763     if (ret != mode) {
764         plog(_("グラフィクスを初期化できません!", "Cannot initialize graphics!"));
765     }
766     arg_graphics = static_cast<byte>(ret);
767     use_graphics = (arg_graphics > 0);
768 }
769
770 /*!
771  * @brief ターミナルのサイズ更新
772  * @details 行数、列数の変更に対応する。
773  * @param td term_dataのポインタ
774  * @param resize_window trueの場合に再計算されたウインドウサイズにリサイズする
775  */
776 static void rebuild_term(term_data *td, bool resize_window = true)
777 {
778     term_type *old = Term;
779     td->size_hack = true;
780     term_activate(&td->t);
781     term_getsize(td);
782     if (resize_window) {
783         term_window_resize(td);
784     }
785     td->dispose_offscreen();
786     term_resize(td->cols, td->rows);
787     td->size_hack = false;
788     term_activate(old);
789 }
790
791 /*!
792  * @brief React to global changes
793  */
794 static errr term_xtra_win_react(player_type *player_ptr)
795 {
796     refresh_color_table();
797
798     const byte current_mode = static_cast<byte>(graphic.get_mode());
799     if (current_mode != arg_graphics) {
800         change_graphics_mode(static_cast<graphics_mode>(arg_graphics));
801         reset_visuals(player_ptr);
802     }
803
804     for (int i = 0; i < MAX_TERM_DATA; i++) {
805         term_type *old = Term;
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 == static_cast<byte>(graphics_mode::GRAPHICS_NONE) ? MF_CHECKED : MF_UNCHECKED));
1496     CheckMenuItem(hm, IDM_OPTIONS_OLD_GRAPHICS, (arg_graphics == static_cast<byte>(graphics_mode::GRAPHICS_ORIGINAL) ? MF_CHECKED : MF_UNCHECKED));
1497     CheckMenuItem(hm, IDM_OPTIONS_NEW_GRAPHICS, (arg_graphics == static_cast<byte>(graphics_mode::GRAPHICS_ADAM_BOLT) ? MF_CHECKED : MF_UNCHECKED));
1498     CheckMenuItem(hm, IDM_OPTIONS_NEW2_GRAPHICS, (arg_graphics == static_cast<byte>(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(NULL);
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, NULL);
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 != static_cast<byte>(graphics_mode::GRAPHICS_NONE)) {
1785             arg_graphics = static_cast<byte>(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 != static_cast<byte>(graphics_mode::GRAPHICS_ORIGINAL)) {
1793             arg_graphics = static_cast<byte>(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 != static_cast<byte>(graphics_mode::GRAPHICS_ADAM_BOLT)) {
1802             arg_graphics = static_cast<byte>(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 != static_cast<byte>(graphics_mode::GRAPHICS_HENGBAND)) {
1811             arg_graphics = static_cast<byte>(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, NULL, 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(NULL);
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(NULL);
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(NULL);
2349         return 0;
2350     }
2351     case WM_QUIT: {
2352         quit(NULL);
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
2501     UnregisterClassW(AppName, hInstance);
2502     if (hIcon)
2503         DestroyIcon(hIcon);
2504
2505     exit(0);
2506 }
2507
2508 /*!
2509  * @brief Init some stuff
2510  */
2511 static void init_stuff(void)
2512 {
2513     char path[MAIN_WIN_MAX_PATH];
2514     DWORD path_len = GetModuleFileNameA(hInstance, path, MAIN_WIN_MAX_PATH);
2515     strcpy(path + path_len - 4, ".INI");
2516     ini_file = string_make(path);
2517
2518     int i = path_len;
2519     for (; i > 0; i--) {
2520         if (path[i] == '\\') {
2521             break;
2522         }
2523     }
2524
2525     strcpy(path + i + 1, "lib\\");
2526     validate_dir(path, true);
2527     init_file_paths(path, path);
2528     validate_dir(ANGBAND_DIR_APEX, false);
2529     validate_dir(ANGBAND_DIR_BONE, false);
2530     if (!check_dir(ANGBAND_DIR_EDIT)) {
2531         validate_dir(ANGBAND_DIR_DATA, true);
2532     } else {
2533         validate_dir(ANGBAND_DIR_DATA, false);
2534     }
2535
2536     validate_dir(ANGBAND_DIR_FILE, true);
2537     validate_dir(ANGBAND_DIR_HELP, false);
2538     validate_dir(ANGBAND_DIR_INFO, false);
2539     validate_dir(ANGBAND_DIR_PREF, true);
2540     validate_dir(ANGBAND_DIR_SAVE, false);
2541     validate_dir(ANGBAND_DIR_DEBUG_SAVE, false);
2542     validate_dir(ANGBAND_DIR_USER, true);
2543     validate_dir(ANGBAND_DIR_XTRA, true);
2544     path_build(path, sizeof(path), ANGBAND_DIR_FILE, _("news_j.txt", "news.txt"));
2545
2546     validate_file(path);
2547     path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "graf");
2548     ANGBAND_DIR_XTRA_GRAF = string_make(path);
2549     validate_dir(ANGBAND_DIR_XTRA_GRAF, true);
2550
2551     path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "sound");
2552     ANGBAND_DIR_XTRA_SOUND = string_make(path);
2553     validate_dir(ANGBAND_DIR_XTRA_SOUND, false);
2554
2555     path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "music");
2556     ANGBAND_DIR_XTRA_MUSIC = string_make(path);
2557     validate_dir(ANGBAND_DIR_XTRA_MUSIC, false);
2558
2559     for (i = 0; special_key_list[i]; ++i) {
2560         special_key[special_key_list[i]] = true;
2561     }
2562
2563     for (i = 0; ignore_key_list[i]; ++i) {
2564         ignore_key[ignore_key_list[i]] = true;
2565     }
2566
2567     ANGBAND_SYS = "win";
2568     if (7 != GetKeyboardType(0))
2569         ANGBAND_KEYBOARD = "0";
2570     else {
2571         switch (GetKeyboardType(1)) {
2572         case 0x0D01:
2573         case 0x0D02:
2574         case 0x0D03:
2575         case 0x0D04:
2576         case 0x0D05:
2577         case 0x0D06:
2578             /* NEC PC-98x1 */
2579             ANGBAND_KEYBOARD = "NEC98";
2580             break;
2581         default:
2582             /* PC/AT */
2583             ANGBAND_KEYBOARD = "JAPAN";
2584         }
2585     }
2586 }
2587
2588 /*!
2589  * @brief 全スポイラー出力を行う
2590  * Create Spoiler files
2591  * @details スポイラー出力処理の成功、失敗に関わらずプロセスを終了する。
2592  */
2593 void create_debug_spoiler(void)
2594 {
2595     init_stuff();
2596     init_angband(p_ptr, true);
2597
2598     switch (output_all_spoilers()) {
2599     case spoiler_output_status::SPOILER_OUTPUT_SUCCESS:
2600         fprintf(stdout, "Successfully created a spoiler file.");
2601     case spoiler_output_status::SPOILER_OUTPUT_FAIL_FOPEN:
2602         fprintf(stderr, "Cannot create spoiler file.");
2603     case spoiler_output_status::SPOILER_OUTPUT_FAIL_FCLOSE:
2604         fprintf(stderr, "Cannot close spoiler file.");
2605     default:
2606         break;
2607     }
2608
2609     quit(NULL);
2610 }
2611
2612 /*!
2613  * @brief メインウインドウ、サブウインドウのウインドウクラス登録
2614  */
2615 static void register_wndclass(void)
2616 {
2617     WNDCLASSW wc{};
2618     wc.style = CS_CLASSDC;
2619     wc.lpfnWndProc = AngbandWndProc;
2620     wc.cbClsExtra = 0;
2621     wc.cbWndExtra = 4;
2622     wc.hInstance = hInstance;
2623     wc.hIcon = hIcon = LoadIconW(hInstance, AppName);
2624     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
2625     wc.hbrBackground = NULL;
2626     wc.lpszMenuName = AppName;
2627     wc.lpszClassName = AppName;
2628
2629     if (!RegisterClassW(&wc))
2630         exit(1);
2631
2632     wc.lpfnWndProc = AngbandListProc;
2633     wc.lpszMenuName = NULL;
2634     wc.lpszClassName = AngList;
2635
2636     if (!RegisterClassW(&wc))
2637         exit(2);
2638 }
2639
2640 /*!
2641  * @brief (Windows固有)Windowsアプリケーションとしてのエントリポイント
2642  */
2643 int WINAPI WinMain(
2644     _In_ HINSTANCE hInst, [[maybe_unused]] _In_opt_ HINSTANCE hPrevInst, [[maybe_unused]] _In_ LPSTR lpCmdLine, [[maybe_unused]] _In_ int nCmdShow)
2645 {
2646     setlocale(LC_ALL, "ja_JP");
2647     hInstance = hInst;
2648     if (is_already_running()) {
2649         MessageBoxW(
2650             NULL, _(L"変愚蛮怒はすでに起動しています。", L"Hengband is already running."), _(L"エラー!", L"Error"), MB_ICONEXCLAMATION | MB_OK | MB_ICONSTOP);
2651         return 0;
2652     }
2653
2654     command_line.handle();
2655     register_wndclass();
2656
2657     // before term_data initialize
2658     plog_aux = [](concptr str) {
2659         if (str)
2660             MessageBoxW(NULL, to_wchar(str).wc_str(), _(L"警告!", L"Warning"), MB_ICONEXCLAMATION | MB_OK);
2661     };
2662     quit_aux = [](concptr str) {
2663         if (str) {
2664             MessageBoxW(NULL, to_wchar(str).wc_str(), _(L"エラー!", L"Error"), MB_ICONEXCLAMATION | MB_OK | MB_ICONSTOP);
2665         }
2666
2667         UnregisterClassW(AppName, hInstance);
2668         if (hIcon)
2669             DestroyIcon(hIcon);
2670
2671         exit(0);
2672     };
2673     core_aux = quit_aux;
2674
2675     init_stuff();
2676
2677     HDC hdc = GetDC(NULL);
2678     if (GetDeviceCaps(hdc, BITSPIXEL) <= 8) {
2679         quit(_("画面を16ビット以上のカラーモードにして下さい。", "Please switch to High Color (16-bit) or higher color mode."));
2680     }
2681     ReleaseDC(NULL, hdc);
2682
2683     refresh_color_table();
2684     init_windows();
2685     change_graphics_mode(static_cast<graphics_mode>(arg_graphics));
2686     change_bg_mode(current_bg_mode, true);
2687
2688     // after term_data initialize
2689     plog_aux = hook_plog;
2690     quit_aux = hook_quit;
2691     core_aux = hook_quit;
2692
2693     signals_init();
2694     term_activate(term_screen);
2695     init_angband(p_ptr, false);
2696     initialized = true;
2697
2698     check_for_save_file(command_line.get_savefile_option());
2699     prt(_("[ファイル] メニューの [新規] または [開く] を選択してください。", "[Choose 'New' or 'Open' from the 'File' menu]"), 23, _(8, 17));
2700     term_fresh();
2701
2702     change_sound_mode(arg_sound);
2703     use_music = arg_music;
2704     if (use_music) {
2705         init_music();
2706     }
2707
2708     // ユーザーがゲーム開始を選択するまで待つループ
2709     MSG msg;
2710     while (GetMessage(&msg, NULL, 0, 0)) {
2711         TranslateMessage(&msg);
2712         DispatchMessage(&msg);
2713         if (game_in_progress || movie_in_progress)
2714             break;
2715     }
2716
2717     term_flush();
2718     if (movie_in_progress) {
2719         // selected movie
2720         play_game(p_ptr, false, true);
2721     } else if (savefile[0] == '\0') {
2722         // new game
2723         play_game(p_ptr, true, false);
2724     } else {
2725         // selected savefile
2726         play_game(p_ptr, false, false);
2727     }
2728
2729     quit(NULL);
2730     return 0;
2731 }
2732 #endif /* WINDOWS */