OSDN Git Service

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