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-exceptions.h"
7 #include "system/angband-version.h"
8 #include "system/player-type-definition.h"
9 #include "term/gameterm.h"
10 #include "term/screen-processor.h"
11 #include "term/term-color-types.h"
12 #include "term/z-form.h"
13 #include "util/angband-files.h"
14 #include "util/int-char-converter.h"
15 #include "util/string-processor.h"
16 #include "view/display-messages.h"
19 #include <string_view>
22 * @brief ファイル内容の一行をコンソールに出力する
23 * Display single line of on-line help file
29 * You can insert some special color tag to change text color.
31 * WHITETEXT [[[[y|SOME TEXT WHICH IS DISPLAYED IN YELLOW| WHITETEXT
32 * A colored segment is between "[[[[y|" and the last "|".
33 * You can use any single character in place of the "|".
37 static void show_file_aux_line(std::string_view str, int cy, std::string_view shower)
41 byte textcolor = TERM_WHITE;
42 byte focuscolor = TERM_YELLOW;
44 if (!shower.empty()) {
45 strcpy(lcstr, str.data());
48 ptr = angband_strstr(lcstr, shower);
49 textcolor = (ptr == nullptr) ? TERM_L_DARK : TERM_WHITE;
55 static const char tag_str[] = "[[[[";
56 byte color = textcolor;
58 for (size_t i = 0; i < str.length();) {
59 int len = strlen(&str[i]);
60 int showercol = len + 1;
61 int bracketcol = len + 1;
63 if (!shower.empty()) {
64 ptr = angband_strstr(&lcstr[i], shower);
66 showercol = ptr - &lcstr[i];
70 ptr = in_tag ? angband_strchr(&str[i], in_tag) : angband_strstr(&str[i], tag_str);
72 bracketcol = ptr - &str[i];
74 if (bracketcol < endcol) {
77 if (showercol < endcol) {
81 term_addstr(endcol, color, &str[i]);
85 if (!shower.empty() && (endcol == showercol)) {
86 const auto showerlen = shower.length();
87 term_addstr(showerlen, focuscolor, &str[i]);
93 if (endcol != bracketcol) {
104 i += sizeof(tag_str) - 1;
105 color = color_char_to_attr(str[i]);
106 if (color == 255 || str[i + 1] == '\0') {
108 term_addstr(-1, color, tag_str);
109 cx += sizeof(tag_str) - 1;
122 * @brief ファイル内容をコンソールに出力する
123 * Recursive file perusal.
124 * @param player_ptr プレイヤーへの参照ポインタ
125 * @param show_version TRUEならばコンソール上にゲームのバージョンを表示する
126 * @param name ファイル名の文字列
127 * @param what 内容キャプションの文字列
132 * Process various special text in the input file, including
133 * the "menu" structures used by the "help file" system.
134 * Return FALSE on 'q' to exit from a deep, otherwise TRUE.
138 bool show_file(PlayerType *player_ptr, bool show_version, std::string_view name_with_tag, int initial_line, uint32_t mode, std::string_view what)
140 TermCenteredOffsetSetter tcos(MAIN_TERM_MIN_COLS, std::nullopt);
142 const auto [wid, hgt] = term_get_size();
145 auto stripped_names = str_split(name_with_tag, '#');
146 auto &name = stripped_names[0];
147 auto tag = stripped_names.size() > 1 ? stripped_names[1] : "";
148 std::filesystem::path path_reopen("");
150 std::stringstream caption;
154 fff = angband_fopen(path_reopen, FileOpenMode::READ);
159 caption << _("ヘルプ・ファイル'", "Help file '");
160 caption << name << "'";
161 path_reopen = path_build(ANGBAND_DIR_HELP, name);
162 fff = angband_fopen(path_reopen, FileOpenMode::READ);
167 caption << _("スポイラー・ファイル'", "Info file '");
168 caption << name << "'";
169 path_reopen = path_build(ANGBAND_DIR_INFO, name);
170 fff = angband_fopen(path_reopen, FileOpenMode::READ);
175 path_reopen = path_build(ANGBAND_DIR, name);
176 caption << _("スポイラー・ファイル'", "Info file '");
177 caption << name << "'";
178 fff = angband_fopen(path_reopen, FileOpenMode::READ);
181 const auto open_error_mes = format(_("'%s'をオープンできません。", "Cannot open '%s'."), name.data());
183 THROW_EXCEPTION(std::runtime_error, open_error_mes);
186 const auto caption_str = caption.str();
192 auto reverse = initial_line < 0;
193 auto line = initial_line;
196 if (angband_fgets(fff, buf, sizeof(buf))) {
199 if (!prefix(str, "***** ")) {
204 if ((str[6] == '[') && isalpha(str[7])) {
205 int k = str[7] - 'A';
207 if ((str[8] == ']') && (str[9] == ' ')) {
208 angband_strcpy(hook[k], str + 10, sizeof(hook[k]));
218 size_t len = strlen(str);
219 if (str[len - 1] == '>') {
221 if (!tag.empty() && streq(str + 7, tag)) {
230 line = ((size - 1) / rows) * rows;
236 std::string finder_str;
238 std::string shower_str;
240 if (line >= size - rows) {
249 fff = angband_fopen(path_reopen, FileOpenMode::READ);
251 THROW_EXCEPTION(std::runtime_error, open_error_mes);
257 while (next < line) {
258 if (angband_fgets(fff, buf, sizeof(buf))) {
261 if (prefix(buf, "***** ")) {
268 for (row_count = 0; row_count < rows;) {
273 if (angband_fgets(fff, buf, sizeof(buf))) {
276 if (prefix(buf, "***** ")) {
280 if (!find.empty() && !row_count) {
284 if (!str_find(lc_buf, find)) {
290 show_file_aux_line(str, row_count + 2, shower);
294 while (row_count < rows) {
295 term_erase(0, row_count + 2);
307 constexpr auto title = _("[%s, %s, %d/%d]", "[%s, %s, Line %d/%d]");
308 prt(format(title, get_version().data(), caption_str.data(), line, size), 0, 0);
310 constexpr auto title = _("[%s, %d/%d]", "[%s, Line %d/%d]");
311 prt(format(title, caption_str.data(), line, size), 0, 0);
315 prt(_("[キー:(?)ヘルプ (ESC)終了]", "[Press ESC to exit.]"), hgt - 1, 0);
319 prt("[キー:(RET/スペース)↑ (-)↓ (?)ヘルプ (ESC)終了]", hgt - 1, 0);
321 prt("[キー:(RET/スペース)↓ (-)↑ (?)ヘルプ (ESC)終了]", hgt - 1, 0);
324 prt("[Press Return, Space, -, =, /, |, or ESC to exit.]", hgt - 1, 0);
328 skey = inkey_special(true);
331 if (name != _("jhelpinfo.txt", "helpinfo.txt")) {
332 show_file(player_ptr, true, _("jhelpinfo.txt", "helpinfo.txt"), 0, mode);
337 prt(_("強調: ", "Show: "), hgt - 1, 0);
338 auto ask_result = askfor(80, shower_str);
343 shower_str = *ask_result;
344 if (shower_str.empty()) {
349 str_tolower(shower_str.data());
355 prt(_("検索: ", "Find: "), hgt - 1, 0);
356 auto ask_result = askfor(80, finder_str);
361 finder_str = *ask_result;
362 if (finder_str.empty()) {
370 str_tolower(finder_str.data());
375 prt(_("行: ", "Goto Line: "), hgt - 1, 0);
376 constexpr auto initial_goto = "0";
378 const auto ask_result = askfor(10, initial_goto);
384 line = std::stoi(*ask_result);
386 } catch (std::invalid_argument const &) {
387 prt(_("数値を入力して下さい。", "Please input numeric value."), hgt - 1, 0);
388 } catch (std::out_of_range const &) {
389 prt(_("入力可能な数値の範囲を超えています。", "Input value overflows the maximum number."), hgt - 1, 0);
399 line = ((size - 1) / rows) * rows;
402 prt(_("ファイル・ネーム: ", "Goto File: "), hgt - 1, 0);
403 const auto ask_result = askfor(80, _("jhelp.hlp", "help.hlp"));
408 if (!show_file(player_ptr, true, *ask_result, 0, mode)) {
415 line = line + (reverse ? rows : -rows);
430 line = line + (reverse ? -1 : 1);
449 line = line + (reverse ? -rows : rows);
464 if (!(skey & SKEY_MASK) && isalpha(skey)) {
468 if ((key > -1) && hook[key][0]) {
469 /* Recurse on that file */
470 if (!show_file(player_ptr, true, hook[key], 0, mode)) {
477 const auto xtmp = input_string(_("ファイル名: ", "File name: "), 80);
478 if (!xtmp.has_value()) {
483 fff = angband_fopen(path_reopen, FileOpenMode::READ);
484 const auto &path_xtemp = path_build(ANGBAND_DIR_USER, xtmp.value());
485 auto *ffp = angband_fopen(path_xtemp, FileOpenMode::WRITE);
488 msg_print(_("ファイルを開けません。", "Failed to open file."));
493 fprintf(ffp, "%s: %s\n", player_ptr->name, !what.empty() ? what.data() : caption_str.data());
495 while (!angband_fgets(fff, buff, sizeof(buff))) {
496 angband_fputs(ffp, buff, 80);
500 fff = angband_fopen(path_reopen, FileOpenMode::READ);
503 if ((skey == ESCAPE) || (skey == '<')) {
507 if (skey == KTRL('q')) {
521 * Convert string to lower case
523 void str_tolower(char *str)
525 for (; *str; str++) {
532 *str = (char)tolower(*str);