OSDN Git Service

[Refactor] #39962 Reshaped 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/gf-descriptions.h"
35
36 #define PREF_TYPE_NORMAL   0
37 #define PREF_TYPE_AUTOPICK 1
38 #define PREF_TYPE_HISTPREF 2
39
40 concptr ANGBAND_DIR; //!< Path name: The main "lib" directory This variable is not actually used anywhere in the code
41 concptr ANGBAND_DIR_APEX; //!< High score files (binary) These files may be portable between platforms
42 concptr ANGBAND_DIR_BONE; //!< Bone files for player ghosts (ascii) These files are portable between platforms
43 concptr ANGBAND_DIR_DATA; //!< Binary image files for the "*_info" arrays (binary) These files are not portable between platforms
44 concptr ANGBAND_DIR_EDIT; //!< Textual template files for the "*_info" arrays (ascii) These files are portable between platforms
45 concptr ANGBAND_DIR_SCRIPT; //!< Script files These files are portable between platforms.
46 concptr ANGBAND_DIR_FILE; //!< Various extra files (ascii) These files may be portable between platforms
47 concptr ANGBAND_DIR_HELP; //!< Help files (normal) for the online help (ascii) These files are portable between platforms
48 concptr ANGBAND_DIR_INFO; //!< Help files (spoilers) for the online help (ascii) These files are portable between platforms
49 concptr ANGBAND_DIR_PREF; //!< Default user "preference" files (ascii) These files are rarely portable between platforms
50 concptr ANGBAND_DIR_SAVE; //!< Savefiles for current characters (binary)
51 concptr ANGBAND_DIR_USER; //!< User "preference" files (ascii) These files are rarely portable between platforms
52 concptr ANGBAND_DIR_XTRA; //!< Various extra files (binary) These files are rarely portable between platforms
53
54 /*
55  * Buffer to hold the current savefile name
56  * 'savefile' holds full path name. 'savefile_base' holds only base name.
57  */
58 char savefile[1024];
59 char savefile_base[40];
60
61 /*!
62  * @brief 各種データテキストをトークン単位に分解する / Extract the first few "tokens" from a buffer
63  * @param buf データテキストの参照ポインタ
64  * @param num トークンの数
65  * @param tokens トークンを保管する文字列参照ポインタ配列
66  * @param mode オプション
67  * @return 解釈した文字列数
68  * @details
69  * <pre>
70  * This function uses "colon" and "slash" as the delimeter characters.
71  * We never extract more than "num" tokens.  The "last" token may include
72  * "delimeter" characters, allowing the buffer to include a "string" token.
73  * We save pointers to the tokens in "tokens", and return the number found.
74  * Hack -- Attempt to handle the 'c' character formalism
75  * Hack -- An empty buffer, or a final delimeter, yields an "empty" token.
76  * Hack -- We will always extract at least one token
77  * </pre>
78  */
79 s16b tokenize(char *buf, s16b num, char **tokens, BIT_FLAGS mode)
80 {
81         s16b i = 0;
82         char *s = buf;
83         while (i < num - 1)
84         {
85                 char *t;
86                 for (t = s; *t; t++)
87                 {
88                         if ((*t == ':') || (*t == '/')) break;
89
90                         if ((mode & TOKENIZE_CHECKQUOTE) && (*t == '\''))
91                         {
92                                 t++;
93                                 if (*t == '\\') t++;
94                                 if (!*t) break;
95
96                                 t++;
97                                 if (*t != '\'') *t = '\'';
98                         }
99
100                         if (*t == '\\') t++;
101                 }
102
103                 if (!*t) break;
104
105                 *t++ = '\0';
106                 tokens[i++] = s;
107                 s = t;
108         }
109
110         tokens[i++] = s;
111         return i;
112 }
113
114
115 /*!
116  * @brief 設定ファイルの各行から各種テキスト情報を取得する /
117  * Parse a sub-file of the "extra info" (format shown below)
118  * @param creature_ptr プレーヤーへの参照ポインタ
119  * @param buf データテキストの参照ポインタ
120  * @return エラーコード
121  * @details
122  * <pre>
123  * Each "action" line has an "action symbol" in the first column,
124  * followed by a colon, followed by some command specific info,
125  * usually in the form of "tokens" separated by colons or slashes.
126  * Blank lines, lines starting with white space, and lines starting
127  * with pound signs ("#") are ignored (as comments).
128  * Note the use of "tokenize()" to allow the use of both colons and
129  * slashes as delimeters, while still allowing final tokens which
130  * may contain any characters including "delimiters".
131  * Note the use of "strtol()" to allow all "integers" to be encoded
132  * in decimal, hexidecimal, or octal form.
133  * Note that "monster zero" is used for the "player" attr/char, "object
134  * zero" will be used for the "stack" attr/char, and "feature zero" is
135  * used for the "nothing" attr/char.
136  * Parse another file recursively, see below for details
137  *   %:\<filename\>
138  * Specify the attr/char values for "monsters" by race index
139  *   R:\<num\>:\<a\>:\<c\>
140  * Specify the attr/char values for "objects" by kind index
141  *   K:\<num\>:\<a\>:\<c\>
142  * Specify the attr/char values for "features" by feature index
143  *   F:\<num\>:\<a\>:\<c\>
144  * Specify the attr/char values for unaware "objects" by kind tval
145  *   U:\<tv\>:\<a\>:\<c\>
146  * Specify the attr/char values for inventory "objects" by kind tval
147  *   E:\<tv\>:\<a\>:\<c\>
148  * Define a macro action, given an encoded macro action
149  *   A:\<str\>
150  * Create a normal macro, given an encoded macro trigger
151  *   P:\<str\>
152  * Create a command macro, given an encoded macro trigger
153  *   C:\<str\>
154  * Create a keyset mapping
155  *   S:\<key\>:\<key\>:\<dir\>
156  * Turn an option off, given its name
157  *   X:\<str\>
158  * Turn an option on, given its name
159  *   Y:\<str\>
160  * Specify visual information, given an index, and some data
161  *   V:\<num\>:\<kv\>:\<rv\>:\<gv\>:\<bv\>
162  * Specify the set of colors to use when drawing a zapped spell
163  *   Z:\<type\>:\<str\>
164  * Specify a macro trigger template and macro trigger names.
165  *   T:\<template\>:\<modifier chr\>:\<modifier name1\>:\<modifier name2\>:...
166  *   T:\<trigger\>:\<keycode\>:\<shift-keycode\>
167  * </pre>
168  */
169 errr process_pref_file_command(player_type *creature_ptr, char *buf)
170 {
171         if (buf[1] != ':') return 1;
172
173         char *zz[16];
174         switch (buf[0])
175         {
176         case 'H':
177         {
178                 /* Process "H:<history>" */
179                 add_history_from_pref_line(buf + 2);
180                 return 0;
181         }
182         case 'R':
183         {
184                 /* Process "R:<num>:<a>/<c>" -- attr/char for monster races */
185                 if (tokenize(buf + 2, 3, zz, TOKENIZE_CHECKQUOTE) != 3) return 1;
186
187                 monster_race *r_ptr;
188                 int i = (huge)strtol(zz[0], NULL, 0);
189                 TERM_COLOR n1 = (TERM_COLOR)strtol(zz[1], NULL, 0);
190                 SYMBOL_CODE n2 = (SYMBOL_CODE)strtol(zz[2], NULL, 0);
191                 if (i >= max_r_idx) return 1;
192                 r_ptr = &r_info[i];
193                 if (n1 || (!(n2 & 0x80) && n2)) r_ptr->x_attr = n1; /* Allow TERM_DARK text */
194                 if (n2) r_ptr->x_char = n2;
195                 return 0;
196         }
197         case 'K':
198         {
199                 /* Process "K:<num>:<a>/<c>"  -- attr/char for object kinds */
200                 if (tokenize(buf + 2, 3, zz, TOKENIZE_CHECKQUOTE) != 3) return 1;
201                 
202                 object_kind *k_ptr;
203                 int i = (huge)strtol(zz[0], NULL, 0);
204                 TERM_COLOR n1 = (TERM_COLOR)strtol(zz[1], NULL, 0);
205                 SYMBOL_CODE n2 = (SYMBOL_CODE)strtol(zz[2], NULL, 0);
206                 if (i >= max_k_idx) return 1;
207                 k_ptr = &k_info[i];
208                 if (n1 || (!(n2 & 0x80) && n2)) k_ptr->x_attr = n1; /* Allow TERM_DARK text */
209                 if (n2) k_ptr->x_char = n2;
210                 return 0;
211         }
212         case 'F':
213         {
214                 /* Process "F:<num>:<a>/<c>" -- attr/char for terrain features */
215                 /* "F:<num>:<a>/<c>" */
216                 /* "F:<num>:<a>/<c>:LIT" */
217                 /* "F:<num>:<a>/<c>:<la>/<lc>:<da>/<dc>" */
218                 feature_type *f_ptr;
219                 int num = tokenize(buf + 2, F_LIT_MAX * 2 + 1, zz, TOKENIZE_CHECKQUOTE);
220
221                 if ((num != 3) && (num != 4) && (num != F_LIT_MAX * 2 + 1)) return 1;
222                 else if ((num == 4) && !streq(zz[3], "LIT")) return 1;
223
224                 int i = (huge)strtol(zz[0], NULL, 0);
225                 if (i >= max_f_idx) return 1;
226                 f_ptr = &f_info[i];
227
228                 TERM_COLOR n1 = (TERM_COLOR)strtol(zz[1], NULL, 0);
229                 SYMBOL_CODE n2 = (SYMBOL_CODE)strtol(zz[2], NULL, 0);
230                 if (n1 || (!(n2 & 0x80) && n2)) f_ptr->x_attr[F_LIT_STANDARD] = n1; /* Allow TERM_DARK text */
231                 if (n2) f_ptr->x_char[F_LIT_STANDARD] = n2;
232
233                 switch (num)
234                 {
235                 case 3:
236                 {
237                         /* No lighting support */
238                         n1 = f_ptr->x_attr[F_LIT_STANDARD];
239                         n2 = f_ptr->x_char[F_LIT_STANDARD];
240                         for (int j = F_LIT_NS_BEGIN; j < F_LIT_MAX; j++)
241                         {
242                                 f_ptr->x_attr[j] = n1;
243                                 f_ptr->x_char[j] = n2;
244                         }
245
246                         return 0;
247                 }
248                 case 4:
249                 {
250                         /* Use default lighting */
251                         apply_default_feat_lighting(f_ptr->x_attr, f_ptr->x_char);
252                         return 0;
253                 }
254                 case F_LIT_MAX * 2 + 1:
255                 {
256                         /* Use desired lighting */
257                         for (int j = F_LIT_NS_BEGIN; j < F_LIT_MAX; j++)
258                         {
259                                 n1 = (TERM_COLOR)strtol(zz[j * 2 + 1], NULL, 0);
260                                 n2 = (SYMBOL_CODE)strtol(zz[j * 2 + 2], NULL, 0);
261                                 if (n1 || (!(n2 & 0x80) && n2)) f_ptr->x_attr[j] = n1; /* Allow TERM_DARK text */
262                                 if (n2) f_ptr->x_char[j] = n2;
263                         }
264
265                         return 0;
266                 }
267                 default:
268                         return 0;
269                 }
270         }
271         case 'S':
272         {
273                 /* Process "S:<num>:<a>/<c>" -- attr/char for special things */
274                 if (tokenize(buf + 2, 3, zz, TOKENIZE_CHECKQUOTE) != 3) return 1;
275
276                 int j = (byte)strtol(zz[0], NULL, 0);
277                 TERM_COLOR n1 = (TERM_COLOR)strtol(zz[1], NULL, 0);
278                 SYMBOL_CODE n2 = (SYMBOL_CODE)strtol(zz[2], NULL, 0);
279                 misc_to_attr[j] = n1;
280                 misc_to_char[j] = n2;
281                 return 0;
282         }
283         case 'U':
284         {
285                 /* Process "U:<tv>:<a>/<c>" -- attr/char for unaware items */
286                 if (tokenize(buf + 2, 3, zz, TOKENIZE_CHECKQUOTE) != 3) return 1;
287
288                 int j = (huge)strtol(zz[0], NULL, 0);
289                 TERM_COLOR n1 = (TERM_COLOR)strtol(zz[1], NULL, 0);
290                 SYMBOL_CODE n2 = (SYMBOL_CODE)strtol(zz[2], NULL, 0);
291                 for (int i = 1; i < max_k_idx; i++)
292                 {
293                         object_kind *k_ptr = &k_info[i];
294                         if (k_ptr->tval == j)
295                         {
296                                 if (n1) k_ptr->d_attr = n1;
297                                 if (n2) k_ptr->d_char = n2;
298                         }
299                 }
300
301                 return 0;
302         }
303         case 'E':
304         {
305                 /* Process "E:<tv>:<a>" -- attribute for inventory objects */
306                 if (tokenize(buf + 2, 2, zz, TOKENIZE_CHECKQUOTE) != 2) return 1;
307
308                 int j = (byte)strtol(zz[0], NULL, 0) % 128;
309                 TERM_COLOR n1 = (TERM_COLOR)strtol(zz[1], NULL, 0);
310                 if (n1) tval_to_attr[j] = n1;
311                 return 0;
312         }
313         case 'A':
314         {
315                 /* Process "A:<str>" -- save an "action" for later */
316                 text_to_ascii(macro__buf, buf + 2);
317                 return 0;
318         }
319         case 'P':
320         {
321                 /* Process "P:<str>" -- normal macro */
322                 char tmp[1024];
323                 text_to_ascii(tmp, buf + 2);
324                 macro_add(tmp, macro__buf);
325                 return 0;
326         }
327         case 'C':
328         {
329                 /* Process "C:<str>" -- create keymap */
330                 if (tokenize(buf + 2, 2, zz, TOKENIZE_CHECKQUOTE) != 2) return 1;
331
332                 int mode = strtol(zz[0], NULL, 0);
333                 if ((mode < 0) || (mode >= KEYMAP_MODES)) return 1;
334
335                 char tmp[1024];
336                 text_to_ascii(tmp, zz[1]);
337                 if (!tmp[0] || tmp[1]) return 1;
338
339                 int i = (byte)(tmp[0]);
340                 string_free(keymap_act[mode][i]);
341                 keymap_act[mode][i] = string_make(macro__buf);
342                 return 0;
343         }
344         case 'V':
345         {
346                 /* Process "V:<num>:<kv>:<rv>:<gv>:<bv>" -- visual info */
347                 if (tokenize(buf + 2, 5, zz, TOKENIZE_CHECKQUOTE) != 5) return 1;
348
349                 int i = (byte)strtol(zz[0], NULL, 0);
350                 angband_color_table[i][0] = (byte)strtol(zz[1], NULL, 0);
351                 angband_color_table[i][1] = (byte)strtol(zz[2], NULL, 0);
352                 angband_color_table[i][2] = (byte)strtol(zz[3], NULL, 0);
353                 angband_color_table[i][3] = (byte)strtol(zz[4], NULL, 0);
354                 return 0;
355         }
356         case 'X':
357         case 'Y':
358         {
359                 /* Process "X:<str>" -- turn option off */
360                 /* Process "Y:<str>" -- turn option on */
361                 for (int i = 0; option_info[i].o_desc; i++)
362                 {
363                         bool is_option = option_info[i].o_var != NULL;
364                         is_option &= option_info[i].o_text != NULL;
365                         is_option &= streq(option_info[i].o_text, buf + 2);
366                         if (!is_option) continue;
367
368                         int os = option_info[i].o_set;
369                         int ob = option_info[i].o_bit;
370
371                         if ((creature_ptr->playing || current_world_ptr->character_xtra) &&
372                                 (OPT_PAGE_BIRTH == option_info[i].o_page) && !current_world_ptr->wizard)
373                         {
374                                 msg_format(_("初期オプションは変更できません! '%s'", "Birth options can not changed! '%s'"), buf);
375                                 msg_print(NULL);
376                                 return 0;
377                         }
378
379                         if (buf[0] == 'X')
380                         {
381                                 option_flag[os] &= ~(1L << ob);
382                                 (*option_info[i].o_var) = FALSE;
383                                 return 0;
384                         }
385
386                         option_flag[os] |= (1L << ob);
387                         (*option_info[i].o_var) = TRUE;
388                         return 0;
389                 }
390
391                 msg_format(_("オプションの名前が正しくありません: %s", "Ignored invalid option: %s"), buf);
392                 msg_print(NULL);
393                 return 0;
394         }
395         case 'Z':
396         {
397                 /* Process "Z:<type>:<str>" -- set spell color */
398                 char *t = my_strchr(buf + 2, ':');
399                 if (!t) return 1;
400
401                 *(t++) = '\0';
402                 for (int i = 0; i < MAX_NAMED_NUM; i++)
403                 {
404                         if (!streq(gf_desc[i].name, buf + 2)) continue;
405
406                         gf_color[gf_desc[i].num] = (TERM_COLOR)quark_add(t);
407                         return 0;
408                 }
409
410                 return 1;
411         }
412         case 'T':
413         {
414                 /* Initialize macro trigger names and a template */
415                 /* Process "T:<trigger>:<keycode>:<shift-keycode>" */
416                 /* Process "T:<template>:<modifier chr>:<modifier name>:..." */
417                 int tok = tokenize(buf + 2, 2 + MAX_MACRO_MOD, zz, 0);
418
419                 /* Process "T:<template>:<modifier chr>:<modifier name>:..." */
420                 if (tok >= 4)
421                 {
422                         if (macro_template != NULL)
423                         {
424                                 int macro_modifier_length = strlen(macro_modifier_chr);
425                                 string_free(macro_template);
426                                 macro_template = NULL;
427                                 string_free(macro_modifier_chr);
428                                 for (int i = 0; i < macro_modifier_length; i++)
429                                 {
430                                         string_free(macro_modifier_name[i]);
431                                 }
432
433                                 for (int i = 0; i < max_macrotrigger; i++)
434                                 {
435                                         string_free(macro_trigger_name[i]);
436                                         string_free(macro_trigger_keycode[0][i]);
437                                         string_free(macro_trigger_keycode[1][i]);
438                                 }
439
440                                 max_macrotrigger = 0;
441                         }
442
443                         if (*zz[0] == '\0') return 0;
444
445                         int zz_length = strlen(zz[1]);
446                         zz_length = MIN(MAX_MACRO_MOD, zz_length);
447                         if (2 + zz_length != tok) return 1;
448
449                         macro_template = string_make(zz[0]);
450                         macro_modifier_chr = string_make(zz[1]);
451                         for (int i = 0; i < zz_length; i++)
452                         {
453                                 macro_modifier_name[i] = string_make(zz[2 + i]);
454                         }
455
456                         return 0;
457                 }
458
459                 /* Process "T:<trigger>:<keycode>:<shift-keycode>" */
460                 if (tok < 2) return 0;
461
462                 char buf_aux[1024];
463                 char *t, *s;
464                 if (max_macrotrigger >= MAX_MACRO_TRIG)
465                 {
466                         msg_print(_("マクロトリガーの設定が多すぎます!", "Too many macro triggers!"));
467                         return 1;
468                 }
469
470                 int m = max_macrotrigger;
471                 max_macrotrigger++;
472                 t = buf_aux;
473                 s = zz[0];
474                 while (*s)
475                 {
476                         if ('\\' == *s) s++;
477                         *t++ = *s++;
478                 }
479
480                 *t = '\0';
481                 macro_trigger_name[m] = string_make(buf_aux);
482                 macro_trigger_keycode[0][m] = string_make(zz[1]);
483                 if (tok == 3)
484                 {
485                         macro_trigger_keycode[1][m] = string_make(zz[2]);
486                         return 0;
487                 }
488
489                 macro_trigger_keycode[1][m] = string_make(zz[1]);
490                 return 0;
491         }
492         }
493
494         return 1;
495 }
496
497
498 /*!
499  * @brief process_pref_fileのサブルーチンとして条件分岐処理の解釈と結果を返す /
500  * Helper function for "process_pref_file()"
501  * @param creature_ptr プレーヤーへの参照ポインタ
502  * @param sp テキスト文字列の参照ポインタ
503  * @param fp 再帰中のポインタ参照
504  * @return
505  * @details
506  * <pre>
507  * Input:
508  *   v: output buffer array
509  *   f: final character
510  * Output:
511  *   result
512  * </pre>
513  */
514 concptr process_pref_file_expr(player_type *creature_ptr, char **sp, char *fp)
515 {
516         char *s;
517         s = (*sp);
518         while (iswspace(*s)) s++;
519
520         char *b;
521         b = s;
522
523         concptr v = "?o?o?";
524
525         char b1 = '[';
526         char b2 = ']';
527         char f = ' ';
528         static char tmp[16];
529         if (*s == b1)
530         {
531                 concptr p;
532                 concptr t;
533
534                 /* Skip b1 */
535                 s++;
536
537                 /* First */
538                 t = process_pref_file_expr(creature_ptr, &s, &f);
539
540                 if (!*t)
541                 {
542                 }
543                 else if (streq(t, "IOR"))
544                 {
545                         v = "0";
546                         while (*s && (f != b2))
547                         {
548                                 t = process_pref_file_expr(creature_ptr, &s, &f);
549                                 if (*t && !streq(t, "0")) v = "1";
550                         }
551                 }
552                 else if (streq(t, "AND"))
553                 {
554                         v = "1";
555                         while (*s && (f != b2))
556                         {
557                                 t = process_pref_file_expr(creature_ptr, &s, &f);
558                                 if (*t && streq(t, "0")) v = "0";
559                         }
560                 }
561                 else if (streq(t, "NOT"))
562                 {
563                         v = "1";
564                         while (*s && (f != b2))
565                         {
566                                 t = process_pref_file_expr(creature_ptr, &s, &f);
567                                 if (*t && streq(t, "1")) v = "0";
568                         }
569                 }
570                 else if (streq(t, "EQU"))
571                 {
572                         v = "0";
573                         if (*s && (f != b2))
574                         {
575                                 t = process_pref_file_expr(creature_ptr, &s, &f);
576                         }
577                         while (*s && (f != b2))
578                         {
579                                 p = process_pref_file_expr(creature_ptr, &s, &f);
580                                 if (streq(t, p)) v = "1";
581                         }
582                 }
583                 else if (streq(t, "LEQ"))
584                 {
585                         v = "1";
586                         if (*s && (f != b2))
587                         {
588                                 t = process_pref_file_expr(creature_ptr, &s, &f);
589                         }
590                         while (*s && (f != b2))
591                         {
592                                 p = t;
593                                 t = process_pref_file_expr(creature_ptr, &s, &f);
594                                 if (*t && atoi(p) > atoi(t)) v = "0";
595                         }
596                 }
597                 else if (streq(t, "GEQ"))
598                 {
599                         v = "1";
600                         if (*s && (f != b2))
601                         {
602                                 t = process_pref_file_expr(creature_ptr, &s, &f);
603                         }
604                         while (*s && (f != b2))
605                         {
606                                 p = t;
607                                 t = process_pref_file_expr(creature_ptr, &s, &f);
608                                 if (*t && atoi(p) < atoi(t)) v = "0";
609                         }
610                 }
611                 else
612                 {
613                         while (*s && (f != b2))
614                         {
615                                 t = process_pref_file_expr(creature_ptr, &s, &f);
616                         }
617                 }
618
619                 if (f != b2) v = "?x?x?";
620
621                 if ((f = *s) != '\0') *s++ = '\0';
622
623                 *fp = f;
624                 *sp = s;
625                 return v;
626         }
627
628         /* Accept all printables except spaces and brackets */
629 #ifdef JP
630         while (iskanji(*s) || (isprint(*s) && !my_strchr(" []", *s)))
631         {
632                 if (iskanji(*s)) s++;
633                 s++;
634         }
635 #else
636         while (isprint(*s) && !my_strchr(" []", *s)) ++s;
637 #endif
638
639         if ((f = *s) != '\0') *s++ = '\0';
640
641         if (*b != '$')
642         {
643                 v = b;
644                 *fp = f;
645                 *sp = s;
646                 return v;
647         }
648
649         if (streq(b + 1, "SYS"))
650         {
651                 v = ANGBAND_SYS;
652         }
653         else if (streq(b + 1, "KEYBOARD"))
654         {
655                 v = ANGBAND_KEYBOARD;
656         }
657         else if (streq(b + 1, "GRAF"))
658         {
659                 v = ANGBAND_GRAF;
660         }
661         else if (streq(b + 1, "MONOCHROME"))
662         {
663                 if (arg_monochrome)
664                         v = "ON";
665                 else
666                         v = "OFF";
667         }
668         else if (streq(b + 1, "RACE"))
669         {
670 #ifdef JP
671                 v = rp_ptr->E_title;
672 #else
673                 v = rp_ptr->title;
674 #endif
675         }
676         else if (streq(b + 1, "CLASS"))
677         {
678 #ifdef JP
679                 v = cp_ptr->E_title;
680 #else
681                 v = cp_ptr->title;
682 #endif
683         }
684         else if (streq(b + 1, "PLAYER"))
685         {
686                 static char tmp_player_name[32];
687                 char *pn, *tpn;
688                 for (pn = creature_ptr->name, tpn = tmp_player_name; *pn; pn++, tpn++)
689                 {
690 #ifdef JP
691                         if (iskanji(*pn))
692                         {
693                                 *(tpn++) = *(pn++);
694                                 *tpn = *pn;
695                                 continue;
696                         }
697 #endif
698                         *tpn = my_strchr(" []", *pn) ? '_' : *pn;
699                 }
700
701                 *tpn = '\0';
702                 v = tmp_player_name;
703         }
704         else if (streq(b + 1, "REALM1"))
705         {
706 #ifdef JP
707                 v = E_realm_names[creature_ptr->realm1];
708 #else
709                 v = realm_names[creature_ptr->realm1];
710 #endif
711         }
712         else if (streq(b + 1, "REALM2"))
713         {
714 #ifdef JP
715                 v = E_realm_names[creature_ptr->realm2];
716 #else
717                 v = realm_names[creature_ptr->realm2];
718 #endif
719         }
720         else if (streq(b + 1, "LEVEL"))
721         {
722                 sprintf(tmp, "%02d", creature_ptr->lev);
723                 v = tmp;
724         }
725         else if (streq(b + 1, "AUTOREGISTER"))
726         {
727                 if (creature_ptr->autopick_autoregister)
728                         v = "1";
729                 else
730                         v = "0";
731         }
732         else if (streq(b + 1, "MONEY"))
733         {
734                 sprintf(tmp, "%09ld", (long int)creature_ptr->au);
735                 v = tmp;
736         }
737
738         *fp = f;
739         *sp = s;
740         return v;
741 }
742
743
744 /*!
745  * @brief process_pref_fileのサブルーチン /
746  * Open the "user pref file" and parse it.
747  * @param creature_ptr プレーヤーへの参照ポインタ
748  * @param name 読み込むファイル名
749  * @param preftype prefファイルのタイプ
750  * @return エラーコード
751  * @details
752  * <pre>
753  * Input:
754  *   v: output buffer array
755  *   f: final character
756  * Output:
757  *   result
758  * </pre>
759  */
760 static errr process_pref_file_aux(player_type *creature_ptr, concptr name, int preftype)
761 {
762         FILE *fp;
763         fp = my_fopen(name, "r");
764         if (!fp) return -1;
765
766         char buf[1024];
767         char old[1024];
768         int line = -1;
769         errr err = 0;
770         bool bypass = FALSE;
771         while (my_fgets(fp, buf, sizeof(buf)) == 0)
772         {
773                 line++;
774                 if (!buf[0]) continue;
775
776 #ifdef JP
777                 if (!iskanji(buf[0]))
778 #endif
779                         if (iswspace(buf[0])) continue;
780
781                 if (buf[0] == '#') continue;
782                 strcpy(old, buf);
783
784                 /* Process "?:<expr>" */
785                 if ((buf[0] == '?') && (buf[1] == ':'))
786                 {
787                         char f;
788                         char *s;
789                         s = buf + 2;
790                         concptr v = process_pref_file_expr(creature_ptr, &s, &f);
791                         bypass = streq(v, "0");
792                         continue;
793                 }
794
795                 if (bypass) continue;
796
797                 /* Process "%:<file>" */
798                 if (buf[0] == '%')
799                 {
800                         static int depth_count = 0;
801                         if (depth_count > 20) continue;
802
803                         depth_count++;
804                         switch (preftype)
805                         {
806                         case PREF_TYPE_AUTOPICK:
807                                 (void)process_autopick_file(creature_ptr, buf + 2);
808                                 break;
809                         case PREF_TYPE_HISTPREF:
810                                 (void)process_histpref_file(creature_ptr, buf + 2);
811                                 break;
812                         default:
813                                 (void)process_pref_file(creature_ptr, buf + 2);
814                                 break;
815                         }
816
817                         depth_count--;
818                         continue;
819                 }
820
821                 err = process_pref_file_command(creature_ptr, buf);
822                 if (err != 0)
823                 {
824                         if (preftype != PREF_TYPE_AUTOPICK)
825                                 break;
826                         err = process_autopick_file_command(buf);
827                 }
828         }
829
830         if (err != 0)
831         {
832                 /* Print error message */
833                 /* ToDo: Add better error messages */
834                 msg_format(_("ファイル'%s'の%d行でエラー番号%dのエラー。", "Error %d in line %d of file '%s'."),
835                         _(name, err), line, _(err, name));
836                 msg_format(_("('%s'を解析中)", "Parsing '%s'"), old);
837                 msg_print(NULL);
838         }
839
840         my_fclose(fp);
841         return (err);
842 }
843
844
845 /*!
846  * @brief pref設定ファイルを読み込み設定を反映させる /
847  * Process the "user pref file" with the given name
848  * @param creature_ptr プレーヤーへの参照ポインタ
849  * @param name 読み込むファイル名
850  * @return エラーコード
851  * @details
852  * <pre>
853  * See the functions above for a list of legal "commands".
854  * We also accept the special "?" and "%" directives, which
855  * allow conditional evaluation and filename inclusion.
856  * </pre>
857  */
858 errr process_pref_file(player_type *creature_ptr, concptr name)
859 {
860         char buf[1024];
861         path_build(buf, sizeof(buf), ANGBAND_DIR_PREF, name);
862
863         errr err1 = process_pref_file_aux(creature_ptr, buf, PREF_TYPE_NORMAL);
864         if (err1 > 0) return err1;
865
866         path_build(buf, sizeof(buf), ANGBAND_DIR_USER, name);
867         errr err2 = process_pref_file_aux(creature_ptr, buf, PREF_TYPE_NORMAL);
868         if (err2 < 0 && !err1)
869                 return -2;
870
871         return err2;
872 }
873
874
875 /*!
876  * @brief プレイヤーステータスをファイルダンプ出力する
877  * Hack -- Dump a character description file
878  * @param creature_ptr プレーヤーへの参照ポインタ
879  * @param name 出力ファイル名
880  * @return エラーコード
881  * @details
882  * Allow the "full" flag to dump additional info,
883  * and trigger its usage from various places in the code.
884  */
885 errr file_character(player_type *creature_ptr, concptr name, display_player_pf display_player, map_name_pf map_name)
886 {
887         char buf[1024];
888         path_build(buf, sizeof(buf), ANGBAND_DIR_USER, name);
889
890         FILE_TYPE(FILE_TYPE_TEXT);
891
892         int     fd = fd_open(buf, O_RDONLY);
893         if (fd >= 0)
894         {
895                 char out_val[160];
896                 (void)fd_close(fd);
897                 (void)sprintf(out_val, _("現存するファイル %s に上書きしますか? ", "Replace existing file %s? "), buf);
898                 if (get_check_strict(out_val, CHECK_NO_HISTORY)) fd = -1;
899         }
900
901         FILE *fff = NULL;
902         if (fd < 0) fff = my_fopen(buf, "w");
903
904         if (!fff)
905         {
906                 prt(_("キャラクタ情報のファイルへの書き出しに失敗しました!", "Character dump failed!"), 0, 0);
907                 (void)inkey();
908                 return -1;
909         }
910
911         make_character_dump(creature_ptr, fff, update_playtime, display_player, map_name);
912         my_fclose(fff);
913         msg_print(_("キャラクタ情報のファイルへの書き出しに成功しました。", "Character dump successful."));
914         msg_print(NULL);
915         return 0;
916 }
917
918
919 /*!
920  * @brief ファイル内容の一行をコンソールに出力する
921  * Display single line of on-line help file
922  * @param str 出力する文字列
923  * @param cy コンソールの行
924  * @param shower 確認中
925  * @return なし
926  * @details
927  * <pre>
928  * You can insert some special color tag to change text color.
929  * Such as...
930  * WHITETEXT [[[[y|SOME TEXT WHICH IS DISPLAYED IN YELLOW| WHITETEXT
931  * A colored segment is between "[[[[y|" and the last "|".
932  * You can use any single character in place of the "|".
933  * </pre>
934  */
935 static void show_file_aux_line(concptr str, int cy, concptr shower)
936 {
937         char lcstr[1024];
938         if (shower)
939         {
940                 strcpy(lcstr, str);
941                 str_tolower(lcstr);
942         }
943
944         int cx = 0;
945         Term_gotoxy(cx, cy);
946
947         static const char tag_str[] = "[[[[";
948         byte color = TERM_WHITE;
949         char in_tag = '\0';
950         for (int i = 0; str[i];)
951         {
952                 int len = strlen(&str[i]);
953                 int showercol = len + 1;
954                 int bracketcol = len + 1;
955                 int endcol = len;
956                 concptr ptr;
957                 if (shower)
958                 {
959                         ptr = my_strstr(&lcstr[i], shower);
960                         if (ptr) showercol = ptr - &lcstr[i];
961                 }
962
963                 ptr = in_tag ? my_strchr(&str[i], in_tag) : my_strstr(&str[i], tag_str);
964                 if (ptr) bracketcol = ptr - &str[i];
965                 if (bracketcol < endcol) endcol = bracketcol;
966                 if (showercol < endcol) endcol = showercol;
967
968                 Term_addstr(endcol, color, &str[i]);
969                 cx += endcol;
970                 i += endcol;
971
972                 if (endcol == showercol)
973                 {
974                         int showerlen = strlen(shower);
975                         Term_addstr(showerlen, TERM_YELLOW, &str[i]);
976                         cx += showerlen;
977                         i += showerlen;
978                         continue;
979                 }
980
981                 if (endcol != bracketcol) continue;
982
983                 if (in_tag)
984                 {
985                         i++;
986                         in_tag = '\0';
987                         color = TERM_WHITE;
988                         continue;
989                 }
990
991                 i += sizeof(tag_str) - 1;
992                 color = color_char_to_attr(str[i]);
993                 if (color == 255 || str[i + 1] == '\0')
994                 {
995                         color = TERM_WHITE;
996                         Term_addstr(-1, TERM_WHITE, tag_str);
997                         cx += sizeof(tag_str) - 1;
998                         continue;
999                 }
1000
1001                 i++;
1002                 in_tag = str[i];
1003                 i++;
1004         }
1005
1006         Term_erase(cx, cy, 255);
1007 }
1008
1009
1010 /*!
1011  * @brief ファイル内容をコンソールに出力する
1012  * Recursive file perusal.
1013  * @param creature_ptr プレーヤーへの参照ポインタ
1014  * @param show_version TRUEならばコンソール上にゲームのバージョンを表示する
1015  * @param name ファイル名の文字列
1016  * @param what 内容キャプションの文字列
1017  * @param line 表示の現在行
1018  * @param mode オプション
1019  * @return なし
1020  * @details
1021  * <pre>
1022  * Process various special text in the input file, including
1023  * the "menu" structures used by the "help file" system.
1024  * Return FALSE on 'q' to exit from a deep, otherwise TRUE.
1025  * </pre>
1026  */
1027 bool show_file(player_type *creature_ptr, bool show_version, concptr name, concptr what, int line, BIT_FLAGS mode)
1028 {
1029         int wid, hgt;
1030         Term_get_size(&wid, &hgt);
1031
1032         char finder_str[81];
1033         strcpy(finder_str, "");
1034
1035         char shower_str[81];
1036         strcpy(shower_str, "");
1037
1038         char caption[128];
1039         strcpy(caption, "");
1040
1041         char hook[68][32];
1042         for (int i = 0; i < 68; i++)
1043         {
1044                 hook[i][0] = '\0';
1045         }
1046
1047         char filename[1024];
1048         strcpy(filename, name);
1049         int n = strlen(filename);
1050
1051         concptr tag = NULL;
1052         for (int i = 0; i < n; i++)
1053         {
1054                 if (filename[i] == '#')
1055                 {
1056                         filename[i] = '\0';
1057                         tag = filename + i + 1;
1058                         break;
1059                 }
1060         }
1061
1062         name = filename;
1063         FILE *fff = NULL;
1064         char path[1024];
1065         if (what)
1066         {
1067                 strcpy(caption, what);
1068                 strcpy(path, name);
1069                 fff = my_fopen(path, "r");
1070         }
1071
1072         if (!fff)
1073         {
1074                 sprintf(caption, _("ヘルプ・ファイル'%s'", "Help file '%s'"), name);
1075                 path_build(path, sizeof(path), ANGBAND_DIR_HELP, name);
1076                 fff = my_fopen(path, "r");
1077         }
1078
1079         if (!fff)
1080         {
1081                 sprintf(caption, _("スポイラー・ファイル'%s'", "Info file '%s'"), name);
1082                 path_build(path, sizeof(path), ANGBAND_DIR_INFO, name);
1083                 fff = my_fopen(path, "r");
1084         }
1085
1086         if (!fff)
1087         {
1088                 path_build(path, sizeof(path), ANGBAND_DIR, name);
1089
1090                 for (int i = 0; path[i]; i++)
1091                         if ('\\' == path[i])
1092                                 path[i] = PATH_SEP[0];
1093
1094                 sprintf(caption, _("スポイラー・ファイル'%s'", "Info file '%s'"), name);
1095                 fff = my_fopen(path, "r");
1096         }
1097
1098         if (!fff)
1099         {
1100                 msg_format(_("'%s'をオープンできません。", "Cannot open '%s'."), name);
1101                 msg_print(NULL);
1102
1103                 return TRUE;
1104         }
1105
1106         int skey;
1107         int next = 0;
1108         int size = 0;
1109         int back = 0;
1110         bool menu = FALSE;
1111         char buf[1024];
1112         bool reverse = (line < 0);
1113         while (TRUE)
1114         {
1115                 char *str = buf;
1116                 if (my_fgets(fff, buf, sizeof(buf))) break;
1117                 if (!prefix(str, "***** "))
1118                 {
1119                         next++;
1120                         continue;
1121                 }
1122
1123                 if ((str[6] == '[') && isalpha(str[7]))
1124                 {
1125                         int k = str[7] - 'A';
1126                         menu = TRUE;
1127                         if ((str[8] == ']') && (str[9] == ' '))
1128                         {
1129                                 strncpy(hook[k], str + 10, 31);
1130                                 hook[k][31] = '\0';
1131                         }
1132
1133                         continue;
1134                 }
1135
1136                 if (str[6] != '<') continue;
1137
1138                 size_t len = strlen(str);
1139                 if (str[len - 1] == '>')
1140                 {
1141                         str[len - 1] = '\0';
1142                         if (tag && streq(str + 7, tag)) line = next;
1143                 }
1144         }
1145
1146         size = next;
1147         int rows = hgt - 4;
1148         if (line == -1)
1149                 line = ((size - 1) / rows)*rows;
1150
1151         Term_clear();
1152
1153         concptr find = NULL;
1154         while (TRUE)
1155         {
1156                 if (line >= size - rows)
1157                         line = size - rows;
1158                 if (line < 0) line = 0;
1159
1160                 if (next > line)
1161                 {
1162                         my_fclose(fff);
1163                         fff = my_fopen(path, "r");
1164                         if (!fff) return FALSE;
1165
1166                         next = 0;
1167                 }
1168
1169                 while (next < line)
1170                 {
1171                         if (my_fgets(fff, buf, sizeof(buf))) break;
1172                         if (prefix(buf, "***** ")) continue;
1173                         next++;
1174                 }
1175
1176                 int row_count = 0;
1177                 concptr shower = NULL;
1178                 for (int i = 0; i < rows; i++)
1179                 {
1180                         concptr str = buf;
1181                         if (!i) line = next;
1182                         if (my_fgets(fff, buf, sizeof(buf))) break;
1183                         if (prefix(buf, "***** ")) continue;
1184                         next++;
1185                         if (find && !i)
1186                         {
1187                                 char lc_buf[1024];
1188                                 strcpy(lc_buf, str);
1189                                 str_tolower(lc_buf);
1190                                 if (!my_strstr(lc_buf, find)) continue;
1191                         }
1192
1193                         find = NULL;
1194                         show_file_aux_line(str, i + 2, shower);
1195                         row_count++;
1196                 }
1197
1198                 while (row_count < rows)
1199                 {
1200                         Term_erase(0, row_count + 2, 255);
1201                         row_count++;
1202                 }
1203
1204                 if (find)
1205                 {
1206                         bell();
1207                         line = back;
1208                         find = NULL;
1209                         continue;
1210                 }
1211
1212                 if (show_version)
1213                 {
1214                         prt(format(_("[変愚蛮怒 %d.%d.%d, %s, %d/%d]", "[Hengband %d.%d.%d, %s, Line %d/%d]"),
1215                                 FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH,
1216                                 caption, line, size), 0, 0);
1217                 }
1218                 else
1219                 {
1220                         prt(format(_("[%s, %d/%d]", "[%s, Line %d/%d]"),
1221                                 caption, line, size), 0, 0);
1222                 }
1223
1224                 if (size <= rows)
1225                 {
1226                         prt(_("[キー:(?)ヘルプ (ESC)終了]", "[Press ESC to exit.]"), hgt - 1, 0);
1227                 }
1228                 else
1229                 {
1230 #ifdef JP
1231                         if (reverse)
1232                                 prt("[キー:(RET/スペース)↑ (-)↓ (?)ヘルプ (ESC)終了]", hgt - 1, 0);
1233                         else
1234                                 prt("[キー:(RET/スペース)↓ (-)↑ (?)ヘルプ (ESC)終了]", hgt - 1, 0);
1235 #else
1236                         prt("[Press Return, Space, -, =, /, |, or ESC to exit.]", hgt - 1, 0);
1237 #endif
1238                 }
1239
1240                 skey = inkey_special(TRUE);
1241                 switch (skey)
1242                 {
1243                 case '?':
1244                         if (strcmp(name, _("jhelpinfo.txt", "helpinfo.txt")) != 0)
1245                                 show_file(creature_ptr, TRUE, _("jhelpinfo.txt", "helpinfo.txt"), NULL, 0, mode);
1246                         break;
1247                 case '=':
1248                         prt(_("強調: ", "Show: "), hgt - 1, 0);
1249
1250                         char back_str[81];
1251                         strcpy(back_str, shower_str);
1252                         if (askfor(shower_str, 80))
1253                         {
1254                                 if (shower_str[0])
1255                                 {
1256                                         str_tolower(shower_str);
1257                                         shower = shower_str;
1258                                 }
1259                                 else shower = NULL;
1260                         }
1261                         else strcpy(shower_str, back_str);
1262                         break;
1263
1264                 case '/':
1265                 case KTRL('s'):
1266                         prt(_("検索: ", "Find: "), hgt - 1, 0);
1267                         strcpy(back_str, finder_str);
1268                         if (askfor(finder_str, 80))
1269                         {
1270                                 if (finder_str[0])
1271                                 {
1272                                         find = finder_str;
1273                                         back = line;
1274                                         line = line + 1;
1275                                         str_tolower(finder_str);
1276                                         shower = finder_str;
1277                                 }
1278                                 else shower = NULL;
1279                         }
1280                         else strcpy(finder_str, back_str);
1281                         break;
1282
1283                 case '#':
1284                 {
1285                         char tmp[81];
1286                         prt(_("行: ", "Goto Line: "), hgt - 1, 0);
1287                         strcpy(tmp, "0");
1288
1289                         if (askfor(tmp, 80)) line = atoi(tmp);
1290                         break;
1291                 }
1292
1293                 case SKEY_TOP:
1294                         line = 0;
1295                         break;
1296
1297                 case SKEY_BOTTOM:
1298                         line = ((size - 1) / rows) * rows;
1299                         break;
1300
1301                 case '%':
1302                 {
1303                         char tmp[81];
1304                         prt(_("ファイル・ネーム: ", "Goto File: "), hgt - 1, 0);
1305                         strcpy(tmp, _("jhelp.hlp", "help.hlp"));
1306
1307                         if (askfor(tmp, 80))
1308                         {
1309                                 if (!show_file(creature_ptr, TRUE, tmp, NULL, 0, mode)) skey = 'q';
1310                         }
1311
1312                         break;
1313                 }
1314
1315                 case '-':
1316                         line = line + (reverse ? rows : -rows);
1317                         if (line < 0) line = 0;
1318                         break;
1319
1320                 case SKEY_PGUP:
1321                         line = line - rows;
1322                         if (line < 0) line = 0;
1323                         break;
1324
1325                 case '\n':
1326                 case '\r':
1327                         line = line + (reverse ? -1 : 1);
1328                         if (line < 0) line = 0;
1329                         break;
1330
1331                 case '8':
1332                 case SKEY_UP:
1333                         line--;
1334                         if (line < 0) line = 0;
1335                         break;
1336
1337                 case '2':
1338                 case SKEY_DOWN:
1339                         line++;
1340                         break;
1341
1342                 case ' ':
1343                         line = line + (reverse ? -rows : rows);
1344                         if (line < 0) line = 0;
1345                         break;
1346
1347                 case SKEY_PGDOWN:
1348                         line = line + rows;
1349                         break;
1350                 }
1351
1352                 if (menu)
1353                 {
1354                         int key = -1;
1355                         if (!(skey & SKEY_MASK) && isalpha(skey))
1356                                 key = skey - 'A';
1357
1358                         if ((key > -1) && hook[key][0])
1359                         {
1360                                 /* Recurse on that file */
1361                                 if (!show_file(creature_ptr, TRUE, hook[key], NULL, 0, mode))
1362                                         skey = 'q';
1363                         }
1364                 }
1365
1366                 if (skey == '|')
1367                 {
1368                         FILE *ffp;
1369                         char buff[1024];
1370                         char xtmp[82];
1371
1372                         strcpy(xtmp, "");
1373
1374                         if (!get_string(_("ファイル名: ", "File name: "), xtmp, 80)) continue;
1375                         my_fclose(fff);
1376                         path_build(buff, sizeof(buff), ANGBAND_DIR_USER, xtmp);
1377
1378                         /* Hack -- Re-Open the file */
1379                         fff = my_fopen(path, "r");
1380
1381                         ffp = my_fopen(buff, "w");
1382
1383                         if (!(fff && ffp))
1384                         {
1385                                 msg_print(_("ファイルを開けません。", "Failed to open file."));
1386                                 skey = ESCAPE;
1387                                 break;
1388                         }
1389
1390                         sprintf(xtmp, "%s: %s", creature_ptr->name, what ? what : caption);
1391                         my_fputs(ffp, xtmp, 80);
1392                         my_fputs(ffp, "\n", 80);
1393
1394                         while (!my_fgets(fff, buff, sizeof(buff)))
1395                                 my_fputs(ffp, buff, 80);
1396                         my_fclose(fff);
1397                         my_fclose(ffp);
1398                         fff = my_fopen(path, "r");
1399                 }
1400
1401                 if ((skey == ESCAPE) || (skey == '<')) break;
1402
1403                 if (skey == KTRL('q')) skey = 'q';
1404
1405                 if (skey == 'q') break;
1406         }
1407
1408         my_fclose(fff);
1409         return (skey != 'q');
1410 }
1411
1412
1413 /*!
1414  * @brief ヘルプを表示するコマンドのメインルーチン
1415  * Peruse the On-Line-Help
1416  * @param creature_ptr プレーヤーへの参照ポインタ
1417  * @return なし
1418  * @details
1419  */
1420 void do_cmd_help(player_type *creature_ptr)
1421 {
1422         screen_save();
1423         (void)show_file(creature_ptr, TRUE, _("jhelp.hlp", "help.hlp"), NULL, 0, 0);
1424         screen_load();
1425 }
1426
1427
1428 /*!
1429  * @brief プレイヤーの名前をチェックして修正する
1430  * Process the player name.
1431  * @param player_ptr プレーヤーへの参照ポインタ
1432  * @param sf セーブファイル名に合わせた修正を行うならばTRUE
1433  * @return なし
1434  * @details
1435  * Extract a clean "base name".
1436  * Build the savefile name if needed.
1437  */
1438 void process_player_name(player_type *creature_ptr, bool sf)
1439 {
1440         char old_player_base[32] = "";
1441         if (current_world_ptr->character_generated)
1442                 strcpy(old_player_base, creature_ptr->base_name);
1443
1444         for (int i = 0; creature_ptr->name[i]; i++)
1445         {
1446 #ifdef JP
1447                 if (iskanji(creature_ptr->name[i]))
1448                 {
1449                         i++;
1450                         continue;
1451                 }
1452
1453                 if (iscntrl((unsigned char)creature_ptr->name[i]))
1454 #else
1455                 if (iscntrl(creature_ptr->name[i]))
1456 #endif
1457                 {
1458                         quit_fmt(_("'%s' という名前は不正なコントロールコードを含んでいます。", "The name '%s' contains control chars!"), creature_ptr->name);
1459                 }
1460         }
1461
1462         int k = 0;
1463         for (int i = 0; creature_ptr->name[i]; i++)
1464         {
1465 #ifdef JP
1466                 unsigned char c = creature_ptr->name[i];
1467 #else
1468                 char c = creature_ptr->name[i];
1469 #endif
1470
1471 #ifdef JP
1472                 if (iskanji(c)) {
1473                         if (k + 2 >= sizeof(creature_ptr->base_name) || !creature_ptr->name[i + 1])
1474                                 break;
1475
1476                         creature_ptr->base_name[k++] = c;
1477                         i++;
1478                         creature_ptr->base_name[k++] = creature_ptr->name[i];
1479                 }
1480 #ifdef SJIS
1481                 else if (iskana(c)) creature_ptr->base_name[k++] = c;
1482 #endif
1483                 else
1484 #endif
1485                         if (!strncmp(PATH_SEP, creature_ptr->name + i, strlen(PATH_SEP)))
1486                         {
1487                                 creature_ptr->base_name[k++] = '_';
1488                                 i += strlen(PATH_SEP);
1489                         }
1490 #if defined(WINDOWS)
1491                         else if (my_strchr("\"*,/:;<>?\\|", c))
1492                                 creature_ptr->base_name[k++] = '_';
1493 #endif
1494                         else if (isprint(c))
1495                                 creature_ptr->base_name[k++] = c;
1496         }
1497
1498         creature_ptr->base_name[k] = '\0';
1499         if (!creature_ptr->base_name[0])
1500                 strcpy(creature_ptr->base_name, "PLAYER");
1501
1502 #ifdef SAVEFILE_MUTABLE
1503         sf = TRUE;
1504 #endif
1505         if (!savefile_base[0] && savefile[0])
1506         {
1507                 concptr s = savefile;
1508                 while (TRUE)
1509                 {
1510                         concptr t;
1511                         t = my_strstr(s, PATH_SEP);
1512                         if (!t)
1513                                 break;
1514                         s = t + 1;
1515                 }
1516
1517                 strcpy(savefile_base, s);
1518         }
1519
1520         if (!savefile_base[0] || !savefile[0])
1521                 sf = TRUE;
1522
1523         if (sf)
1524         {
1525                 char temp[128];
1526                 strcpy(savefile_base, creature_ptr->base_name);
1527
1528 #ifdef SAVEFILE_USE_UID
1529                 /* Rename the savefile, using the creature_ptr->player_uid and creature_ptr->base_name */
1530                 (void)sprintf(temp, "%d.%s", creature_ptr->player_uid, creature_ptr->base_name);
1531 #else
1532                 /* Rename the savefile, using the creature_ptr->base_name */
1533                 (void)sprintf(temp, "%s", creature_ptr->base_name);
1534 #endif
1535                 path_build(savefile, sizeof(savefile), ANGBAND_DIR_SAVE, temp);
1536         }
1537
1538         if (current_world_ptr->character_generated && !streq(old_player_base, creature_ptr->base_name))
1539         {
1540                 autopick_load_pref(creature_ptr, FALSE);
1541         }
1542 }
1543
1544
1545 /*!
1546  * @brief プレイヤーの名前を変更するコマンドのメインルーチン
1547  * Gets a name for the character, reacting to name changes.
1548  * @param creature_ptr プレーヤーへの参照ポインタ
1549  * @return なし
1550  * @details
1551  * <pre>
1552  * Assumes that "display_player()" has just been called
1553  * Perhaps we should NOT ask for a name (at "birth()") on
1554  * Unix machines?  XXX XXX
1555  * What a horrible name for a global function.
1556  * </pre>
1557  */
1558 void get_name(player_type *creature_ptr)
1559 {
1560         char tmp[64];
1561         strcpy(tmp, creature_ptr->name);
1562
1563         if (get_string(_("キャラクターの名前を入力して下さい: ", "Enter a name for your character: "), tmp, 15))
1564         {
1565                 strcpy(creature_ptr->name, tmp);
1566         }
1567
1568         if (strlen(creature_ptr->name) == 0)
1569         {
1570                 strcpy(creature_ptr->name, "PLAYER");
1571         }
1572
1573         strcpy(tmp, ap_ptr->title);
1574 #ifdef JP
1575         if (ap_ptr->no == 1)
1576                 strcat(tmp, "の");
1577 #else
1578         strcat(tmp, " ");
1579 #endif
1580         strcat(tmp, creature_ptr->name);
1581
1582         Term_erase(34, 1, 255);
1583         c_put_str(TERM_L_BLUE, tmp, 1, 34);
1584         clear_from(22);
1585 }
1586
1587
1588 /*!
1589  * @brief セーブするコマンドのメインルーチン
1590  * Save the game
1591  * @param creature_ptr プレーヤーへの参照ポインタ
1592  * @param is_autosave オートセーブ中の処理ならばTRUE
1593  * @return なし
1594  * @details
1595  */
1596 void do_cmd_save_game(player_type *creature_ptr, int is_autosave)
1597 {
1598         if (is_autosave)
1599         {
1600                 msg_print(_("自動セーブ中", "Autosaving the game..."));
1601         }
1602         else
1603         {
1604                 disturb(creature_ptr, TRUE, TRUE);
1605         }
1606
1607         msg_print(NULL);
1608         handle_stuff(creature_ptr);
1609         prt(_("ゲームをセーブしています...", "Saving game..."), 0, 0);
1610         Term_fresh();
1611         (void)strcpy(creature_ptr->died_from, _("(セーブ)", "(saved)"));
1612         signals_ignore_tstp();
1613         if (save_player(creature_ptr))
1614         {
1615                 prt(_("ゲームをセーブしています... 終了", "Saving game... done."), 0, 0);
1616         }
1617         else
1618         {
1619                 prt(_("ゲームをセーブしています... 失敗!", "Saving game... failed!"), 0, 0);
1620         }
1621
1622         signals_handle_tstp();
1623         Term_fresh();
1624         (void)strcpy(creature_ptr->died_from, _("(元気に生きている)", "(alive and well)"));
1625         current_world_ptr->is_loading_now = FALSE;
1626         update_creature(creature_ptr);
1627         mproc_init(creature_ptr->current_floor_ptr);
1628         current_world_ptr->is_loading_now = TRUE;
1629 }
1630
1631
1632 /*!
1633  * @brief セーブ後にゲーム中断フラグを立てる/
1634  * Save the game and exit
1635  * @return なし
1636  * @details
1637  */
1638 void do_cmd_save_and_exit(player_type *creature_ptr)
1639 {
1640         creature_ptr->playing = FALSE;
1641         creature_ptr->leaving = TRUE;
1642         exe_write_diary(creature_ptr, DIARY_GAMESTART, 0, _("----ゲーム中断----", "---- Save and Exit Game ----"));
1643 }
1644
1645
1646 /*!
1647  * @brief 異常発生時のゲーム緊急終了処理 /
1648  * Handle abrupt death of the visual system
1649  * @param creature_ptr プレーヤーへの参照ポインタ
1650  * @return なし
1651  * @details
1652  * This routine is called only in very rare situations, and only
1653  * by certain visual systems, when they experience fatal errors.
1654  */
1655 void exit_game_panic(player_type *creature_ptr)
1656 {
1657         if (!current_world_ptr->character_generated || current_world_ptr->character_saved)
1658                 quit(_("緊急事態", "panic"));
1659         msg_flag = FALSE;
1660
1661         prt("", 0, 0);
1662         disturb(creature_ptr, TRUE, TRUE);
1663         if (creature_ptr->chp < 0) creature_ptr->is_dead = FALSE;
1664
1665         creature_ptr->panic_save = 1;
1666         signals_ignore_tstp();
1667         (void)strcpy(creature_ptr->died_from, _("(緊急セーブ)", "(panic save)"));
1668         if (!save_player(creature_ptr)) quit(_("緊急セーブ失敗!", "panic save failed!"));
1669         quit(_("緊急セーブ成功!", "panic save succeeded!"));
1670 }
1671
1672
1673 /*!
1674  * @brief ファイルからランダムに行を一つ取得する /
1675  * Get a random line from a file
1676  * @param file_name ファイル名
1677  * @param entry 特定条件時のN:タグヘッダID
1678  * @param output 出力先の文字列参照ポインタ
1679  * @return エラーコード
1680  * @details
1681  * <pre>
1682  * Based on the monster speech patch by Matt Graham,
1683  * </pre>
1684  */
1685 errr get_rnd_line(concptr file_name, int entry, char *output)
1686 {
1687         char buf[1024];
1688         path_build(buf, sizeof(buf), ANGBAND_DIR_FILE, file_name);
1689         FILE *fp;
1690         fp = my_fopen(buf, "r");
1691         if (!fp) return -1;
1692
1693         int test;
1694         int line_num = 0;
1695         while (TRUE)
1696         {
1697                 if (my_fgets(fp, buf, sizeof(buf)) != 0)
1698                 {
1699                         my_fclose(fp);
1700                         return -1;
1701                 }
1702
1703                 line_num++;
1704                 if ((buf[0] != 'N') || (buf[1] != ':')) continue;
1705
1706                 if (buf[2] == '*')
1707                 {
1708                         break;
1709                 }
1710                 else if (buf[2] == 'M')
1711                 {
1712                         if (r_info[entry].flags1 & RF1_MALE) break;
1713                 }
1714                 else if (buf[2] == 'F')
1715                 {
1716                         if (r_info[entry].flags1 & RF1_FEMALE) break;
1717                 }
1718                 else if (sscanf(&(buf[2]), "%d", &test) != EOF)
1719                 {
1720                         if (test == entry) break;
1721                 }
1722
1723                 msg_format("Error in line %d of %s!", line_num, file_name);
1724                 my_fclose(fp);
1725                 return -1;
1726         }
1727
1728         int counter;
1729         for (counter = 0; ; counter++)
1730         {
1731                 while (TRUE)
1732                 {
1733                         test = my_fgets(fp, buf, sizeof(buf));
1734                         if (!test)
1735                         {
1736                                 /* Ignore lines starting with 'N:' */
1737                                 if ((buf[0] == 'N') && (buf[1] == ':')) continue;
1738
1739                                 if (buf[0] != '#') break;
1740                         }
1741                         else break;
1742                 }
1743
1744                 if (!buf[0]) break;
1745
1746                 if (one_in_(counter + 1)) strcpy(output, buf);
1747         }
1748
1749         my_fclose(fp);
1750         return counter ? 0 : -1;
1751 }
1752
1753
1754 #ifdef JP
1755 /*!
1756  * @brief ファイルからランダムに行を一つ取得する(日本語文字列のみ) /
1757  * @param file_name ファイル名
1758  * @param entry 特定条件時のN:タグヘッダID
1759  * @param output 出力先の文字列参照ポインタ
1760  * @param count 試行回数
1761  * @return エラーコード
1762  * @details
1763  */
1764 errr get_rnd_line_jonly(concptr file_name, int entry, char *output, int count)
1765 {
1766         errr result = 1;
1767         for (int i = 0; i < count; i++)
1768         {
1769                 result = get_rnd_line(file_name, entry, output);
1770                 if (result) break;
1771                 bool kanji = FALSE;
1772                 for (int j = 0; output[j]; j++) kanji |= iskanji(output[j]);
1773                 if (kanji) break;
1774         }
1775
1776         return result;
1777 }
1778 #endif
1779
1780
1781 /*!
1782  * @brief 自動拾いファイルを読み込む /
1783  * @param creature_ptr プレーヤーへの参照ポインタ
1784  * @param name ファイル名
1785  * @details
1786  */
1787 errr process_autopick_file(player_type *creature_ptr, concptr name)
1788 {
1789         char buf[1024];
1790         path_build(buf, sizeof(buf), ANGBAND_DIR_USER, name);
1791         errr err = process_pref_file_aux(creature_ptr, buf, PREF_TYPE_AUTOPICK);
1792         return err;
1793 }
1794
1795
1796 /*!
1797  * @brief プレイヤーの生い立ちファイルを読み込む /
1798  * Process file for player's history editor.
1799  * @param creature_ptr プレーヤーへの参照ポインタ
1800  * @param name ファイル名
1801  * @return エラーコード
1802  * @details
1803  */
1804 errr process_histpref_file(player_type *creature_ptr, concptr name)
1805 {
1806         bool old_character_xtra = current_world_ptr->character_xtra;
1807         char buf[1024];
1808         path_build(buf, sizeof(buf), ANGBAND_DIR_USER, name);
1809
1810         /* Hack -- prevent modification birth options in this file */
1811         current_world_ptr->character_xtra = TRUE;
1812         errr err = process_pref_file_aux(creature_ptr, buf, PREF_TYPE_HISTPREF);
1813         current_world_ptr->character_xtra = old_character_xtra;
1814         return err;
1815 }
1816
1817
1818 /*!
1819  * @brief ファイル位置をシーク /
1820  * @param creature_ptr プレーヤーへの参照ポインタ
1821  * @param fd ファイルディスクリプタ
1822  * @param where ファイルバイト位置
1823  * @param flag FALSEならば現ファイルを超えた位置へシーク時エラー、TRUEなら足りない間を0で埋め尽くす
1824  * @return エラーコード
1825  * @details
1826  */
1827 static errr counts_seek(player_type *creature_ptr, int fd, u32b where, bool flag)
1828 {
1829         char temp1[128], temp2[128];
1830 #ifdef SAVEFILE_USE_UID
1831         (void)sprintf(temp1, "%d.%s.%d%d%d", creature_ptr->player_uid, savefile_base, creature_ptr->pclass, creature_ptr->pseikaku, creature_ptr->age);
1832 #else
1833         (void)sprintf(temp1, "%s.%d%d%d", savefile_base, creature_ptr->pclass, creature_ptr->pseikaku, creature_ptr->age);
1834 #endif
1835         for (int i = 0; temp1[i]; i++)
1836                 temp1[i] ^= (i + 1) * 63;
1837
1838         int seekpoint = 0;
1839         u32b zero_header[3] = { 0L, 0L, 0L };
1840         while (TRUE)
1841         {
1842                 if (fd_seek(fd, seekpoint + 3 * sizeof(u32b)))
1843                         return 1;
1844                 if (fd_read(fd, (char*)(temp2), sizeof(temp2)))
1845                 {
1846                         if (!flag)
1847                                 return 1;
1848                         /* add new name */
1849                         fd_seek(fd, seekpoint);
1850                         fd_write(fd, (char*)zero_header, 3 * sizeof(u32b));
1851                         fd_write(fd, (char*)(temp1), sizeof(temp1));
1852                         break;
1853                 }
1854
1855                 if (strcmp(temp1, temp2) == 0)
1856                         break;
1857
1858                 seekpoint += 128 + 3 * sizeof(u32b);
1859         }
1860
1861         return fd_seek(fd, seekpoint + where * sizeof(u32b));
1862 }
1863
1864
1865 /*!
1866  * @brief ファイル位置を読み込む
1867  * @param creature_ptr プレーヤーへの参照ポインタ
1868  * @param where ファイルバイト位置
1869  * @return エラーコード
1870  * @details
1871  */
1872 u32b counts_read(player_type *creature_ptr, int where)
1873 {
1874         char buf[1024];
1875         path_build(buf, sizeof(buf), ANGBAND_DIR_DATA, _("z_info_j.raw", "z_info.raw"));
1876         int fd = fd_open(buf, O_RDONLY);
1877
1878         u32b count = 0;
1879         if (counts_seek(creature_ptr, fd, where, FALSE) ||
1880                 fd_read(fd, (char*)(&count), sizeof(u32b)))
1881                 count = 0;
1882
1883         (void)fd_close(fd);
1884
1885         return count;
1886 }
1887
1888
1889 /*!
1890  * @brief ファイル位置に書き込む /
1891  * @param creature_ptr プレーヤーへの参照ポインタ
1892  * @param where ファイルバイト位置
1893  * @param count 書き込む値
1894  * @return エラーコード
1895  * @details
1896  */
1897 errr counts_write(player_type *creature_ptr, int where, u32b count)
1898 {
1899         char buf[1024];
1900         path_build(buf, sizeof(buf), ANGBAND_DIR_DATA, _("z_info_j.raw", "z_info.raw"));
1901
1902         safe_setuid_grab();
1903         int fd = fd_open(buf, O_RDWR);
1904         safe_setuid_drop();
1905         if (fd < 0)
1906         {
1907                 FILE_TYPE(FILE_TYPE_DATA);
1908                 safe_setuid_grab();
1909                 fd = fd_make(buf, 0644);
1910                 safe_setuid_drop();
1911         }
1912
1913         safe_setuid_grab();
1914         errr err = fd_lock(fd, F_WRLCK);
1915         safe_setuid_drop();
1916         if (err) return 1;
1917
1918         counts_seek(creature_ptr, fd, where, TRUE);
1919         fd_write(fd, (char*)(&count), sizeof(u32b));
1920         safe_setuid_grab();
1921         err = fd_lock(fd, F_UNLCK);
1922         safe_setuid_drop();
1923
1924         if (err) return 1;
1925
1926         (void)fd_close(fd);
1927         return 0;
1928 }
1929
1930
1931 /*!
1932  * @brief 墓のアスキーアートテンプレを読み込む
1933  * @param buf テンプレへのバッファ
1934  * @param buf_size バッファの長さ
1935  * @return なし
1936  */
1937 void read_dead_file(char *buf, size_t buf_size)
1938 {
1939         path_build(buf, buf_size, ANGBAND_DIR_FILE, _("dead_j.txt", "dead.txt"));
1940
1941         FILE *fp;
1942         fp = my_fopen(buf, "r");
1943         if (!fp) return;
1944
1945         int i = 0;
1946         while (my_fgets(fp, buf, buf_size) == 0)
1947         {
1948                 put_str(buf, i++, 0);
1949         }
1950
1951         my_fclose(fp);
1952 }