OSDN Git Service

[Refactor] #39962 Separated cmd-save.c/h from files.c
[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 "term.h"
17 #include "uid-checker.h"
18 #include "files.h"
19 #include "core.h" // リファクタリングして後で消す
20
21 #include "character-dump.h"
22 #include "world.h"
23 #include "view-mainwindow.h"
24 #include "autopick.h"
25 #include "io/tokenizer.h"
26 #include "io/process-pref-file.h" // 暫定。依存性の向きがこれで良いか要確認.
27
28 #define PREF_TYPE_NORMAL   0
29 #define PREF_TYPE_AUTOPICK 1
30 #define PREF_TYPE_HISTPREF 2
31
32 concptr ANGBAND_DIR; //!< Path name: The main "lib" directory This variable is not actually used anywhere in the code
33 concptr ANGBAND_DIR_APEX; //!< High score files (binary) These files may be portable between platforms
34 concptr ANGBAND_DIR_BONE; //!< Bone files for player ghosts (ascii) These files are portable between platforms
35 concptr ANGBAND_DIR_DATA; //!< Binary image files for the "*_info" arrays (binary) These files are not portable between platforms
36 concptr ANGBAND_DIR_EDIT; //!< Textual template files for the "*_info" arrays (ascii) These files are portable between platforms
37 concptr ANGBAND_DIR_SCRIPT; //!< Script files These files are portable between platforms.
38 concptr ANGBAND_DIR_FILE; //!< Various extra files (ascii) These files may be portable between platforms
39 concptr ANGBAND_DIR_HELP; //!< Help files (normal) for the online help (ascii) These files are portable between platforms
40 concptr ANGBAND_DIR_INFO; //!< Help files (spoilers) for the online help (ascii) These files are portable between platforms
41 concptr ANGBAND_DIR_PREF; //!< Default user "preference" files (ascii) These files are rarely portable between platforms
42 concptr ANGBAND_DIR_SAVE; //!< Savefiles for current characters (binary)
43 concptr ANGBAND_DIR_USER; //!< User "preference" files (ascii) These files are rarely portable between platforms
44 concptr ANGBAND_DIR_XTRA; //!< Various extra files (binary) These files are rarely portable between platforms
45
46 /*
47  * Buffer to hold the current savefile name
48  * 'savefile' holds full path name. 'savefile_base' holds only base name.
49  */
50 char savefile[1024];
51 char savefile_base[40];
52
53 /*!
54  * todo サブルーチンとは言い難い。autopick.c から直接呼ばれている
55  * @brief process_pref_fileのサブルーチンとして条件分岐処理の解釈と結果を返す
56  * Helper function for "process_pref_file()"
57  * @param creature_ptr プレーヤーへの参照ポインタ
58  * @param sp テキスト文字列の参照ポインタ
59  * @param fp 再帰中のポインタ参照
60  * @return
61  * @details
62  * <pre>
63  * Input:
64  *   v: output buffer array
65  *   f: final character
66  * Output:
67  *   result
68  * </pre>
69  */
70 concptr process_pref_file_expr(player_type *creature_ptr, char **sp, char *fp)
71 {
72         char *s;
73         s = (*sp);
74         while (iswspace(*s)) s++;
75
76         char *b;
77         b = s;
78
79         concptr v = "?o?o?";
80
81         char b1 = '[';
82         char b2 = ']';
83         char f = ' ';
84         static char tmp[16];
85         if (*s == b1)
86         {
87                 concptr p;
88                 concptr t;
89
90                 /* Skip b1 */
91                 s++;
92
93                 /* First */
94                 t = process_pref_file_expr(creature_ptr, &s, &f);
95
96                 if (!*t)
97                 {
98                 }
99                 else if (streq(t, "IOR"))
100                 {
101                         v = "0";
102                         while (*s && (f != b2))
103                         {
104                                 t = process_pref_file_expr(creature_ptr, &s, &f);
105                                 if (*t && !streq(t, "0")) v = "1";
106                         }
107                 }
108                 else if (streq(t, "AND"))
109                 {
110                         v = "1";
111                         while (*s && (f != b2))
112                         {
113                                 t = process_pref_file_expr(creature_ptr, &s, &f);
114                                 if (*t && streq(t, "0")) v = "0";
115                         }
116                 }
117                 else if (streq(t, "NOT"))
118                 {
119                         v = "1";
120                         while (*s && (f != b2))
121                         {
122                                 t = process_pref_file_expr(creature_ptr, &s, &f);
123                                 if (*t && streq(t, "1")) v = "0";
124                         }
125                 }
126                 else if (streq(t, "EQU"))
127                 {
128                         v = "0";
129                         if (*s && (f != b2))
130                         {
131                                 t = process_pref_file_expr(creature_ptr, &s, &f);
132                         }
133                         while (*s && (f != b2))
134                         {
135                                 p = process_pref_file_expr(creature_ptr, &s, &f);
136                                 if (streq(t, p)) v = "1";
137                         }
138                 }
139                 else if (streq(t, "LEQ"))
140                 {
141                         v = "1";
142                         if (*s && (f != b2))
143                         {
144                                 t = process_pref_file_expr(creature_ptr, &s, &f);
145                         }
146                         while (*s && (f != b2))
147                         {
148                                 p = t;
149                                 t = process_pref_file_expr(creature_ptr, &s, &f);
150                                 if (*t && atoi(p) > atoi(t)) v = "0";
151                         }
152                 }
153                 else if (streq(t, "GEQ"))
154                 {
155                         v = "1";
156                         if (*s && (f != b2))
157                         {
158                                 t = process_pref_file_expr(creature_ptr, &s, &f);
159                         }
160                         while (*s && (f != b2))
161                         {
162                                 p = t;
163                                 t = process_pref_file_expr(creature_ptr, &s, &f);
164                                 if (*t && atoi(p) < atoi(t)) v = "0";
165                         }
166                 }
167                 else
168                 {
169                         while (*s && (f != b2))
170                         {
171                                 t = process_pref_file_expr(creature_ptr, &s, &f);
172                         }
173                 }
174
175                 if (f != b2) v = "?x?x?";
176
177                 if ((f = *s) != '\0') *s++ = '\0';
178
179                 *fp = f;
180                 *sp = s;
181                 return v;
182         }
183
184         /* Accept all printables except spaces and brackets */
185 #ifdef JP
186         while (iskanji(*s) || (isprint(*s) && !my_strchr(" []", *s)))
187         {
188                 if (iskanji(*s)) s++;
189                 s++;
190         }
191 #else
192         while (isprint(*s) && !my_strchr(" []", *s)) ++s;
193 #endif
194
195         if ((f = *s) != '\0') *s++ = '\0';
196
197         if (*b != '$')
198         {
199                 v = b;
200                 *fp = f;
201                 *sp = s;
202                 return v;
203         }
204
205         if (streq(b + 1, "SYS"))
206         {
207                 v = ANGBAND_SYS;
208         }
209         else if (streq(b + 1, "KEYBOARD"))
210         {
211                 v = ANGBAND_KEYBOARD;
212         }
213         else if (streq(b + 1, "GRAF"))
214         {
215                 v = ANGBAND_GRAF;
216         }
217         else if (streq(b + 1, "MONOCHROME"))
218         {
219                 if (arg_monochrome)
220                         v = "ON";
221                 else
222                         v = "OFF";
223         }
224         else if (streq(b + 1, "RACE"))
225         {
226 #ifdef JP
227                 v = rp_ptr->E_title;
228 #else
229                 v = rp_ptr->title;
230 #endif
231         }
232         else if (streq(b + 1, "CLASS"))
233         {
234 #ifdef JP
235                 v = cp_ptr->E_title;
236 #else
237                 v = cp_ptr->title;
238 #endif
239         }
240         else if (streq(b + 1, "PLAYER"))
241         {
242                 static char tmp_player_name[32];
243                 char *pn, *tpn;
244                 for (pn = creature_ptr->name, tpn = tmp_player_name; *pn; pn++, tpn++)
245                 {
246 #ifdef JP
247                         if (iskanji(*pn))
248                         {
249                                 *(tpn++) = *(pn++);
250                                 *tpn = *pn;
251                                 continue;
252                         }
253 #endif
254                         *tpn = my_strchr(" []", *pn) ? '_' : *pn;
255                 }
256
257                 *tpn = '\0';
258                 v = tmp_player_name;
259         }
260         else if (streq(b + 1, "REALM1"))
261         {
262 #ifdef JP
263                 v = E_realm_names[creature_ptr->realm1];
264 #else
265                 v = realm_names[creature_ptr->realm1];
266 #endif
267         }
268         else if (streq(b + 1, "REALM2"))
269         {
270 #ifdef JP
271                 v = E_realm_names[creature_ptr->realm2];
272 #else
273                 v = realm_names[creature_ptr->realm2];
274 #endif
275         }
276         else if (streq(b + 1, "LEVEL"))
277         {
278                 sprintf(tmp, "%02d", creature_ptr->lev);
279                 v = tmp;
280         }
281         else if (streq(b + 1, "AUTOREGISTER"))
282         {
283                 if (creature_ptr->autopick_autoregister)
284                         v = "1";
285                 else
286                         v = "0";
287         }
288         else if (streq(b + 1, "MONEY"))
289         {
290                 sprintf(tmp, "%09ld", (long int)creature_ptr->au);
291                 v = tmp;
292         }
293
294         *fp = f;
295         *sp = s;
296         return v;
297 }
298
299
300 /*!
301  * @brief process_pref_fileのサブルーチン /
302  * Open the "user pref file" and parse it.
303  * @param creature_ptr プレーヤーへの参照ポインタ
304  * @param name 読み込むファイル名
305  * @param preftype prefファイルのタイプ
306  * @return エラーコード
307  * @details
308  * <pre>
309  * Input:
310  *   v: output buffer array
311  *   f: final character
312  * Output:
313  *   result
314  * </pre>
315  */
316 static errr process_pref_file_aux(player_type *creature_ptr, concptr name, int preftype)
317 {
318         FILE *fp;
319         fp = my_fopen(name, "r");
320         if (!fp) return -1;
321
322         char buf[1024];
323         char old[1024];
324         int line = -1;
325         errr err = 0;
326         bool bypass = FALSE;
327         while (my_fgets(fp, buf, sizeof(buf)) == 0)
328         {
329                 line++;
330                 if (!buf[0]) continue;
331
332 #ifdef JP
333                 if (!iskanji(buf[0]))
334 #endif
335                         if (iswspace(buf[0])) continue;
336
337                 if (buf[0] == '#') continue;
338                 strcpy(old, buf);
339
340                 /* Process "?:<expr>" */
341                 if ((buf[0] == '?') && (buf[1] == ':'))
342                 {
343                         char f;
344                         char *s;
345                         s = buf + 2;
346                         concptr v = process_pref_file_expr(creature_ptr, &s, &f);
347                         bypass = streq(v, "0");
348                         continue;
349                 }
350
351                 if (bypass) continue;
352
353                 /* Process "%:<file>" */
354                 if (buf[0] == '%')
355                 {
356                         static int depth_count = 0;
357                         if (depth_count > 20) continue;
358
359                         depth_count++;
360                         switch (preftype)
361                         {
362                         case PREF_TYPE_AUTOPICK:
363                                 (void)process_autopick_file(creature_ptr, buf + 2);
364                                 break;
365                         case PREF_TYPE_HISTPREF:
366                                 (void)process_histpref_file(creature_ptr, buf + 2);
367                                 break;
368                         default:
369                                 (void)process_pref_file(creature_ptr, buf + 2);
370                                 break;
371                         }
372
373                         depth_count--;
374                         continue;
375                 }
376
377                 err = process_pref_file_command(creature_ptr, buf);
378                 if (err != 0)
379                 {
380                         if (preftype != PREF_TYPE_AUTOPICK)
381                                 break;
382                         err = process_autopick_file_command(buf);
383                 }
384         }
385
386         if (err != 0)
387         {
388                 /* Print error message */
389                 /* ToDo: Add better error messages */
390                 msg_format(_("ファイル'%s'の%d行でエラー番号%dのエラー。", "Error %d in line %d of file '%s'."),
391                         _(name, err), line, _(err, name));
392                 msg_format(_("('%s'を解析中)", "Parsing '%s'"), old);
393                 msg_print(NULL);
394         }
395
396         my_fclose(fp);
397         return (err);
398 }
399
400
401 /*!
402  * @brief pref設定ファイルを読み込み設定を反映させる /
403  * Process the "user pref file" with the given name
404  * @param creature_ptr プレーヤーへの参照ポインタ
405  * @param name 読み込むファイル名
406  * @return エラーコード
407  * @details
408  * <pre>
409  * See the functions above for a list of legal "commands".
410  * We also accept the special "?" and "%" directives, which
411  * allow conditional evaluation and filename inclusion.
412  * </pre>
413  */
414 errr process_pref_file(player_type *creature_ptr, concptr name)
415 {
416         char buf[1024];
417         path_build(buf, sizeof(buf), ANGBAND_DIR_PREF, name);
418
419         errr err1 = process_pref_file_aux(creature_ptr, buf, PREF_TYPE_NORMAL);
420         if (err1 > 0) return err1;
421
422         path_build(buf, sizeof(buf), ANGBAND_DIR_USER, name);
423         errr err2 = process_pref_file_aux(creature_ptr, buf, PREF_TYPE_NORMAL);
424         if (err2 < 0 && !err1)
425                 return -2;
426
427         return err2;
428 }
429
430
431 /*!
432  * @brief プレイヤーステータスをファイルダンプ出力する
433  * Hack -- Dump a character description file
434  * @param creature_ptr プレーヤーへの参照ポインタ
435  * @param name 出力ファイル名
436  * @return エラーコード
437  * @details
438  * Allow the "full" flag to dump additional info,
439  * and trigger its usage from various places in the code.
440  */
441 errr file_character(player_type *creature_ptr, concptr name, display_player_pf display_player, map_name_pf map_name)
442 {
443         char buf[1024];
444         path_build(buf, sizeof(buf), ANGBAND_DIR_USER, name);
445
446         FILE_TYPE(FILE_TYPE_TEXT);
447
448         int     fd = fd_open(buf, O_RDONLY);
449         if (fd >= 0)
450         {
451                 char out_val[160];
452                 (void)fd_close(fd);
453                 (void)sprintf(out_val, _("現存するファイル %s に上書きしますか? ", "Replace existing file %s? "), buf);
454                 if (get_check_strict(out_val, CHECK_NO_HISTORY)) fd = -1;
455         }
456
457         FILE *fff = NULL;
458         if (fd < 0) fff = my_fopen(buf, "w");
459
460         if (!fff)
461         {
462                 prt(_("キャラクタ情報のファイルへの書き出しに失敗しました!", "Character dump failed!"), 0, 0);
463                 (void)inkey();
464                 return -1;
465         }
466
467         make_character_dump(creature_ptr, fff, update_playtime, display_player, map_name);
468         my_fclose(fff);
469         msg_print(_("キャラクタ情報のファイルへの書き出しに成功しました。", "Character dump successful."));
470         msg_print(NULL);
471         return 0;
472 }
473
474
475 /*!
476  * @brief ファイル内容の一行をコンソールに出力する
477  * Display single line of on-line help file
478  * @param str 出力する文字列
479  * @param cy コンソールの行
480  * @param shower 確認中
481  * @return なし
482  * @details
483  * <pre>
484  * You can insert some special color tag to change text color.
485  * Such as...
486  * WHITETEXT [[[[y|SOME TEXT WHICH IS DISPLAYED IN YELLOW| WHITETEXT
487  * A colored segment is between "[[[[y|" and the last "|".
488  * You can use any single character in place of the "|".
489  * </pre>
490  */
491 static void show_file_aux_line(concptr str, int cy, concptr shower)
492 {
493         char lcstr[1024];
494         if (shower)
495         {
496                 strcpy(lcstr, str);
497                 str_tolower(lcstr);
498         }
499
500         int cx = 0;
501         Term_gotoxy(cx, cy);
502
503         static const char tag_str[] = "[[[[";
504         byte color = TERM_WHITE;
505         char in_tag = '\0';
506         for (int i = 0; str[i];)
507         {
508                 int len = strlen(&str[i]);
509                 int showercol = len + 1;
510                 int bracketcol = len + 1;
511                 int endcol = len;
512                 concptr ptr;
513                 if (shower)
514                 {
515                         ptr = my_strstr(&lcstr[i], shower);
516                         if (ptr) showercol = ptr - &lcstr[i];
517                 }
518
519                 ptr = in_tag ? my_strchr(&str[i], in_tag) : my_strstr(&str[i], tag_str);
520                 if (ptr) bracketcol = ptr - &str[i];
521                 if (bracketcol < endcol) endcol = bracketcol;
522                 if (showercol < endcol) endcol = showercol;
523
524                 Term_addstr(endcol, color, &str[i]);
525                 cx += endcol;
526                 i += endcol;
527
528                 if (endcol == showercol)
529                 {
530                         int showerlen = strlen(shower);
531                         Term_addstr(showerlen, TERM_YELLOW, &str[i]);
532                         cx += showerlen;
533                         i += showerlen;
534                         continue;
535                 }
536
537                 if (endcol != bracketcol) continue;
538
539                 if (in_tag)
540                 {
541                         i++;
542                         in_tag = '\0';
543                         color = TERM_WHITE;
544                         continue;
545                 }
546
547                 i += sizeof(tag_str) - 1;
548                 color = color_char_to_attr(str[i]);
549                 if (color == 255 || str[i + 1] == '\0')
550                 {
551                         color = TERM_WHITE;
552                         Term_addstr(-1, TERM_WHITE, tag_str);
553                         cx += sizeof(tag_str) - 1;
554                         continue;
555                 }
556
557                 i++;
558                 in_tag = str[i];
559                 i++;
560         }
561
562         Term_erase(cx, cy, 255);
563 }
564
565
566 /*!
567  * @brief ファイル内容をコンソールに出力する
568  * Recursive file perusal.
569  * @param creature_ptr プレーヤーへの参照ポインタ
570  * @param show_version TRUEならばコンソール上にゲームのバージョンを表示する
571  * @param name ファイル名の文字列
572  * @param what 内容キャプションの文字列
573  * @param line 表示の現在行
574  * @param mode オプション
575  * @return なし
576  * @details
577  * <pre>
578  * Process various special text in the input file, including
579  * the "menu" structures used by the "help file" system.
580  * Return FALSE on 'q' to exit from a deep, otherwise TRUE.
581  * </pre>
582  */
583 bool show_file(player_type *creature_ptr, bool show_version, concptr name, concptr what, int line, BIT_FLAGS mode)
584 {
585         int wid, hgt;
586         Term_get_size(&wid, &hgt);
587
588         char finder_str[81];
589         strcpy(finder_str, "");
590
591         char shower_str[81];
592         strcpy(shower_str, "");
593
594         char caption[128];
595         strcpy(caption, "");
596
597         char hook[68][32];
598         for (int i = 0; i < 68; i++)
599         {
600                 hook[i][0] = '\0';
601         }
602
603         char filename[1024];
604         strcpy(filename, name);
605         int n = strlen(filename);
606
607         concptr tag = NULL;
608         for (int i = 0; i < n; i++)
609         {
610                 if (filename[i] == '#')
611                 {
612                         filename[i] = '\0';
613                         tag = filename + i + 1;
614                         break;
615                 }
616         }
617
618         name = filename;
619         FILE *fff = NULL;
620         char path[1024];
621         if (what)
622         {
623                 strcpy(caption, what);
624                 strcpy(path, name);
625                 fff = my_fopen(path, "r");
626         }
627
628         if (!fff)
629         {
630                 sprintf(caption, _("ヘルプ・ファイル'%s'", "Help file '%s'"), name);
631                 path_build(path, sizeof(path), ANGBAND_DIR_HELP, name);
632                 fff = my_fopen(path, "r");
633         }
634
635         if (!fff)
636         {
637                 sprintf(caption, _("スポイラー・ファイル'%s'", "Info file '%s'"), name);
638                 path_build(path, sizeof(path), ANGBAND_DIR_INFO, name);
639                 fff = my_fopen(path, "r");
640         }
641
642         if (!fff)
643         {
644                 path_build(path, sizeof(path), ANGBAND_DIR, name);
645
646                 for (int i = 0; path[i]; i++)
647                         if ('\\' == path[i])
648                                 path[i] = PATH_SEP[0];
649
650                 sprintf(caption, _("スポイラー・ファイル'%s'", "Info file '%s'"), name);
651                 fff = my_fopen(path, "r");
652         }
653
654         if (!fff)
655         {
656                 msg_format(_("'%s'をオープンできません。", "Cannot open '%s'."), name);
657                 msg_print(NULL);
658
659                 return TRUE;
660         }
661
662         int skey;
663         int next = 0;
664         int size = 0;
665         int back = 0;
666         bool menu = FALSE;
667         char buf[1024];
668         bool reverse = (line < 0);
669         while (TRUE)
670         {
671                 char *str = buf;
672                 if (my_fgets(fff, buf, sizeof(buf))) break;
673                 if (!prefix(str, "***** "))
674                 {
675                         next++;
676                         continue;
677                 }
678
679                 if ((str[6] == '[') && isalpha(str[7]))
680                 {
681                         int k = str[7] - 'A';
682                         menu = TRUE;
683                         if ((str[8] == ']') && (str[9] == ' '))
684                         {
685                                 strncpy(hook[k], str + 10, 31);
686                                 hook[k][31] = '\0';
687                         }
688
689                         continue;
690                 }
691
692                 if (str[6] != '<') continue;
693
694                 size_t len = strlen(str);
695                 if (str[len - 1] == '>')
696                 {
697                         str[len - 1] = '\0';
698                         if (tag && streq(str + 7, tag)) line = next;
699                 }
700         }
701
702         size = next;
703         int rows = hgt - 4;
704         if (line == -1)
705                 line = ((size - 1) / rows)*rows;
706
707         Term_clear();
708
709         concptr find = NULL;
710         while (TRUE)
711         {
712                 if (line >= size - rows)
713                         line = size - rows;
714                 if (line < 0) line = 0;
715
716                 if (next > line)
717                 {
718                         my_fclose(fff);
719                         fff = my_fopen(path, "r");
720                         if (!fff) return FALSE;
721
722                         next = 0;
723                 }
724
725                 while (next < line)
726                 {
727                         if (my_fgets(fff, buf, sizeof(buf))) break;
728                         if (prefix(buf, "***** ")) continue;
729                         next++;
730                 }
731
732                 int row_count = 0;
733                 concptr shower = NULL;
734                 for (int i = 0; i < rows; i++)
735                 {
736                         concptr str = buf;
737                         if (!i) line = next;
738                         if (my_fgets(fff, buf, sizeof(buf))) break;
739                         if (prefix(buf, "***** ")) continue;
740                         next++;
741                         if (find && !i)
742                         {
743                                 char lc_buf[1024];
744                                 strcpy(lc_buf, str);
745                                 str_tolower(lc_buf);
746                                 if (!my_strstr(lc_buf, find)) continue;
747                         }
748
749                         find = NULL;
750                         show_file_aux_line(str, i + 2, shower);
751                         row_count++;
752                 }
753
754                 while (row_count < rows)
755                 {
756                         Term_erase(0, row_count + 2, 255);
757                         row_count++;
758                 }
759
760                 if (find)
761                 {
762                         bell();
763                         line = back;
764                         find = NULL;
765                         continue;
766                 }
767
768                 if (show_version)
769                 {
770                         prt(format(_("[変愚蛮怒 %d.%d.%d, %s, %d/%d]", "[Hengband %d.%d.%d, %s, Line %d/%d]"),
771                                 FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH,
772                                 caption, line, size), 0, 0);
773                 }
774                 else
775                 {
776                         prt(format(_("[%s, %d/%d]", "[%s, Line %d/%d]"),
777                                 caption, line, size), 0, 0);
778                 }
779
780                 if (size <= rows)
781                 {
782                         prt(_("[キー:(?)ヘルプ (ESC)終了]", "[Press ESC to exit.]"), hgt - 1, 0);
783                 }
784                 else
785                 {
786 #ifdef JP
787                         if (reverse)
788                                 prt("[キー:(RET/スペース)↑ (-)↓ (?)ヘルプ (ESC)終了]", hgt - 1, 0);
789                         else
790                                 prt("[キー:(RET/スペース)↓ (-)↑ (?)ヘルプ (ESC)終了]", hgt - 1, 0);
791 #else
792                         prt("[Press Return, Space, -, =, /, |, or ESC to exit.]", hgt - 1, 0);
793 #endif
794                 }
795
796                 skey = inkey_special(TRUE);
797                 switch (skey)
798                 {
799                 case '?':
800                         if (strcmp(name, _("jhelpinfo.txt", "helpinfo.txt")) != 0)
801                                 show_file(creature_ptr, TRUE, _("jhelpinfo.txt", "helpinfo.txt"), NULL, 0, mode);
802                         break;
803                 case '=':
804                         prt(_("強調: ", "Show: "), hgt - 1, 0);
805
806                         char back_str[81];
807                         strcpy(back_str, shower_str);
808                         if (askfor(shower_str, 80))
809                         {
810                                 if (shower_str[0])
811                                 {
812                                         str_tolower(shower_str);
813                                         shower = shower_str;
814                                 }
815                                 else shower = NULL;
816                         }
817                         else strcpy(shower_str, back_str);
818                         break;
819
820                 case '/':
821                 case KTRL('s'):
822                         prt(_("検索: ", "Find: "), hgt - 1, 0);
823                         strcpy(back_str, finder_str);
824                         if (askfor(finder_str, 80))
825                         {
826                                 if (finder_str[0])
827                                 {
828                                         find = finder_str;
829                                         back = line;
830                                         line = line + 1;
831                                         str_tolower(finder_str);
832                                         shower = finder_str;
833                                 }
834                                 else shower = NULL;
835                         }
836                         else strcpy(finder_str, back_str);
837                         break;
838
839                 case '#':
840                 {
841                         char tmp[81];
842                         prt(_("行: ", "Goto Line: "), hgt - 1, 0);
843                         strcpy(tmp, "0");
844
845                         if (askfor(tmp, 80)) line = atoi(tmp);
846                         break;
847                 }
848
849                 case SKEY_TOP:
850                         line = 0;
851                         break;
852
853                 case SKEY_BOTTOM:
854                         line = ((size - 1) / rows) * rows;
855                         break;
856
857                 case '%':
858                 {
859                         char tmp[81];
860                         prt(_("ファイル・ネーム: ", "Goto File: "), hgt - 1, 0);
861                         strcpy(tmp, _("jhelp.hlp", "help.hlp"));
862
863                         if (askfor(tmp, 80))
864                         {
865                                 if (!show_file(creature_ptr, TRUE, tmp, NULL, 0, mode)) skey = 'q';
866                         }
867
868                         break;
869                 }
870
871                 case '-':
872                         line = line + (reverse ? rows : -rows);
873                         if (line < 0) line = 0;
874                         break;
875
876                 case SKEY_PGUP:
877                         line = line - rows;
878                         if (line < 0) line = 0;
879                         break;
880
881                 case '\n':
882                 case '\r':
883                         line = line + (reverse ? -1 : 1);
884                         if (line < 0) line = 0;
885                         break;
886
887                 case '8':
888                 case SKEY_UP:
889                         line--;
890                         if (line < 0) line = 0;
891                         break;
892
893                 case '2':
894                 case SKEY_DOWN:
895                         line++;
896                         break;
897
898                 case ' ':
899                         line = line + (reverse ? -rows : rows);
900                         if (line < 0) line = 0;
901                         break;
902
903                 case SKEY_PGDOWN:
904                         line = line + rows;
905                         break;
906                 }
907
908                 if (menu)
909                 {
910                         int key = -1;
911                         if (!(skey & SKEY_MASK) && isalpha(skey))
912                                 key = skey - 'A';
913
914                         if ((key > -1) && hook[key][0])
915                         {
916                                 /* Recurse on that file */
917                                 if (!show_file(creature_ptr, TRUE, hook[key], NULL, 0, mode))
918                                         skey = 'q';
919                         }
920                 }
921
922                 if (skey == '|')
923                 {
924                         FILE *ffp;
925                         char buff[1024];
926                         char xtmp[82];
927
928                         strcpy(xtmp, "");
929
930                         if (!get_string(_("ファイル名: ", "File name: "), xtmp, 80)) continue;
931                         my_fclose(fff);
932                         path_build(buff, sizeof(buff), ANGBAND_DIR_USER, xtmp);
933
934                         /* Hack -- Re-Open the file */
935                         fff = my_fopen(path, "r");
936
937                         ffp = my_fopen(buff, "w");
938
939                         if (!(fff && ffp))
940                         {
941                                 msg_print(_("ファイルを開けません。", "Failed to open file."));
942                                 skey = ESCAPE;
943                                 break;
944                         }
945
946                         sprintf(xtmp, "%s: %s", creature_ptr->name, what ? what : caption);
947                         my_fputs(ffp, xtmp, 80);
948                         my_fputs(ffp, "\n", 80);
949
950                         while (!my_fgets(fff, buff, sizeof(buff)))
951                                 my_fputs(ffp, buff, 80);
952                         my_fclose(fff);
953                         my_fclose(ffp);
954                         fff = my_fopen(path, "r");
955                 }
956
957                 if ((skey == ESCAPE) || (skey == '<')) break;
958
959                 if (skey == KTRL('q')) skey = 'q';
960
961                 if (skey == 'q') break;
962         }
963
964         my_fclose(fff);
965         return (skey != 'q');
966 }
967
968
969 /*!
970  * @brief ファイルからランダムに行を一つ取得する /
971  * Get a random line from a file
972  * @param file_name ファイル名
973  * @param entry 特定条件時のN:タグヘッダID
974  * @param output 出力先の文字列参照ポインタ
975  * @return エラーコード
976  * @details
977  * <pre>
978  * Based on the monster speech patch by Matt Graham,
979  * </pre>
980  */
981 errr get_rnd_line(concptr file_name, int entry, char *output)
982 {
983         char buf[1024];
984         path_build(buf, sizeof(buf), ANGBAND_DIR_FILE, file_name);
985         FILE *fp;
986         fp = my_fopen(buf, "r");
987         if (!fp) return -1;
988
989         int test;
990         int line_num = 0;
991         while (TRUE)
992         {
993                 if (my_fgets(fp, buf, sizeof(buf)) != 0)
994                 {
995                         my_fclose(fp);
996                         return -1;
997                 }
998
999                 line_num++;
1000                 if ((buf[0] != 'N') || (buf[1] != ':')) continue;
1001
1002                 if (buf[2] == '*')
1003                 {
1004                         break;
1005                 }
1006                 else if (buf[2] == 'M')
1007                 {
1008                         if (r_info[entry].flags1 & RF1_MALE) break;
1009                 }
1010                 else if (buf[2] == 'F')
1011                 {
1012                         if (r_info[entry].flags1 & RF1_FEMALE) break;
1013                 }
1014                 else if (sscanf(&(buf[2]), "%d", &test) != EOF)
1015                 {
1016                         if (test == entry) break;
1017                 }
1018
1019                 msg_format("Error in line %d of %s!", line_num, file_name);
1020                 my_fclose(fp);
1021                 return -1;
1022         }
1023
1024         int counter;
1025         for (counter = 0; ; counter++)
1026         {
1027                 while (TRUE)
1028                 {
1029                         test = my_fgets(fp, buf, sizeof(buf));
1030                         if (!test)
1031                         {
1032                                 /* Ignore lines starting with 'N:' */
1033                                 if ((buf[0] == 'N') && (buf[1] == ':')) continue;
1034
1035                                 if (buf[0] != '#') break;
1036                         }
1037                         else break;
1038                 }
1039
1040                 if (!buf[0]) break;
1041
1042                 if (one_in_(counter + 1)) strcpy(output, buf);
1043         }
1044
1045         my_fclose(fp);
1046         return counter ? 0 : -1;
1047 }
1048
1049
1050 #ifdef JP
1051 /*!
1052  * @brief ファイルからランダムに行を一つ取得する(日本語文字列のみ) /
1053  * @param file_name ファイル名
1054  * @param entry 特定条件時のN:タグヘッダID
1055  * @param output 出力先の文字列参照ポインタ
1056  * @param count 試行回数
1057  * @return エラーコード
1058  * @details
1059  */
1060 errr get_rnd_line_jonly(concptr file_name, int entry, char *output, int count)
1061 {
1062         errr result = 1;
1063         for (int i = 0; i < count; i++)
1064         {
1065                 result = get_rnd_line(file_name, entry, output);
1066                 if (result) break;
1067                 bool kanji = FALSE;
1068                 for (int j = 0; output[j]; j++) kanji |= iskanji(output[j]);
1069                 if (kanji) break;
1070         }
1071
1072         return result;
1073 }
1074 #endif
1075
1076
1077 /*!
1078  * @brief 自動拾いファイルを読み込む /
1079  * @param creature_ptr プレーヤーへの参照ポインタ
1080  * @param name ファイル名
1081  * @details
1082  */
1083 errr process_autopick_file(player_type *creature_ptr, concptr name)
1084 {
1085         char buf[1024];
1086         path_build(buf, sizeof(buf), ANGBAND_DIR_USER, name);
1087         errr err = process_pref_file_aux(creature_ptr, buf, PREF_TYPE_AUTOPICK);
1088         return err;
1089 }
1090
1091
1092 /*!
1093  * @brief プレイヤーの生い立ちファイルを読み込む /
1094  * Process file for player's history editor.
1095  * @param creature_ptr プレーヤーへの参照ポインタ
1096  * @param name ファイル名
1097  * @return エラーコード
1098  * @details
1099  */
1100 errr process_histpref_file(player_type *creature_ptr, concptr name)
1101 {
1102         bool old_character_xtra = current_world_ptr->character_xtra;
1103         char buf[1024];
1104         path_build(buf, sizeof(buf), ANGBAND_DIR_USER, name);
1105
1106         /* Hack -- prevent modification birth options in this file */
1107         current_world_ptr->character_xtra = TRUE;
1108         errr err = process_pref_file_aux(creature_ptr, buf, PREF_TYPE_HISTPREF);
1109         current_world_ptr->character_xtra = old_character_xtra;
1110         return err;
1111 }
1112
1113
1114 /*!
1115  * @brief ファイル位置をシーク /
1116  * @param creature_ptr プレーヤーへの参照ポインタ
1117  * @param fd ファイルディスクリプタ
1118  * @param where ファイルバイト位置
1119  * @param flag FALSEならば現ファイルを超えた位置へシーク時エラー、TRUEなら足りない間を0で埋め尽くす
1120  * @return エラーコード
1121  * @details
1122  */
1123 static errr counts_seek(player_type *creature_ptr, int fd, u32b where, bool flag)
1124 {
1125         char temp1[128], temp2[128];
1126 #ifdef SAVEFILE_USE_UID
1127         (void)sprintf(temp1, "%d.%s.%d%d%d", creature_ptr->player_uid, savefile_base, creature_ptr->pclass, creature_ptr->pseikaku, creature_ptr->age);
1128 #else
1129         (void)sprintf(temp1, "%s.%d%d%d", savefile_base, creature_ptr->pclass, creature_ptr->pseikaku, creature_ptr->age);
1130 #endif
1131         for (int i = 0; temp1[i]; i++)
1132                 temp1[i] ^= (i + 1) * 63;
1133
1134         int seekpoint = 0;
1135         u32b zero_header[3] = { 0L, 0L, 0L };
1136         while (TRUE)
1137         {
1138                 if (fd_seek(fd, seekpoint + 3 * sizeof(u32b)))
1139                         return 1;
1140                 if (fd_read(fd, (char*)(temp2), sizeof(temp2)))
1141                 {
1142                         if (!flag)
1143                                 return 1;
1144                         /* add new name */
1145                         fd_seek(fd, seekpoint);
1146                         fd_write(fd, (char*)zero_header, 3 * sizeof(u32b));
1147                         fd_write(fd, (char*)(temp1), sizeof(temp1));
1148                         break;
1149                 }
1150
1151                 if (strcmp(temp1, temp2) == 0)
1152                         break;
1153
1154                 seekpoint += 128 + 3 * sizeof(u32b);
1155         }
1156
1157         return fd_seek(fd, seekpoint + where * sizeof(u32b));
1158 }
1159
1160
1161 /*!
1162  * @brief ファイル位置を読み込む
1163  * @param creature_ptr プレーヤーへの参照ポインタ
1164  * @param where ファイルバイト位置
1165  * @return エラーコード
1166  * @details
1167  */
1168 u32b counts_read(player_type *creature_ptr, int where)
1169 {
1170         char buf[1024];
1171         path_build(buf, sizeof(buf), ANGBAND_DIR_DATA, _("z_info_j.raw", "z_info.raw"));
1172         int fd = fd_open(buf, O_RDONLY);
1173
1174         u32b count = 0;
1175         if (counts_seek(creature_ptr, fd, where, FALSE) ||
1176                 fd_read(fd, (char*)(&count), sizeof(u32b)))
1177                 count = 0;
1178
1179         (void)fd_close(fd);
1180
1181         return count;
1182 }
1183
1184
1185 /*!
1186  * @brief ファイル位置に書き込む /
1187  * @param creature_ptr プレーヤーへの参照ポインタ
1188  * @param where ファイルバイト位置
1189  * @param count 書き込む値
1190  * @return エラーコード
1191  * @details
1192  */
1193 errr counts_write(player_type *creature_ptr, int where, u32b count)
1194 {
1195         char buf[1024];
1196         path_build(buf, sizeof(buf), ANGBAND_DIR_DATA, _("z_info_j.raw", "z_info.raw"));
1197
1198         safe_setuid_grab();
1199         int fd = fd_open(buf, O_RDWR);
1200         safe_setuid_drop();
1201         if (fd < 0)
1202         {
1203                 FILE_TYPE(FILE_TYPE_DATA);
1204                 safe_setuid_grab();
1205                 fd = fd_make(buf, 0644);
1206                 safe_setuid_drop();
1207         }
1208
1209         safe_setuid_grab();
1210         errr err = fd_lock(fd, F_WRLCK);
1211         safe_setuid_drop();
1212         if (err) return 1;
1213
1214         counts_seek(creature_ptr, fd, where, TRUE);
1215         fd_write(fd, (char*)(&count), sizeof(u32b));
1216         safe_setuid_grab();
1217         err = fd_lock(fd, F_UNLCK);
1218         safe_setuid_drop();
1219
1220         if (err) return 1;
1221
1222         (void)fd_close(fd);
1223         return 0;
1224 }
1225
1226
1227 /*!
1228  * @brief 墓のアスキーアートテンプレを読み込む
1229  * @param buf テンプレへのバッファ
1230  * @param buf_size バッファの長さ
1231  * @return なし
1232  */
1233 void read_dead_file(char *buf, size_t buf_size)
1234 {
1235         path_build(buf, buf_size, ANGBAND_DIR_FILE, _("dead_j.txt", "dead.txt"));
1236
1237         FILE *fp;
1238         fp = my_fopen(buf, "r");
1239         if (!fp) return;
1240
1241         int i = 0;
1242         while (my_fgets(fp, buf, buf_size) == 0)
1243         {
1244                 put_str(buf, i++, 0);
1245         }
1246
1247         my_fclose(fp);
1248 }