OSDN Git Service

[Fix] #37353 リファクタリング時にエンバグした get_rnd_line() の分岐処理を修正. / Fix a conditional branch...
[hengband/hengband.git] / src / files.c
1 /*!
2  * @file files.c
3  * @brief ファイル入出力管理 / Purpose: code dealing with files (and death)
4  * @date 2014/01/28
5  * @author
6  * <pre>
7  * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
8  * This software may be copied and distributed for educational, research,
9  * and not for profit purposes provided that this copyright and statement
10  * are included in all such copies.  Other copyrights may also apply.
11  * 2014 Deskull rearranged comment for Doxygen.\n
12  * </pre>
13  */
14
15 #include "angband.h"
16 #include "uid-checker.h"
17 #include "files.h"
18 #include "core.h" // 暫定。後で消す.
19 #include "character-dump.h"
20
21 concptr ANGBAND_DIR; //!< Path name: The main "lib" directory This variable is not actually used anywhere in the code
22 concptr ANGBAND_DIR_APEX; //!< High score files (binary) These files may be portable between platforms
23 concptr ANGBAND_DIR_BONE; //!< Bone files for player ghosts (ascii) These files are portable between platforms
24 concptr ANGBAND_DIR_DATA; //!< Binary image files for the "*_info" arrays (binary) These files are not portable between platforms
25 concptr ANGBAND_DIR_EDIT; //!< Textual template files for the "*_info" arrays (ascii) These files are portable between platforms
26 concptr ANGBAND_DIR_SCRIPT; //!< Script files These files are portable between platforms.
27 concptr ANGBAND_DIR_FILE; //!< Various extra files (ascii) These files may be portable between platforms
28 concptr ANGBAND_DIR_HELP; //!< Help files (normal) for the online help (ascii) These files are portable between platforms
29 concptr ANGBAND_DIR_INFO; //!< Help files (spoilers) for the online help (ascii) These files are portable between platforms
30 concptr ANGBAND_DIR_PREF; //!< Default user "preference" files (ascii) These files are rarely portable between platforms
31 concptr ANGBAND_DIR_SAVE; //!< Savefiles for current characters (binary)
32 concptr ANGBAND_DIR_USER; //!< User "preference" files (ascii) These files are rarely portable between platforms
33 concptr ANGBAND_DIR_XTRA; //!< Various extra files (binary) These files are rarely portable between platforms
34
35 /*
36  * Buffer to hold the current savefile name
37  * 'savefile' holds full path name. 'savefile_base' holds only base name.
38  */
39 char savefile[1024];
40 char savefile_base[40];
41
42 /*!
43  * todo サブルーチンとは言い難い。autopick.c から直接呼ばれている
44  * @brief process_pref_fileのサブルーチンとして条件分岐処理の解釈と結果を返す
45  * Helper function for "process_pref_file()"
46  * @param creature_ptr プレーヤーへの参照ポインタ
47  * @param sp テキスト文字列の参照ポインタ
48  * @param fp 再帰中のポインタ参照
49  * @return
50  * @details
51  * <pre>
52  * Input:
53  *   v: output buffer array
54  *   f: final character
55  * Output:
56  *   result
57  * </pre>
58  */
59 concptr process_pref_file_expr(player_type *creature_ptr, char **sp, char *fp)
60 {
61         char *s;
62         s = (*sp);
63         while (iswspace(*s)) s++;
64
65         char *b;
66         b = s;
67
68         concptr v = "?o?o?";
69
70         char b1 = '[';
71         char b2 = ']';
72         char f = ' ';
73         static char tmp[16];
74         if (*s == b1)
75         {
76                 concptr p;
77                 concptr t;
78
79                 /* Skip b1 */
80                 s++;
81
82                 /* First */
83                 t = process_pref_file_expr(creature_ptr, &s, &f);
84
85                 if (!*t)
86                 {
87                 }
88                 else if (streq(t, "IOR"))
89                 {
90                         v = "0";
91                         while (*s && (f != b2))
92                         {
93                                 t = process_pref_file_expr(creature_ptr, &s, &f);
94                                 if (*t && !streq(t, "0")) v = "1";
95                         }
96                 }
97                 else if (streq(t, "AND"))
98                 {
99                         v = "1";
100                         while (*s && (f != b2))
101                         {
102                                 t = process_pref_file_expr(creature_ptr, &s, &f);
103                                 if (*t && streq(t, "0")) v = "0";
104                         }
105                 }
106                 else if (streq(t, "NOT"))
107                 {
108                         v = "1";
109                         while (*s && (f != b2))
110                         {
111                                 t = process_pref_file_expr(creature_ptr, &s, &f);
112                                 if (*t && streq(t, "1")) v = "0";
113                         }
114                 }
115                 else if (streq(t, "EQU"))
116                 {
117                         v = "0";
118                         if (*s && (f != b2))
119                         {
120                                 t = process_pref_file_expr(creature_ptr, &s, &f);
121                         }
122                         while (*s && (f != b2))
123                         {
124                                 p = process_pref_file_expr(creature_ptr, &s, &f);
125                                 if (streq(t, p)) v = "1";
126                         }
127                 }
128                 else if (streq(t, "LEQ"))
129                 {
130                         v = "1";
131                         if (*s && (f != b2))
132                         {
133                                 t = process_pref_file_expr(creature_ptr, &s, &f);
134                         }
135                         while (*s && (f != b2))
136                         {
137                                 p = t;
138                                 t = process_pref_file_expr(creature_ptr, &s, &f);
139                                 if (*t && atoi(p) > atoi(t)) v = "0";
140                         }
141                 }
142                 else if (streq(t, "GEQ"))
143                 {
144                         v = "1";
145                         if (*s && (f != b2))
146                         {
147                                 t = process_pref_file_expr(creature_ptr, &s, &f);
148                         }
149                         while (*s && (f != b2))
150                         {
151                                 p = t;
152                                 t = process_pref_file_expr(creature_ptr, &s, &f);
153                                 if (*t && atoi(p) < atoi(t)) v = "0";
154                         }
155                 }
156                 else
157                 {
158                         while (*s && (f != b2))
159                         {
160                                 t = process_pref_file_expr(creature_ptr, &s, &f);
161                         }
162                 }
163
164                 if (f != b2) v = "?x?x?";
165
166                 if ((f = *s) != '\0') *s++ = '\0';
167
168                 *fp = f;
169                 *sp = s;
170                 return v;
171         }
172
173         /* Accept all printables except spaces and brackets */
174 #ifdef JP
175         while (iskanji(*s) || (isprint(*s) && !my_strchr(" []", *s)))
176         {
177                 if (iskanji(*s)) s++;
178                 s++;
179         }
180 #else
181         while (isprint(*s) && !my_strchr(" []", *s)) ++s;
182 #endif
183
184         if ((f = *s) != '\0') *s++ = '\0';
185
186         if (*b != '$')
187         {
188                 v = b;
189                 *fp = f;
190                 *sp = s;
191                 return v;
192         }
193
194         if (streq(b + 1, "SYS"))
195         {
196                 v = ANGBAND_SYS;
197         }
198         else if (streq(b + 1, "KEYBOARD"))
199         {
200                 v = ANGBAND_KEYBOARD;
201         }
202         else if (streq(b + 1, "GRAF"))
203         {
204                 v = ANGBAND_GRAF;
205         }
206         else if (streq(b + 1, "MONOCHROME"))
207         {
208                 if (arg_monochrome)
209                         v = "ON";
210                 else
211                         v = "OFF";
212         }
213         else if (streq(b + 1, "RACE"))
214         {
215 #ifdef JP
216                 v = rp_ptr->E_title;
217 #else
218                 v = rp_ptr->title;
219 #endif
220         }
221         else if (streq(b + 1, "CLASS"))
222         {
223 #ifdef JP
224                 v = cp_ptr->E_title;
225 #else
226                 v = cp_ptr->title;
227 #endif
228         }
229         else if (streq(b + 1, "PLAYER"))
230         {
231                 static char tmp_player_name[32];
232                 char *pn, *tpn;
233                 for (pn = creature_ptr->name, tpn = tmp_player_name; *pn; pn++, tpn++)
234                 {
235 #ifdef JP
236                         if (iskanji(*pn))
237                         {
238                                 *(tpn++) = *(pn++);
239                                 *tpn = *pn;
240                                 continue;
241                         }
242 #endif
243                         *tpn = my_strchr(" []", *pn) ? '_' : *pn;
244                 }
245
246                 *tpn = '\0';
247                 v = tmp_player_name;
248         }
249         else if (streq(b + 1, "REALM1"))
250         {
251 #ifdef JP
252                 v = E_realm_names[creature_ptr->realm1];
253 #else
254                 v = realm_names[creature_ptr->realm1];
255 #endif
256         }
257         else if (streq(b + 1, "REALM2"))
258         {
259 #ifdef JP
260                 v = E_realm_names[creature_ptr->realm2];
261 #else
262                 v = realm_names[creature_ptr->realm2];
263 #endif
264         }
265         else if (streq(b + 1, "LEVEL"))
266         {
267                 sprintf(tmp, "%02d", creature_ptr->lev);
268                 v = tmp;
269         }
270         else if (streq(b + 1, "AUTOREGISTER"))
271         {
272                 if (creature_ptr->autopick_autoregister)
273                         v = "1";
274                 else
275                         v = "0";
276         }
277         else if (streq(b + 1, "MONEY"))
278         {
279                 sprintf(tmp, "%09ld", (long int)creature_ptr->au);
280                 v = tmp;
281         }
282
283         *fp = f;
284         *sp = s;
285         return v;
286 }
287
288
289 /*!
290  * @brief プレイヤーステータスをファイルダンプ出力する
291  * Hack -- Dump a character description file
292  * @param creature_ptr プレーヤーへの参照ポインタ
293  * @param name 出力ファイル名
294  * @return エラーコード
295  * @details
296  * Allow the "full" flag to dump additional info,
297  * and trigger its usage from various places in the code.
298  */
299 errr file_character(player_type *creature_ptr, concptr name, update_playtime_pf update_playtime, display_player_pf display_player, map_name_pf map_name)
300 {
301         char buf[1024];
302         path_build(buf, sizeof(buf), ANGBAND_DIR_USER, name);
303
304         FILE_TYPE(FILE_TYPE_TEXT);
305
306         int     fd = fd_open(buf, O_RDONLY);
307         if (fd >= 0)
308         {
309                 char out_val[160];
310                 (void)fd_close(fd);
311                 (void)sprintf(out_val, _("現存するファイル %s に上書きしますか? ", "Replace existing file %s? "), buf);
312                 if (get_check_strict(out_val, CHECK_NO_HISTORY)) fd = -1;
313         }
314
315         FILE *fff = NULL;
316         if (fd < 0) fff = my_fopen(buf, "w");
317
318         if (!fff)
319         {
320                 prt(_("キャラクタ情報のファイルへの書き出しに失敗しました!", "Character dump failed!"), 0, 0);
321                 (void)inkey();
322                 return -1;
323         }
324
325         make_character_dump(creature_ptr, fff, update_playtime, display_player, map_name);
326         my_fclose(fff);
327         msg_print(_("キャラクタ情報のファイルへの書き出しに成功しました。", "Character dump successful."));
328         msg_print(NULL);
329         return 0;
330 }
331
332
333 /*!
334  * @brief ファイルからランダムに行を一つ取得する /
335  * Get a random line from a file
336  * @param file_name ファイル名
337  * @param entry 特定条件時のN:タグヘッダID
338  * @param output 出力先の文字列参照ポインタ
339  * @return エラーコード
340  * @details
341  * <pre>
342  * Based on the monster speech patch by Matt Graham,
343  * </pre>
344  */
345 errr get_rnd_line(concptr file_name, int entry, char *output)
346 {
347         char buf[1024];
348         path_build(buf, sizeof(buf), ANGBAND_DIR_FILE, file_name);
349         FILE *fp;
350         fp = my_fopen(buf, "r");
351         if (!fp) return -1;
352
353         int test;
354         int line_num = 0;
355         while (TRUE)
356         {
357                 if (my_fgets(fp, buf, sizeof(buf)) != 0)
358                 {
359                         my_fclose(fp);
360                         return -1;
361                 }
362
363                 line_num++;
364                 if ((buf[0] != 'N') || (buf[1] != ':')) continue;
365
366                 if (buf[2] == '*')
367                 {
368                         break;
369                 }
370                 else if (buf[2] == 'M')
371                 {
372                         if (r_info[entry].flags1 & RF1_MALE) break;
373                 }
374                 else if (buf[2] == 'F')
375                 {
376                         if (r_info[entry].flags1 & RF1_FEMALE) break;
377                 }
378                 else if (sscanf(&(buf[2]), "%d", &test) != EOF)
379                 {
380                         if (test == entry) break;
381                 }
382                 else
383                 {
384                         msg_format("Error in line %d of %s!", line_num, file_name);
385                         my_fclose(fp);
386                         return -1;
387                 }
388         }
389
390         int counter;
391         for (counter = 0; ; counter++)
392         {
393                 while (TRUE)
394                 {
395                         test = my_fgets(fp, buf, sizeof(buf));
396                         if (!test)
397                         {
398                                 /* Ignore lines starting with 'N:' */
399                                 if ((buf[0] == 'N') && (buf[1] == ':')) continue;
400
401                                 if (buf[0] != '#') break;
402                         }
403                         else break;
404                 }
405
406                 if (!buf[0]) break;
407
408                 if (one_in_(counter + 1)) strcpy(output, buf);
409         }
410
411         my_fclose(fp);
412         return counter ? 0 : -1;
413 }
414
415
416 #ifdef JP
417 /*!
418  * @brief ファイルからランダムに行を一つ取得する(日本語文字列のみ) /
419  * @param file_name ファイル名
420  * @param entry 特定条件時のN:タグヘッダID
421  * @param output 出力先の文字列参照ポインタ
422  * @param count 試行回数
423  * @return エラーコード
424  * @details
425  */
426 errr get_rnd_line_jonly(concptr file_name, int entry, char *output, int count)
427 {
428         errr result = 1;
429         for (int i = 0; i < count; i++)
430         {
431                 result = get_rnd_line(file_name, entry, output);
432                 if (result) break;
433                 bool kanji = FALSE;
434                 for (int j = 0; output[j]; j++) kanji |= iskanji(output[j]);
435                 if (kanji) break;
436         }
437
438         return result;
439 }
440 #endif
441
442
443 /*!
444  * @brief ファイル位置をシーク /
445  * @param creature_ptr プレーヤーへの参照ポインタ
446  * @param fd ファイルディスクリプタ
447  * @param where ファイルバイト位置
448  * @param flag FALSEならば現ファイルを超えた位置へシーク時エラー、TRUEなら足りない間を0で埋め尽くす
449  * @return エラーコード
450  * @details
451  */
452 static errr counts_seek(player_type *creature_ptr, int fd, u32b where, bool flag)
453 {
454         char temp1[128], temp2[128];
455 #ifdef SAVEFILE_USE_UID
456         (void)sprintf(temp1, "%d.%s.%d%d%d", creature_ptr->player_uid, savefile_base, creature_ptr->pclass, creature_ptr->pseikaku, creature_ptr->age);
457 #else
458         (void)sprintf(temp1, "%s.%d%d%d", savefile_base, creature_ptr->pclass, creature_ptr->pseikaku, creature_ptr->age);
459 #endif
460         for (int i = 0; temp1[i]; i++)
461                 temp1[i] ^= (i + 1) * 63;
462
463         int seekpoint = 0;
464         u32b zero_header[3] = { 0L, 0L, 0L };
465         while (TRUE)
466         {
467                 if (fd_seek(fd, seekpoint + 3 * sizeof(u32b)))
468                         return 1;
469                 if (fd_read(fd, (char*)(temp2), sizeof(temp2)))
470                 {
471                         if (!flag)
472                                 return 1;
473                         /* add new name */
474                         fd_seek(fd, seekpoint);
475                         fd_write(fd, (char*)zero_header, 3 * sizeof(u32b));
476                         fd_write(fd, (char*)(temp1), sizeof(temp1));
477                         break;
478                 }
479
480                 if (strcmp(temp1, temp2) == 0)
481                         break;
482
483                 seekpoint += 128 + 3 * sizeof(u32b);
484         }
485
486         return fd_seek(fd, seekpoint + where * sizeof(u32b));
487 }
488
489
490 /*!
491  * @brief ファイル位置を読み込む
492  * @param creature_ptr プレーヤーへの参照ポインタ
493  * @param where ファイルバイト位置
494  * @return エラーコード
495  * @details
496  */
497 u32b counts_read(player_type *creature_ptr, int where)
498 {
499         char buf[1024];
500         path_build(buf, sizeof(buf), ANGBAND_DIR_DATA, _("z_info_j.raw", "z_info.raw"));
501         int fd = fd_open(buf, O_RDONLY);
502
503         u32b count = 0;
504         if (counts_seek(creature_ptr, fd, where, FALSE) ||
505                 fd_read(fd, (char*)(&count), sizeof(u32b)))
506                 count = 0;
507
508         (void)fd_close(fd);
509
510         return count;
511 }
512
513
514 /*!
515  * @brief ファイル位置に書き込む /
516  * @param creature_ptr プレーヤーへの参照ポインタ
517  * @param where ファイルバイト位置
518  * @param count 書き込む値
519  * @return エラーコード
520  * @details
521  */
522 errr counts_write(player_type *creature_ptr, int where, u32b count)
523 {
524         char buf[1024];
525         path_build(buf, sizeof(buf), ANGBAND_DIR_DATA, _("z_info_j.raw", "z_info.raw"));
526
527         safe_setuid_grab();
528         int fd = fd_open(buf, O_RDWR);
529         safe_setuid_drop();
530         if (fd < 0)
531         {
532                 FILE_TYPE(FILE_TYPE_DATA);
533                 safe_setuid_grab();
534                 fd = fd_make(buf, 0644);
535                 safe_setuid_drop();
536         }
537
538         safe_setuid_grab();
539         errr err = fd_lock(fd, F_WRLCK);
540         safe_setuid_drop();
541         if (err) return 1;
542
543         counts_seek(creature_ptr, fd, where, TRUE);
544         fd_write(fd, (char*)(&count), sizeof(u32b));
545         safe_setuid_grab();
546         err = fd_lock(fd, F_UNLCK);
547         safe_setuid_drop();
548
549         if (err) return 1;
550
551         (void)fd_close(fd);
552         return 0;
553 }
554
555
556 /*!
557  * @brief 墓のアスキーアートテンプレを読み込む
558  * @param buf テンプレへのバッファ
559  * @param buf_size バッファの長さ
560  * @return なし
561  */
562 void read_dead_file(char *buf, size_t buf_size)
563 {
564         path_build(buf, buf_size, ANGBAND_DIR_FILE, _("dead_j.txt", "dead.txt"));
565
566         FILE *fp;
567         fp = my_fopen(buf, "r");
568         if (!fp) return;
569
570         int i = 0;
571         while (my_fgets(fp, buf, buf_size) == 0)
572         {
573                 put_str(buf, i++, 0);
574         }
575
576         my_fclose(fp);
577 }