OSDN Git Service

[Refactor] .cfgファイル読み込み処理のリファクタリング #635
[hengbandforosx/hengbandosx.git] / src / main-win / main-win-music.cpp
1 /*!
2  * @file main-win-music.cpp
3  * @brief Windows版固有実装(BGM)
4  */
5
6 #include "main-win/main-win-music.h"
7 #include "dungeon/quest.h"
8 #include "floor/floor-town.h"
9 #include "main-win/main-win-define.h"
10 #include "main-win/main-win-file-utils.h"
11 #include "main-win/main-win-mci.h"
12 #include "main-win/main-win-mmsystem.h"
13 #include "main-win/main-win-tokenizer.h"
14 #include "main/scene-table.h"
15 #include "term/z-term.h"
16 #include "util/angband-files.h"
17 #include "world/world.h"
18
19 static int current_music_type = TERM_XTRA_MUSIC_MUTE;
20 static int current_music_id = 0;
21 // current filename being played
22 static char current_music_path[MAIN_WIN_MAX_PATH];
23
24 /*
25  * Directory name
26  */
27 concptr ANGBAND_DIR_XTRA_MUSIC;
28
29 /*
30  * "music.cfg" data
31  */
32 CfgData *music_cfg_data;
33
34 namespace main_win_music {
35
36 /*!
37  * @brief action-valに対応する[Basic]セクションのキー名を取得する
38  * @param index "term_xtra()"の第2引数action-valに対応する値
39  * @param buf 使用しない
40  * @return 対応するキー名を返す
41  */
42 static concptr basic_key_at(int index, char *buf)
43 {
44     (void)buf;
45
46     if (index >= MUSIC_BASIC_MAX)
47         return NULL;
48
49     return angband_music_basic_name[index];
50 }
51
52 static inline DUNGEON_IDX get_dungeon_count()
53 {
54     return current_world_ptr->max_d_idx;
55 }
56
57 /*!
58  * @brief action-valに対応する[Dungeon]セクションのキー名を取得する
59  * @param index "term_xtra()"の第2引数action-valに対応する値
60  * @param buf バッファ
61  * @return 対応するキー名を返す
62  */
63 static concptr dungeon_key_at(int index, char *buf)
64 {
65     if (index >= get_dungeon_count())
66         return NULL;
67
68     sprintf(buf, "dungeon%03d", index);
69     return buf;
70 }
71
72 static inline QUEST_IDX get_quest_count()
73 {
74     return max_q_idx;
75 }
76
77 /*!
78  * @brief action-valに対応する[Quest]セクションのキー名を取得する
79  * @param index "term_xtra()"の第2引数action-valに対応する値
80  * @param buf バッファ
81  * @return 対応するキー名を返す
82  */
83 static concptr quest_key_at(int index, char *buf)
84 {
85     if (index >= get_quest_count())
86         return NULL;
87
88     sprintf(buf, "quest%03d", index);
89     return buf;
90 }
91
92 static inline TOWN_IDX get_town_count()
93 {
94     return max_towns;
95 }
96
97 /*!
98  * @brief action-valに対応する[Town]セクションのキー名を取得する
99  * @param index "term_xtra()"の第2引数action-valに対応する値
100  * @param buf バッファ
101  * @return 対応するキー名を返す
102  */
103 static concptr town_key_at(int index, char *buf)
104 {
105     if (index >= get_town_count())
106         return NULL;
107
108     sprintf(buf, "town%03d", index);
109     return buf;
110 }
111
112 /*!
113  * @brief BGMの設定を読み込む。
114  * @details
115  * "music_debug.cfg"ファイルを優先して読み込む。無ければ"music.cfg"ファイルを読み込む。
116  * この処理は複数回実行されることを想定していない。複数回実行した場合には前回読み込まれた設定のメモリは解放されない。
117  */
118 void load_music_prefs()
119 {
120     CfgReader reader(ANGBAND_DIR_XTRA_MUSIC, { "music_debug.cfg", "music.cfg" });
121
122     GetPrivateProfileString("Device", "type", "MPEGVideo", mci_device_type, _countof(mci_device_type), reader.get_cfg_path());
123
124     // clang-format off
125     music_cfg_data = reader.read_sections({
126         { "Basic", TERM_XTRA_MUSIC_BASIC, basic_key_at },
127         { "Dungeon", TERM_XTRA_MUSIC_DUNGEON, dungeon_key_at },
128         { "Quest", TERM_XTRA_MUSIC_QUEST, quest_key_at },
129         { "Town", TERM_XTRA_MUSIC_TOWN, town_key_at }
130         });
131     // clang-format on
132 }
133
134 /*
135  * Stop a music
136  */
137 errr stop_music(void)
138 {
139     mciSendCommand(mci_open_parms.wDeviceID, MCI_STOP, MCI_WAIT, 0);
140     mciSendCommand(mci_open_parms.wDeviceID, MCI_CLOSE, MCI_WAIT, 0);
141     current_music_type = TERM_XTRA_MUSIC_MUTE;
142     current_music_id = 0;
143     strcpy(current_music_path, "\0");
144     return 0;
145 }
146
147 /*
148  * Play a music
149  */
150 errr play_music(int type, int val)
151 {
152     if (type == TERM_XTRA_MUSIC_MUTE)
153         return stop_music();
154
155     if (current_music_type == type && current_music_id == val)
156         return 0; // now playing
157
158     concptr filename = music_cfg_data->get_rand(type, val);
159     if (!filename)
160         return 1; // no setting
161
162     char buf[MAIN_WIN_MAX_PATH];
163     path_build(buf, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_MUSIC, filename);
164
165     if (current_music_type != TERM_XTRA_MUSIC_MUTE)
166         if (0 == strcmp(current_music_path, buf))
167             return 0; // now playing same file
168
169     current_music_type = type;
170     current_music_id = val;
171     strcpy(current_music_path, buf);
172
173     mci_open_parms.lpstrDeviceType = mci_device_type;
174     mci_open_parms.lpstrElementName = buf;
175     mciSendCommand(mci_open_parms.wDeviceID, MCI_STOP, MCI_WAIT, 0);
176     mciSendCommand(mci_open_parms.wDeviceID, MCI_CLOSE, MCI_WAIT, 0);
177     mciSendCommand(mci_open_parms.wDeviceID, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT | MCI_NOTIFY, (DWORD)&mci_open_parms);
178     // Send MCI_PLAY in the notification event once MCI_OPEN is completed
179     return 0;
180 }
181
182 /*
183  * Play a music matches a situation
184  */
185 errr play_music_scene()
186 {
187     // テーブルの先頭から順に再生を試み、再生できたら抜ける
188     auto ite = get_scene_table_iterator();
189     const errr err_sucsess = 0;
190     while (play_music(ite->type, ite->val) != err_sucsess) {
191         ++ite;
192     }
193
194     return 0;
195 }
196
197 /*
198  * Notify event
199  */
200 void on_mci_notify(WPARAM wFlags, LONG lDevID)
201 {
202     UNREFERENCED_PARAMETER(lDevID);
203
204     if (wFlags == MCI_NOTIFY_SUCCESSFUL) {
205         // play a music (repeat)
206         mciSendCommand(mci_open_parms.wDeviceID, MCI_SEEK, MCI_SEEK_TO_START | MCI_WAIT, 0);
207         mciSendCommand(mci_open_parms.wDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)&mci_play_parms);
208     }
209 }
210
211 }