OSDN Git Service

[Implement] ウサウサストライカー召喚処理を追加
[hengbandforosx/hengbandosx.git] / src / io / files-util.cpp
1 /*!
2  * @brief ファイル入出力管理 / Purpose: code dealing with files (and death)
3  * @date 2014/01/28
4  * @author
5  * <pre>
6  * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
7  * This software may be copied and distributed for educational, research,
8  * and not for profit purposes provided that this copyright and statement
9  * are included in all such copies.  Other copyrights may also apply.
10  * 2014 Deskull rearranged comment for Doxygen.\n
11  * </pre>
12  */
13
14 #include "io/files-util.h"
15 #include "core/asking-player.h"
16 #include "io-dump/character-dump.h"
17 #include "io/input-key-acceptor.h"
18 #include "io/uid-checker.h"
19 #include "monster-race/monster-race.h"
20 #include "monster-race/race-flags1.h"
21 #include "system/angband-exceptions.h"
22 #include "system/monster-race-info.h"
23 #include "system/player-type-definition.h"
24 #include "term/screen-processor.h"
25 #include "term/z-form.h"
26 #include "util/angband-files.h"
27 #include "view/display-messages.h"
28 #include <algorithm>
29 #ifdef SAVEFILE_USE_UID
30 #include "main-unix/unix-user-ids.h"
31 #endif
32
33 std::filesystem::path ANGBAND_DIR; //!< Path name: The main "lib" directory This variable is not actually used anywhere in the code
34 std::filesystem::path ANGBAND_DIR_APEX; //!< High score files (binary) These files may be portable between platforms
35 std::filesystem::path ANGBAND_DIR_BONE; //!< Bone files for player ghosts (ascii) These files are portable between platforms
36 std::filesystem::path ANGBAND_DIR_DATA; //!< Binary image files for the "*_info" arrays (binary) These files are not portable between platforms
37 std::filesystem::path ANGBAND_DIR_EDIT; //!< Textual template files for the "*_info" arrays (ascii) These files are portable between platforms
38 std::filesystem::path ANGBAND_DIR_SCRIPT; //!< Script files These files are portable between platforms.
39 std::filesystem::path ANGBAND_DIR_FILE; //!< Various extra files (ascii) These files may be portable between platforms
40 std::filesystem::path ANGBAND_DIR_HELP; //!< Help files (normal) for the online help (ascii) These files are portable between platforms
41 std::filesystem::path ANGBAND_DIR_INFO; //!< Help files (spoilers) for the online help (ascii) These files are portable between platforms
42 std::filesystem::path ANGBAND_DIR_PREF; //!< Default user "preference" files (ascii) These files are rarely portable between platforms
43 std::filesystem::path ANGBAND_DIR_SAVE; //!< Savefiles for current characters (binary)
44 std::filesystem::path ANGBAND_DIR_DEBUG_SAVE; //*< Savefiles for debug data
45 std::filesystem::path ANGBAND_DIR_USER; //!< User "preference" files (ascii) These files are rarely portable between platforms
46 std::filesystem::path ANGBAND_DIR_XTRA; //!< Various extra files (binary) These files are rarely portable between platforms
47
48 std::filesystem::path savefile;
49 std::string savefile_base;
50 std::filesystem::path debug_savefile;
51
52 /*!
53  * @brief プレイヤーステータスをファイルダンプ出力する
54  * Hack -- Dump a character description file
55  * @param player_ptr プレイヤーへの参照ポインタ
56  * @param name 出力ファイル名
57  * @return エラーコード
58  * @details
59  * Allow the "full" flag to dump additional info,
60  * and trigger its usage from various places in the code.
61  */
62 void file_character(PlayerType *player_ptr, std::string_view filename)
63 {
64     const auto &path = path_build(ANGBAND_DIR_USER, filename);
65     auto fd = fd_open(path, O_RDONLY);
66     if (fd >= 0) {
67         const auto &path_str = path.string();
68         std::stringstream ss;
69         ss << _("現存するファイル ", "Replace existing file ") << path_str << _(" に上書きしますか? ", "? ");
70         (void)fd_close(fd);
71         if (input_check_strict(player_ptr, ss.str(), UserCheck::NO_HISTORY)) {
72             fd = -1;
73         } else {
74             return;
75         }
76     }
77
78     FILE *fff = nullptr;
79     if (fd < 0) {
80         fff = angband_fopen(path, FileOpenMode::WRITE);
81     }
82
83     constexpr auto error_msg = _("キャラクタ情報のファイルへの書き出しに失敗しました!", "Character dump failed!");
84     if (!fff) {
85         msg_print(error_msg);
86         msg_print(nullptr);
87         return;
88     }
89
90     screen_save();
91     make_character_dump(player_ptr, fff);
92     screen_load();
93
94     if (ferror(fff)) {
95         angband_fclose(fff);
96         msg_print(error_msg);
97         msg_print(nullptr);
98         return;
99     }
100
101     angband_fclose(fff);
102     msg_print(_("キャラクタ情報のファイルへの書き出しに成功しました。", "Character dump successful."));
103     msg_print(nullptr);
104 }
105
106 /*!
107  * @brief ファイルからランダムに行を一つ取得する
108  * @param file_name ファイル名
109  * @param entry 特定条件時のN:タグヘッダID
110  * @return ファイルから取得した行 (但しファイルがなかったり異常値ならばnullopt)
111  */
112 std::optional<std::string> get_random_line(concptr file_name, int entry)
113 {
114     const auto &path = path_build(ANGBAND_DIR_FILE, file_name);
115     auto *fp = angband_fopen(path, FileOpenMode::READ);
116     if (!fp) {
117         return std::nullopt;
118     }
119
120     int test;
121     auto line_num = 0;
122     while (true) {
123         char buf[1024];
124         if (angband_fgets(fp, buf, sizeof(buf)) != 0) {
125             angband_fclose(fp);
126             return std::nullopt;
127         }
128
129         line_num++;
130         if ((buf[0] != 'N') || (buf[1] != ':')) {
131             continue;
132         }
133
134         if (buf[2] == '*') {
135             break;
136         }
137
138         if (buf[2] == 'M') {
139             if (is_male(monraces_info[i2enum<MonsterRaceId>(entry)])) {
140                 break;
141             }
142         } else if (buf[2] == 'F') {
143             if (is_female(monraces_info[i2enum<MonsterRaceId>(entry)])) {
144                 break;
145             }
146         } else if (sscanf(&(buf[2]), "%d", &test) != EOF) {
147             if (test == entry) {
148                 break;
149             }
150         } else {
151             msg_format("Error in line %d of %s!", line_num, file_name);
152             angband_fclose(fp);
153             return std::nullopt;
154         }
155     }
156
157     auto counter = 0;
158     std::optional<std::string> line{};
159     while (true) {
160         char buf[1024];
161         while (true) {
162             if (angband_fgets(fp, buf, sizeof(buf))) {
163                 break;
164             }
165
166             if ((buf[0] == 'N') && (buf[1] == ':')) {
167                 continue;
168             }
169
170             if (buf[0] != '#') {
171                 break;
172             }
173         }
174
175         if (!buf[0]) {
176             break;
177         }
178
179         if (one_in_(counter + 1)) {
180             line = buf;
181         }
182
183         counter++;
184     }
185
186     angband_fclose(fp);
187     return line;
188 }
189
190 #ifdef JP
191 /*!
192  * @brief ファイルからランダムに行を一つ取得する(日本語文字列のみ)
193  * @param file_name ファイル名
194  * @param entry 特定条件時のN:タグヘッダID
195  * @param count 試行回数
196  * @return ファイルから取得した行 (但しファイルがなかったり異常値ならばnullopt)
197  * @details
198  */
199 std::optional<std::string> get_random_line_ja_only(concptr file_name, int entry, int count)
200 {
201     std::optional<std::string> line;
202     for (auto i = 0; i < count; i++) {
203         line = get_random_line(file_name, entry);
204         if (!line) {
205             return std::nullopt;
206         }
207
208         auto is_kanji = false;
209         for (const auto c : *line) {
210             is_kanji |= iskanji(c);
211         }
212
213         if (is_kanji) {
214             return line;
215         }
216     }
217
218     return line;
219 }
220 #endif
221
222 /*!
223  * @brief ファイル位置をシーク /
224  * @param player_ptr プレイヤーへの参照ポインタ
225  * @param fd ファイルディスクリプタ
226  * @param where ファイルバイト位置
227  * @param flag FALSEならば現ファイルを超えた位置へシーク時エラー、TRUEなら足りない間を0で埋め尽くす
228  * @return エラーコード
229  * @details
230  */
231 static errr counts_seek(PlayerType *player_ptr, int fd, uint32_t where, bool flag)
232 {
233     char temp1[128]{}, temp2[128]{};
234     auto short_pclass = enum2i(player_ptr->pclass);
235 #ifdef SAVEFILE_USE_UID
236     const auto user_id = UnixUserIds::get_instance().get_user_id();
237     strnfmt(temp1, sizeof(temp1), "%d.%s.%d%d%d", user_id, savefile_base.data(), short_pclass, player_ptr->ppersonality, player_ptr->age);
238 #else
239     strnfmt(temp1, sizeof(temp1), "%s.%d%d%d", savefile_base.data(), short_pclass, player_ptr->ppersonality, player_ptr->age);
240 #endif
241     for (int i = 0; temp1[i]; i++) {
242         temp1[i] ^= (i + 1) * 63;
243     }
244
245     int seekpoint = 0;
246     uint32_t zero_header[3] = { 0L, 0L, 0L };
247     while (true) {
248         if (fd_seek(fd, seekpoint + 3 * sizeof(uint32_t))) {
249             return 1;
250         }
251         if (fd_read(fd, (char *)(temp2), sizeof(temp2))) {
252             if (!flag) {
253                 return 1;
254             }
255             /* add new name */
256             fd_seek(fd, seekpoint);
257             fd_write(fd, (char *)zero_header, 3 * sizeof(uint32_t));
258             fd_write(fd, (char *)(temp1), sizeof(temp1));
259             break;
260         }
261
262         if (strcmp(temp1, temp2) == 0) {
263             break;
264         }
265
266         seekpoint += 128 + 3 * sizeof(uint32_t);
267     }
268
269     return fd_seek(fd, seekpoint + where * sizeof(uint32_t));
270 }
271
272 /*!
273  * @brief ファイル位置を読み込む
274  * @param player_ptr プレイヤーへの参照ポインタ
275  * @param where ファイルバイト位置
276  * @return エラーコード
277  * @details
278  */
279 uint32_t counts_read(PlayerType *player_ptr, int where)
280 {
281     const auto &path = path_build(ANGBAND_DIR_DATA, _("z_info_j.raw", "z_info.raw"));
282     auto fd = fd_open(path, O_RDONLY);
283     uint32_t count = 0;
284     if (counts_seek(player_ptr, fd, where, false) || fd_read(fd, (char *)(&count), sizeof(uint32_t))) {
285         count = 0;
286     }
287
288     (void)fd_close(fd);
289     return count;
290 }
291
292 /*!
293  * @brief ファイル位置に書き込む /
294  * @param player_ptr プレイヤーへの参照ポインタ
295  * @param where ファイルバイト位置
296  * @param count 書き込む値
297  * @return エラーコード
298  * @details
299  */
300 errr counts_write(PlayerType *player_ptr, int where, uint32_t count)
301 {
302     const auto &path = path_build(ANGBAND_DIR_DATA, _("z_info_j.raw", "z_info.raw"));
303     safe_setuid_grab();
304     auto fd = fd_open(path, O_RDWR);
305     safe_setuid_drop();
306     if (fd < 0) {
307         safe_setuid_grab();
308         fd = fd_make(path);
309         safe_setuid_drop();
310     }
311
312     safe_setuid_grab();
313     auto err = fd_lock(fd, F_WRLCK);
314     safe_setuid_drop();
315     if (err) {
316         return 1;
317     }
318
319     counts_seek(player_ptr, fd, where, true);
320     fd_write(fd, (char *)(&count), sizeof(uint32_t));
321     safe_setuid_grab();
322     err = fd_lock(fd, F_UNLCK);
323     safe_setuid_drop();
324
325     if (err) {
326         return 1;
327     }
328
329     (void)fd_close(fd);
330     return 0;
331 }
332
333 /*!
334  * @brief 墓のアスキーアートテンプレを読み込んで画面に表示する
335  */
336 void read_dead_file()
337 {
338     const auto &path = path_build(ANGBAND_DIR_FILE, _("dead_j.txt", "dead.txt"));
339     auto *fp = angband_fopen(path, FileOpenMode::READ);
340     if (!fp) {
341         return;
342     }
343
344     int i = 0;
345     char buf[1024]{};
346     while (angband_fgets(fp, buf, sizeof(buf)) == 0) {
347         put_str(buf, i++, 0);
348     }
349
350     angband_fclose(fp);
351 }