OSDN Git Service

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