OSDN Git Service

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