OSDN Git Service

[Refactor] #2807 Renamed monster-race-definition.h to monster-race-info.h
[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/monster-race-info.h"
22 #include "system/player-type-definition.h"
23 #include "term/screen-processor.h"
24 #include "util/angband-files.h"
25 #include "view/display-messages.h"
26
27 concptr ANGBAND_DIR; //!< Path name: The main "lib" directory This variable is not actually used anywhere in the code
28 concptr ANGBAND_DIR_APEX; //!< High score files (binary) These files may be portable between platforms
29 concptr ANGBAND_DIR_BONE; //!< Bone files for player ghosts (ascii) These files are portable between platforms
30 concptr ANGBAND_DIR_DATA; //!< Binary image files for the "*_info" arrays (binary) These files are not portable between platforms
31 concptr ANGBAND_DIR_EDIT; //!< Textual template files for the "*_info" arrays (ascii) These files are portable between platforms
32 concptr ANGBAND_DIR_SCRIPT; //!< Script files These files are portable between platforms.
33 concptr ANGBAND_DIR_FILE; //!< Various extra files (ascii) These files may be portable between platforms
34 concptr ANGBAND_DIR_HELP; //!< Help files (normal) for the online help (ascii) These files are portable between platforms
35 concptr ANGBAND_DIR_INFO; //!< Help files (spoilers) for the online help (ascii) These files are portable between platforms
36 concptr ANGBAND_DIR_PREF; //!< Default user "preference" files (ascii) These files are rarely portable between platforms
37 concptr ANGBAND_DIR_SAVE; //!< Savefiles for current characters (binary)
38 concptr ANGBAND_DIR_DEBUG_SAVE; //*< Savefiles for debug data
39 concptr ANGBAND_DIR_USER; //!< User "preference" files (ascii) These files are rarely portable between platforms
40 concptr ANGBAND_DIR_XTRA; //!< Various extra files (binary) These files are rarely portable between platforms
41
42 /*
43  * Buffer to hold the current savefile name
44  * 'savefile' holds full path name. 'savefile_base' holds only base name.
45  */
46 char savefile[1024];
47 char savefile_base[40];
48 char debug_savefile[1024];
49
50 /*!
51  * @brief プレイヤーステータスをファイルダンプ出力する
52  * Hack -- Dump a character description file
53  * @param player_ptr プレイヤーへの参照ポインタ
54  * @param name 出力ファイル名
55  * @return エラーコード
56  * @details
57  * Allow the "full" flag to dump additional info,
58  * and trigger its usage from various places in the code.
59  */
60 errr file_character(PlayerType *player_ptr, concptr name)
61 {
62     char buf[1024];
63     path_build(buf, sizeof(buf), ANGBAND_DIR_USER, name);
64     int fd = fd_open(buf, O_RDONLY);
65     if (fd >= 0) {
66         char out_val[sizeof(buf) + 128];
67         (void)fd_close(fd);
68         (void)sprintf(out_val, _("現存するファイル %s に上書きしますか? ", "Replace existing file %s? "), buf);
69         if (get_check_strict(player_ptr, out_val, CHECK_NO_HISTORY)) {
70             fd = -1;
71         }
72     }
73
74     FILE *fff = nullptr;
75     if (fd < 0) {
76         fff = angband_fopen(buf, "w");
77     }
78
79     if (!fff) {
80         prt(_("キャラクタ情報のファイルへの書き出しに失敗しました!", "Character dump failed!"), 0, 0);
81         (void)inkey();
82         return -1;
83     }
84
85     make_character_dump(player_ptr, fff);
86     angband_fclose(fff);
87     msg_print(_("キャラクタ情報のファイルへの書き出しに成功しました。", "Character dump successful."));
88     msg_print(nullptr);
89     return 0;
90 }
91
92 /*!
93  * @brief ファイルからランダムに行を一つ取得する /
94  * Get a random line from a file
95  * @param file_name ファイル名
96  * @param entry 特定条件時のN:タグヘッダID
97  * @param output 出力先の文字列参照ポインタ
98  * @return エラーコード
99  * @details
100  * <pre>
101  * Based on the monster speech patch by Matt Graham,
102  * </pre>
103  */
104 errr get_rnd_line(concptr file_name, int entry, char *output)
105 {
106     char buf[1024];
107     path_build(buf, sizeof(buf), ANGBAND_DIR_FILE, file_name);
108     FILE *fp;
109     fp = angband_fopen(buf, "r");
110     if (!fp) {
111         return -1;
112     }
113
114     int test;
115     int line_num = 0;
116     while (true) {
117         if (angband_fgets(fp, buf, sizeof(buf)) != 0) {
118             angband_fclose(fp);
119             return -1;
120         }
121
122         line_num++;
123         if ((buf[0] != 'N') || (buf[1] != ':')) {
124             continue;
125         }
126
127         if (buf[2] == '*') {
128             break;
129         } else if (buf[2] == 'M') {
130             if (monraces_info[i2enum<MonsterRaceId>(entry)].flags1 & RF1_MALE) {
131                 break;
132             }
133         } else if (buf[2] == 'F') {
134             if (monraces_info[i2enum<MonsterRaceId>(entry)].flags1 & RF1_FEMALE) {
135                 break;
136             }
137         } else if (sscanf(&(buf[2]), "%d", &test) != EOF) {
138             if (test == entry) {
139                 break;
140             }
141         } else {
142             msg_format("Error in line %d of %s!", line_num, file_name);
143             angband_fclose(fp);
144             return -1;
145         }
146     }
147
148     int counter;
149     for (counter = 0;; counter++) {
150         while (true) {
151             test = angband_fgets(fp, buf, sizeof(buf));
152             if (!test) {
153                 /* Ignore lines starting with 'N:' */
154                 if ((buf[0] == 'N') && (buf[1] == ':')) {
155                     continue;
156                 }
157
158                 if (buf[0] != '#') {
159                     break;
160                 }
161             } else {
162                 break;
163             }
164         }
165
166         if (!buf[0]) {
167             break;
168         }
169
170         if (one_in_(counter + 1)) {
171             strcpy(output, buf);
172         }
173     }
174
175     angband_fclose(fp);
176     return counter ? 0 : -1;
177 }
178
179 #ifdef JP
180 /*!
181  * @brief ファイルからランダムに行を一つ取得する(日本語文字列のみ) /
182  * @param file_name ファイル名
183  * @param entry 特定条件時のN:タグヘッダID
184  * @param output 出力先の文字列参照ポインタ
185  * @param count 試行回数
186  * @return エラーコード
187  * @details
188  */
189 errr get_rnd_line_jonly(concptr file_name, int entry, char *output, int count)
190 {
191     errr result = 1;
192     for (int i = 0; i < count; i++) {
193         result = get_rnd_line(file_name, entry, output);
194         if (result) {
195             break;
196         }
197         bool kanji = false;
198         for (int j = 0; output[j]; j++) {
199             kanji |= iskanji(output[j]);
200         }
201         if (kanji) {
202             break;
203         }
204     }
205
206     return result;
207 }
208 #endif
209
210 /*!
211  * @brief ファイル位置をシーク /
212  * @param player_ptr プレイヤーへの参照ポインタ
213  * @param fd ファイルディスクリプタ
214  * @param where ファイルバイト位置
215  * @param flag FALSEならば現ファイルを超えた位置へシーク時エラー、TRUEなら足りない間を0で埋め尽くす
216  * @return エラーコード
217  * @details
218  */
219 static errr counts_seek(PlayerType *player_ptr, int fd, uint32_t where, bool flag)
220 {
221     char temp1[128], temp2[128];
222     auto short_pclass = enum2i(player_ptr->pclass);
223 #ifdef SAVEFILE_USE_UID
224     (void)sprintf(temp1, "%d.%s.%d%d%d", player_ptr->player_uid, savefile_base, short_pclass, player_ptr->ppersonality, player_ptr->age);
225 #else
226     (void)sprintf(temp1, "%s.%d%d%d", savefile_base, short_pclass, player_ptr->ppersonality, player_ptr->age);
227 #endif
228     for (int i = 0; temp1[i]; i++) {
229         temp1[i] ^= (i + 1) * 63;
230     }
231
232     int seekpoint = 0;
233     uint32_t zero_header[3] = { 0L, 0L, 0L };
234     while (true) {
235         if (fd_seek(fd, seekpoint + 3 * sizeof(uint32_t))) {
236             return 1;
237         }
238         if (fd_read(fd, (char *)(temp2), sizeof(temp2))) {
239             if (!flag) {
240                 return 1;
241             }
242             /* add new name */
243             fd_seek(fd, seekpoint);
244             fd_write(fd, (char *)zero_header, 3 * sizeof(uint32_t));
245             fd_write(fd, (char *)(temp1), sizeof(temp1));
246             break;
247         }
248
249         if (strcmp(temp1, temp2) == 0) {
250             break;
251         }
252
253         seekpoint += 128 + 3 * sizeof(uint32_t);
254     }
255
256     return fd_seek(fd, seekpoint + where * sizeof(uint32_t));
257 }
258
259 /*!
260  * @brief ファイル位置を読み込む
261  * @param player_ptr プレイヤーへの参照ポインタ
262  * @param where ファイルバイト位置
263  * @return エラーコード
264  * @details
265  */
266 uint32_t counts_read(PlayerType *player_ptr, int where)
267 {
268     char buf[1024];
269     path_build(buf, sizeof(buf), ANGBAND_DIR_DATA, _("z_info_j.raw", "z_info.raw"));
270     int fd = fd_open(buf, O_RDONLY);
271
272     uint32_t count = 0;
273     if (counts_seek(player_ptr, fd, where, false) || fd_read(fd, (char *)(&count), sizeof(uint32_t))) {
274         count = 0;
275     }
276
277     (void)fd_close(fd);
278
279     return count;
280 }
281
282 /*!
283  * @brief ファイル位置に書き込む /
284  * @param player_ptr プレイヤーへの参照ポインタ
285  * @param where ファイルバイト位置
286  * @param count 書き込む値
287  * @return エラーコード
288  * @details
289  */
290 errr counts_write(PlayerType *player_ptr, int where, uint32_t count)
291 {
292     char buf[1024];
293     path_build(buf, sizeof(buf), ANGBAND_DIR_DATA, _("z_info_j.raw", "z_info.raw"));
294
295     safe_setuid_grab(player_ptr);
296     int fd = fd_open(buf, O_RDWR);
297     safe_setuid_drop();
298     if (fd < 0) {
299         safe_setuid_grab(player_ptr);
300         fd = fd_make(buf, 0644);
301         safe_setuid_drop();
302     }
303
304     safe_setuid_grab(player_ptr);
305     errr err = fd_lock(fd, F_WRLCK);
306     safe_setuid_drop();
307     if (err) {
308         return 1;
309     }
310
311     counts_seek(player_ptr, fd, where, true);
312     fd_write(fd, (char *)(&count), sizeof(uint32_t));
313     safe_setuid_grab(player_ptr);
314     err = fd_lock(fd, F_UNLCK);
315     safe_setuid_drop();
316
317     if (err) {
318         return 1;
319     }
320
321     (void)fd_close(fd);
322     return 0;
323 }
324
325 /*!
326  * @brief 墓のアスキーアートテンプレを読み込む
327  * @param buf テンプレへのバッファ
328  * @param buf_size バッファの長さ
329  */
330 void read_dead_file(char *buf, size_t buf_size)
331 {
332     path_build(buf, buf_size, ANGBAND_DIR_FILE, _("dead_j.txt", "dead.txt"));
333
334     FILE *fp;
335     fp = angband_fopen(buf, "r");
336     if (!fp) {
337         return;
338     }
339
340     int i = 0;
341     while (angband_fgets(fp, buf, buf_size) == 0) {
342         put_str(buf, i++, 0);
343     }
344
345     angband_fclose(fp);
346 }