OSDN Git Service

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