OSDN Git Service

Merge pull request #3532 from sikabane-works/release/3.0.0.87-alpha
[hengbandforosx/hengbandosx.git] / src / io / read-pref-file.cpp
1 /*
2  * @brief プレイヤーのインターフェイスに関するコマンドの実装 / Interface commands
3  * @date 2023/04/30
4  * @author Mogami & Hourier
5  */
6
7 #include "io/read-pref-file.h"
8 #include "autopick/autopick-pref-processor.h"
9 #include "autopick/autopick-reader-writer.h"
10 #include "core/asking-player.h"
11 #include "io-dump/dump-remover.h"
12 #include "io/files-util.h"
13 #include "io/interpret-pref-file.h"
14 #include "io/pref-file-expressor.h"
15 #include "player-info/class-info.h"
16 #include "player-info/race-info.h"
17 #include "realm/realm-names-table.h"
18 #include "system/player-type-definition.h"
19 #include "term/z-form.h"
20 #include "util/angband-files.h"
21 #include "util/buffer-shaper.h"
22 #include "util/string-processor.h"
23 #include "view/display-messages.h"
24 #include "world/world.h"
25 #include <algorithm>
26 #include <filesystem>
27 #include <string>
28
29 //!< @todo コールバック関数に変更するので、いずれ消す.
30 #define PREF_TYPE_NORMAL 0
31 #define PREF_TYPE_AUTOPICK 1
32 #define PREF_TYPE_HISTPREF 2
33
34 char auto_dump_header[] = "# vvvvvvv== %s ==vvvvvvv";
35 char auto_dump_footer[] = "# ^^^^^^^== %s ==^^^^^^^";
36
37 // Mark strings for auto dump
38
39 // Variables for auto dump
40 static int auto_dump_line_num;
41
42 /*!
43  * @brief process_pref_fileのサブルーチン /
44  * Open the "user pref file" and parse it.
45  * @param player_ptr プレイヤーへの参照ポインタ
46  * @param name 読み込むファイル名
47  * @param preftype prefファイルのタイプ
48  * @return エラーコード
49  * @todo 関数名を変更する
50  */
51 static errr process_pref_file_aux(PlayerType *player_ptr, const std::filesystem::path &name, int preftype)
52 {
53     auto *fp = angband_fopen(name, FileOpenMode::READ);
54     if (!fp) {
55         return -1;
56     }
57
58     int line = -1;
59     errr err = 0;
60     bool bypass = false;
61     std::vector<char> file_read_buf(FILE_READ_BUFF_SIZE);
62     std::string error_line;
63     while (angband_fgets(fp, file_read_buf.data(), file_read_buf.size()) == 0) {
64         line++;
65         if (!file_read_buf[0]) {
66             continue;
67         }
68
69 #ifdef JP
70         if (!iskanji(file_read_buf[0]))
71 #endif
72             if (iswspace(file_read_buf[0])) {
73                 continue;
74             }
75
76         if (file_read_buf[0] == '#') {
77             continue;
78         }
79         error_line = file_read_buf.data();
80
81         /* Process "?:<expr>" */
82         if ((file_read_buf[0] == '?') && (file_read_buf[1] == ':')) {
83             char f;
84             char *s;
85             s = file_read_buf.data() + 2;
86             concptr v = process_pref_file_expr(player_ptr, &s, &f);
87             bypass = streq(v, "0");
88             continue;
89         }
90
91         if (bypass) {
92             continue;
93         }
94
95         /* Process "%:<file>" */
96         if (file_read_buf[0] == '%') {
97             static int depth_count = 0;
98             if (depth_count > 20) {
99                 continue;
100             }
101
102             depth_count++;
103             switch (preftype) {
104             case PREF_TYPE_AUTOPICK:
105                 (void)process_autopick_file(player_ptr, file_read_buf.data() + 2);
106                 break;
107             case PREF_TYPE_HISTPREF:
108                 (void)process_histpref_file(player_ptr, file_read_buf.data() + 2);
109                 break;
110             default:
111                 (void)process_pref_file(player_ptr, file_read_buf.data() + 2);
112                 break;
113             }
114
115             depth_count--;
116             continue;
117         }
118
119         err = interpret_pref_file(player_ptr, file_read_buf.data());
120         if (err != 0) {
121             if (preftype != PREF_TYPE_AUTOPICK) {
122                 break;
123             }
124
125             process_autopick_file_command(file_read_buf.data());
126             err = 0;
127         }
128     }
129
130     if (err != 0) {
131         /* Print error message */
132         /* ToDo: Add better error messages */
133         const auto &name_str = name.string();
134         msg_format(_("ファイル'%s'の%d行でエラー番号%dのエラー。", "Error %d in line %d of file '%s'."), _(name_str.data(), err), line, _(err, name_str.data()));
135         msg_format(_("('%s'を解析中)", "Parsing '%s'"), error_line.data());
136         msg_print(nullptr);
137     }
138
139     angband_fclose(fp);
140     return err;
141 }
142
143 /*!
144  * @brief pref設定ファイルを読み込み設定を反映させる /
145  * Process the "user pref file" with the given name
146  * @param player_ptr プレイヤーへの参照ポインタ
147  * @param name 読み込むファイル名
148  * @param only_user_dir trueを指定するとANGBAND_DIR_USERからの読み込みのみ行う
149  * @return エラーコード
150  * @details
151  * <pre>
152  * See the functions above for a list of legal "commands".
153  * We also accept the special "?" and "%" directives, which
154  * allow conditional evaluation and filename inclusion.
155  * </pre>
156  */
157 errr process_pref_file(PlayerType *player_ptr, std::string_view name, bool only_user_dir)
158 {
159     errr err1 = 0;
160     if (!only_user_dir) {
161         const auto &path = path_build(ANGBAND_DIR_PREF, name);
162         err1 = process_pref_file_aux(player_ptr, path, PREF_TYPE_NORMAL);
163         if (err1 > 0) {
164             return err1;
165         }
166     }
167
168     const auto &path = path_build(ANGBAND_DIR_USER, name);
169     errr err2 = process_pref_file_aux(player_ptr, path, PREF_TYPE_NORMAL);
170     if (err2 < 0 && !err1) {
171         return -2;
172     }
173
174     return err2;
175 }
176
177 /*!
178  * @brief 自動拾いファイルを読み込む /
179  * @param player_ptr プレイヤーへの参照ポインタ
180  * @param name ファイル名
181  * @details
182  */
183 errr process_autopick_file(PlayerType *player_ptr, std::string_view name)
184 {
185     const auto &path = path_build(ANGBAND_DIR_USER, name);
186     return process_pref_file_aux(player_ptr, path, PREF_TYPE_AUTOPICK);
187 }
188
189 /*!
190  * @brief プレイヤーの生い立ちファイルを読み込む /
191  * Process file for player's history editor.
192  * @param player_ptr プレイヤーへの参照ポインタ
193  * @param name ファイル名
194  * @return エラーコード
195  * @details
196  */
197 errr process_histpref_file(PlayerType *player_ptr, std::string_view name)
198 {
199     bool old_character_xtra = w_ptr->character_xtra;
200     const auto &path = path_build(ANGBAND_DIR_USER, name);
201     w_ptr->character_xtra = true;
202     errr err = process_pref_file_aux(player_ptr, path, PREF_TYPE_HISTPREF);
203     w_ptr->character_xtra = old_character_xtra;
204     return err;
205 }
206
207 /*!
208  * @brief prfファイルのフォーマットに従った内容を出力する /
209  * Dump a formatted line, using "vstrnfmt()".
210  * @param fmt 出力内容
211  */
212 void auto_dump_printf(FILE *auto_dump_stream, const char *fmt, ...)
213 {
214     va_list vp;
215     char buf[1024];
216     va_start(vp, fmt);
217     (void)vstrnfmt(buf, sizeof(buf), fmt, vp);
218     va_end(vp);
219     for (auto p = buf; *p; p++) {
220         if (*p == '\n') {
221             auto_dump_line_num++;
222         }
223     }
224
225     fprintf(auto_dump_stream, "%s", buf);
226 }
227
228 /*!
229  * @brief prfファイルをファイルオープンする /
230  * Open file to append auto dump.
231  * @param path ファイル名
232  * @param mark 出力するヘッダマーク
233  * @return ファイルポインタを取得できたらTRUEを返す
234  */
235 bool open_auto_dump(FILE **fpp, const std::filesystem::path &path, std::string_view mark)
236 {
237     char header_mark_str[80];
238     strnfmt(header_mark_str, sizeof(header_mark_str), auto_dump_header, mark.data());
239     remove_auto_dump(path, mark);
240     *fpp = angband_fopen(path, FileOpenMode::APPEND);
241     if (!fpp) {
242         const auto &path_str = path.string();
243         msg_format(_("%s を開くことができませんでした。", "Failed to open %s."), path_str.data());
244         msg_print(nullptr);
245         return false;
246     }
247
248     fprintf(*fpp, "%s\n", header_mark_str);
249     auto_dump_line_num = 0;
250     auto_dump_printf(*fpp, _("# *警告!!* 以降の行は自動生成されたものです。\n", "# *Warning!*  The lines below are an automatic dump.\n"));
251     auto_dump_printf(
252         *fpp, _("# *警告!!* 後で自動的に削除されるので編集しないでください。\n", "# Don't edit them; changes will be deleted and replaced automatically.\n"));
253     return true;
254 }
255
256 /*!
257  * @brief prfファイルをファイルクローズする /
258  * Append foot part and close auto dump.
259  * @param auto_dump_mark 出力するヘッダマーク
260  */
261 void close_auto_dump(FILE **fpp, std::string_view mark)
262 {
263     char footer_mark_str[80];
264     strnfmt(footer_mark_str, sizeof(footer_mark_str), auto_dump_footer, mark.data());
265     auto_dump_printf(*fpp, _("# *警告!!* 以降の行は自動生成されたものです。\n", "# *Warning!*  The lines below are an automatic dump.\n"));
266     auto_dump_printf(
267         *fpp, _("# *警告!!* 後で自動的に削除されるので編集しないでください。\n", "# Don't edit them; changes will be deleted and replaced automatically.\n"));
268     fprintf(*fpp, "%s (%d)\n", footer_mark_str, auto_dump_line_num);
269     angband_fclose(*fpp);
270 }
271
272 /*!
273  * @brief 全ユーザプロファイルをロードする / Load some "user pref files"
274  * @paaram player_ptr プレイヤーへの参照ポインタ
275  * @note
276  * Modified by Arcum Dagsson to support
277  * separate macro files for different realms.
278  */
279 void load_all_pref_files(PlayerType *player_ptr)
280 {
281     process_pref_file(player_ptr, "user.prf");
282     process_pref_file(player_ptr, std::string("user-").append(ANGBAND_SYS).append(".prf"));
283     process_pref_file(player_ptr, std::string(rp_ptr->title).append(".prf"));
284     process_pref_file(player_ptr, std::string(cp_ptr->title).append(".prf"));
285     process_pref_file(player_ptr, std::string(player_ptr->base_name).append(".prf"));
286     if (player_ptr->realm1 != REALM_NONE) {
287         process_pref_file(player_ptr, std::string(realm_names[player_ptr->realm1]).append(".prf"));
288     }
289
290     if (player_ptr->realm2 != REALM_NONE) {
291         process_pref_file(player_ptr, std::string(realm_names[player_ptr->realm2]).append(".prf"));
292     }
293
294     autopick_load_pref(player_ptr, false);
295 }
296
297 /*!
298  * @brief 生い立ちメッセージをファイルからロードする。
299  */
300 bool read_histpref(PlayerType *player_ptr)
301 {
302     errr err;
303     int i, j, n;
304     char *s;
305     char histbuf[HISTPREF_LIMIT];
306
307     if (!input_check(_("生い立ち設定ファイルをロードしますか? ", "Load background history preference file? "))) {
308         return false;
309     }
310
311     histbuf[0] = '\0';
312     histpref_buf = histbuf;
313
314     err = process_histpref_file(player_ptr, std::string(_("histedit-", "histpref-")).append(player_ptr->base_name).append(".prf").data());
315
316     if (0 > err) {
317         err = process_histpref_file(player_ptr, _("histedit.prf", "histpref.prf"));
318     }
319
320     if (err) {
321         msg_print(_("生い立ち設定ファイルの読み込みに失敗しました。", "Failed to load background history preference."));
322         msg_print(nullptr);
323         histpref_buf = nullptr;
324         return false;
325     } else if (!histpref_buf[0]) {
326         msg_print(_("有効な生い立ち設定はこのファイルにありません。", "There does not exist valid background history preference."));
327         msg_print(nullptr);
328         histpref_buf = nullptr;
329         return false;
330     }
331
332     for (i = 0; i < 4; i++) {
333         player_ptr->history[i][0] = '\0';
334     }
335
336     /* loop */
337     for (s = histpref_buf; *s == ' '; s++) {
338         ;
339     }
340
341     n = strlen(s);
342     while ((n > 0) && (s[n - 1] == ' ')) {
343         s[--n] = '\0';
344     }
345
346     constexpr auto max_line_len = sizeof(player_ptr->history[0]);
347     const auto history_lines = shape_buffer(s, max_line_len);
348     const auto max_lines = std::min<int>(4, history_lines.size());
349     for (auto l = 0; l < max_lines; ++l) {
350         angband_strcpy(player_ptr->history[l], history_lines[l], max_line_len);
351     }
352
353     for (i = 0; i < 4; i++) {
354         /* loop */
355         for (j = 0; player_ptr->history[i][j]; j++) {
356             ;
357         }
358
359         for (; j < 59; j++) {
360             player_ptr->history[i][j] = ' ';
361         }
362         player_ptr->history[i][59] = '\0';
363     }
364
365     histpref_buf = nullptr;
366     return true;
367 }