OSDN Git Service

Merge pull request #2013 from sikabane-works/release/3.0.0Alpha51
[hengbandforosx/hengbandosx.git] / src / core / show-file.cpp
1 #include "core/show-file.h"
2 #include "core/asking-player.h"
3 #include "io/files-util.h"
4 #include "io/input-key-acceptor.h"
5 #include "main/sound-of-music.h"
6 #include "system/angband-version.h"
7 #include "system/player-type-definition.h"
8 #include "term/gameterm.h"
9 #include "term/screen-processor.h"
10 #include "term/term-color-types.h"
11 #include "util/angband-files.h"
12 #include "util/int-char-converter.h"
13 #include "util/string-processor.h"
14 #include "view/display-messages.h"
15
16 /*!
17  * @brief ファイル内容の一行をコンソールに出力する
18  * Display single line of on-line help file
19  * @param str 出力する文字列
20  * @param cy コンソールの行
21  * @param shower 確認中
22  * @details
23  * <pre>
24  * You can insert some special color tag to change text color.
25  * Such as...
26  * WHITETEXT [[[[y|SOME TEXT WHICH IS DISPLAYED IN YELLOW| WHITETEXT
27  * A colored segment is between "[[[[y|" and the last "|".
28  * You can use any single character in place of the "|".
29  * </pre>
30  * @todo 表示とそれ以外を分割する
31  */
32 static void show_file_aux_line(concptr str, int cy, concptr shower)
33 {
34     char lcstr[1024];
35     concptr ptr;
36     byte textcolor = TERM_WHITE;
37     byte focuscolor = TERM_YELLOW;
38
39     if (shower) {
40         strcpy(lcstr, str);
41         str_tolower(lcstr);
42
43         ptr = angband_strstr(lcstr, shower);
44         textcolor = (ptr == nullptr) ? TERM_L_DARK : TERM_WHITE;
45     }
46
47     int cx = 0;
48     term_gotoxy(cx, cy);
49
50     static const char tag_str[] = "[[[[";
51     byte color = textcolor;
52     char in_tag = '\0';
53     for (int i = 0; str[i];) {
54         int len = strlen(&str[i]);
55         int showercol = len + 1;
56         int bracketcol = len + 1;
57         int endcol = len;
58         if (shower) {
59             ptr = angband_strstr(&lcstr[i], shower);
60             if (ptr)
61                 showercol = ptr - &lcstr[i];
62         }
63
64         ptr = in_tag ? angband_strchr(&str[i], in_tag) : angband_strstr(&str[i], tag_str);
65         if (ptr)
66             bracketcol = ptr - &str[i];
67         if (bracketcol < endcol)
68             endcol = bracketcol;
69         if (showercol < endcol)
70             endcol = showercol;
71
72         term_addstr(endcol, color, &str[i]);
73         cx += endcol;
74         i += endcol;
75
76         if (shower && endcol == showercol) {
77             int showerlen = strlen(shower);
78             term_addstr(showerlen, focuscolor, &str[i]);
79             cx += showerlen;
80             i += showerlen;
81             continue;
82         }
83
84         if (endcol != bracketcol)
85             continue;
86
87         if (in_tag) {
88             i++;
89             in_tag = '\0';
90             color = textcolor;
91             continue;
92         }
93
94         i += sizeof(tag_str) - 1;
95         color = color_char_to_attr(str[i]);
96         if (color == 255 || str[i + 1] == '\0') {
97             color = textcolor;
98             term_addstr(-1, color, tag_str);
99             cx += sizeof(tag_str) - 1;
100             continue;
101         }
102
103         i++;
104         in_tag = str[i];
105         i++;
106     }
107
108     term_erase(cx, cy, 255);
109 }
110
111 /*!
112  * @brief ファイル内容をコンソールに出力する
113  * Recursive file perusal.
114  * @param player_ptr プレイヤーへの参照ポインタ
115  * @param show_version TRUEならばコンソール上にゲームのバージョンを表示する
116  * @param name ファイル名の文字列
117  * @param what 内容キャプションの文字列
118  * @param line 表示の現在行
119  * @param mode オプション
120  * @details
121  * <pre>
122  * Process various special text in the input file, including
123  * the "menu" structures used by the "help file" system.
124  * Return FALSE on 'q' to exit from a deep, otherwise TRUE.
125  * </pre>
126  * @todo 表示とそれ以外を分割する
127  */
128 bool show_file(PlayerType *player_ptr, bool show_version, concptr name, concptr what, int line, BIT_FLAGS mode)
129 {
130     int wid, hgt;
131     term_get_size(&wid, &hgt);
132
133     char finder_str[81];
134     strcpy(finder_str, "");
135
136     char shower_str[81];
137     strcpy(shower_str, "");
138
139     char caption[1024 + 256];
140     strcpy(caption, "");
141
142     char hook[68][32];
143     for (int i = 0; i < 68; i++) {
144         hook[i][0] = '\0';
145     }
146
147     char filename[1024];
148     strcpy(filename, name);
149     int n = strlen(filename);
150
151     concptr tag = nullptr;
152     for (int i = 0; i < n; i++) {
153         if (filename[i] == '#') {
154             filename[i] = '\0';
155             tag = filename + i + 1;
156             break;
157         }
158     }
159
160     name = filename;
161     FILE *fff = nullptr;
162     char path[1024];
163     if (what) {
164         strcpy(caption, what);
165         strcpy(path, name);
166         fff = angband_fopen(path, "r");
167     }
168
169     if (!fff) {
170         sprintf(caption, _("ヘルプ・ファイル'%s'", "Help file '%s'"), name);
171         path_build(path, sizeof(path), ANGBAND_DIR_HELP, name);
172         fff = angband_fopen(path, "r");
173     }
174
175     if (!fff) {
176         sprintf(caption, _("スポイラー・ファイル'%s'", "Info file '%s'"), name);
177         path_build(path, sizeof(path), ANGBAND_DIR_INFO, name);
178         fff = angband_fopen(path, "r");
179     }
180
181     if (!fff) {
182         path_build(path, sizeof(path), ANGBAND_DIR, name);
183
184         for (int i = 0; path[i]; i++)
185             if ('\\' == path[i])
186                 path[i] = PATH_SEP[0];
187
188         sprintf(caption, _("スポイラー・ファイル'%s'", "Info file '%s'"), name);
189         fff = angband_fopen(path, "r");
190     }
191
192     if (!fff) {
193         msg_format(_("'%s'をオープンできません。", "Cannot open '%s'."), name);
194         msg_print(nullptr);
195
196         return true;
197     }
198
199     int skey;
200     int next = 0;
201     int size = 0;
202     int back = 0;
203     bool menu = false;
204     char buf[1024];
205     bool reverse = (line < 0);
206     while (true) {
207         char *str = buf;
208         if (angband_fgets(fff, buf, sizeof(buf)))
209             break;
210         if (!prefix(str, "***** ")) {
211             next++;
212             continue;
213         }
214
215         if ((str[6] == '[') && isalpha(str[7])) {
216             int k = str[7] - 'A';
217             menu = true;
218             if ((str[8] == ']') && (str[9] == ' ')) {
219                 memcpy(hook[k], str + 10, 31);
220                 hook[k][31] = '\0';
221             }
222
223             continue;
224         }
225
226         if (str[6] != '<')
227             continue;
228
229         size_t len = strlen(str);
230         if (str[len - 1] == '>') {
231             str[len - 1] = '\0';
232             if (tag && streq(str + 7, tag))
233                 line = next;
234         }
235     }
236
237     size = next;
238     int rows = hgt - 4;
239     if (line == -1)
240         line = ((size - 1) / rows) * rows;
241
242     term_clear();
243
244     concptr find = nullptr;
245     concptr shower = nullptr;
246     while (true) {
247         if (line >= size - rows)
248               line = size - rows;
249         if (line < 0)
250             line = 0;
251
252         if (next > line) {
253             angband_fclose(fff);
254             fff = angband_fopen(path, "r");
255             if (!fff)
256                 return false;
257
258             next = 0;
259         }
260
261         while (next < line) {
262             if (angband_fgets(fff, buf, sizeof(buf)))
263                 break;
264             if (prefix(buf, "***** "))
265                 continue;
266             next++;
267         }
268
269         int row_count;
270         for (row_count = 0; row_count < rows;) {
271             concptr str = buf;
272             if (!row_count)
273                 line = next;
274             if (angband_fgets(fff, buf, sizeof(buf)))
275                 break;
276             if (prefix(buf, "***** "))
277                 continue;
278             next++;
279             if (find && !row_count) {
280                 char lc_buf[1024];
281                 strcpy(lc_buf, str);
282                 str_tolower(lc_buf);
283                 if (!angband_strstr(lc_buf, find))
284                     continue;
285             }
286
287             find = nullptr;
288             show_file_aux_line(str, row_count + 2, shower);
289             row_count++;
290         }
291
292         while (row_count < rows) {
293             term_erase(0, row_count + 2, 255);
294             row_count++;
295         }
296
297         if (find) {
298             bell();
299             line = back;
300             find = nullptr;
301             continue;
302         }
303
304         prt(format(_("[変愚蛮怒 %d.%d.%d, %s, %d/%d]", "[Hengband %d.%d.%d, %s, Line %d/%d]"), FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH, caption,
305                 line, size),
306             0, 0);
307
308         if (show_version) {
309             char title[127];
310             put_version(title);
311             prt(format("[%s]", title), 0, 0);
312         } else {
313             prt(format(_("[%s, %d/%d]", "[%s, Line %d/%d]"), caption, line, size), 0, 0);
314         }
315
316         if (size <= rows) {
317             prt(_("[キー:(?)ヘルプ (ESC)終了]", "[Press ESC to exit.]"), hgt - 1, 0);
318         } else {
319 #ifdef JP
320             if (reverse)
321                 prt("[キー:(RET/スペース)↑ (-)↓ (?)ヘルプ (ESC)終了]", hgt - 1, 0);
322             else
323                 prt("[キー:(RET/スペース)↓ (-)↑ (?)ヘルプ (ESC)終了]", hgt - 1, 0);
324 #else
325             prt("[Press Return, Space, -, =, /, |, or ESC to exit.]", hgt - 1, 0);
326 #endif
327         }
328
329         skey = inkey_special(true);
330         switch (skey) {
331         case '?':
332             if (strcmp(name, _("jhelpinfo.txt", "helpinfo.txt")) != 0)
333                 show_file(player_ptr, true, _("jhelpinfo.txt", "helpinfo.txt"), nullptr, 0, mode);
334             break;
335         case '=':
336             prt(_("強調: ", "Show: "), hgt - 1, 0);
337
338             char back_str[81];
339             strcpy(back_str, shower_str);
340             if (askfor(shower_str, 80)) {
341                 if (shower_str[0]) {
342                     str_tolower(shower_str);
343                     shower = shower_str;
344                 } else
345                     shower = nullptr;
346             } else
347                 strcpy(shower_str, back_str);
348             break;
349
350         case '/':
351         case KTRL('s'):
352             prt(_("検索: ", "Find: "), hgt - 1, 0);
353             strcpy(back_str, finder_str);
354             if (askfor(finder_str, 80)) {
355                 if (finder_str[0]) {
356                     find = finder_str;
357                     back = line;
358                     line = line + 1;
359                     str_tolower(finder_str);
360                     shower = finder_str;
361                 } else
362                     shower = nullptr;
363             } else
364                 strcpy(finder_str, back_str);
365             break;
366
367         case '#': {
368             char tmp[81];
369             prt(_("行: ", "Goto Line: "), hgt - 1, 0);
370             strcpy(tmp, "0");
371
372             if (askfor(tmp, 80))
373                 line = atoi(tmp);
374             break;
375         }
376
377         case SKEY_TOP:
378             line = 0;
379             break;
380
381         case SKEY_BOTTOM:
382             line = ((size - 1) / rows) * rows;
383             break;
384
385         case '%': {
386             char tmp[81];
387             prt(_("ファイル・ネーム: ", "Goto File: "), hgt - 1, 0);
388             strcpy(tmp, _("jhelp.hlp", "help.hlp"));
389
390             if (askfor(tmp, 80)) {
391                 if (!show_file(player_ptr, true, tmp, nullptr, 0, mode))
392                     skey = 'q';
393             }
394
395             break;
396         }
397
398         case '-':
399             line = line + (reverse ? rows : -rows);
400             if (line < 0)
401                 line = 0;
402             break;
403
404         case SKEY_PGUP:
405             line = line - rows;
406             if (line < 0)
407                 line = 0;
408             break;
409
410         case '\n':
411         case '\r':
412             line = line + (reverse ? -1 : 1);
413             if (line < 0)
414                 line = 0;
415             break;
416
417         case '8':
418         case SKEY_UP:
419             line--;
420             if (line < 0)
421                 line = 0;
422             break;
423
424         case '2':
425         case SKEY_DOWN:
426             line++;
427             break;
428
429         case ' ':
430             line = line + (reverse ? -rows : rows);
431             if (line < 0)
432                 line = 0;
433             break;
434
435         case SKEY_PGDOWN:
436             line = line + rows;
437             break;
438         }
439
440         if (menu) {
441             int key = -1;
442             if (!(skey & SKEY_MASK) && isalpha(skey))
443                 key = skey - 'A';
444
445             if ((key > -1) && hook[key][0]) {
446                 /* Recurse on that file */
447                 if (!show_file(player_ptr, true, hook[key], nullptr, 0, mode))
448                     skey = 'q';
449             }
450         }
451
452         if (skey == '|') {
453             FILE *ffp;
454             char buff[1024];
455             char xtmp[sizeof(caption) + 128];
456
457             strcpy(xtmp, "");
458
459             if (!get_string(_("ファイル名: ", "File name: "), xtmp, 80))
460                 continue;
461             angband_fclose(fff);
462             path_build(buff, sizeof(buff), ANGBAND_DIR_USER, xtmp);
463
464             /* Hack -- Re-Open the file */
465             fff = angband_fopen(path, "r");
466
467             ffp = angband_fopen(buff, "w");
468
469             if (!(fff && ffp)) {
470                 msg_print(_("ファイルを開けません。", "Failed to open file."));
471                 skey = ESCAPE;
472                 break;
473             }
474
475             sprintf(xtmp, "%s: %s", player_ptr->name, what ? what : caption);
476             angband_fputs(ffp, xtmp, 80);
477             angband_fputs(ffp, "\n", 80);
478
479             while (!angband_fgets(fff, buff, sizeof(buff)))
480                 angband_fputs(ffp, buff, 80);
481             angband_fclose(fff);
482             angband_fclose(ffp);
483             fff = angband_fopen(path, "r");
484         }
485
486         if ((skey == ESCAPE) || (skey == '<'))
487             break;
488
489         if (skey == KTRL('q'))
490             skey = 'q';
491
492         if (skey == 'q')
493             break;
494     }
495
496     angband_fclose(fff);
497     return (skey != 'q');
498 }
499
500 /*
501  * Convert string to lower case
502  */
503 void str_tolower(char *str)
504 {
505     for (; *str; str++) {
506 #ifdef JP
507         if (iskanji(*str)) {
508             str++;
509             continue;
510         }
511 #endif
512         *str = (char)tolower(*str);
513     }
514 }