OSDN Git Service

Merge branch 'develop' into macos-develop
[hengbandforosx/hengbandosx.git] / src / cmd-io / cmd-process-screen.cpp
1 /*!
2  * @brief 記念撮影のセーブとロード
3  * @date 2020/04/22
4  * @Author Hourier
5  */
6
7 #include "cmd-io/cmd-process-screen.h"
8 #include "cmd-visual/cmd-draw.h"
9 #include "core/asking-player.h"
10 #include "core/stuff-handler.h"
11 #include "core/visuals-reseter.h"
12 #include "game-option/special-options.h"
13 #include "io/files-util.h"
14 #include "io/input-key-acceptor.h"
15 #include "system/angband-exceptions.h"
16 #include "system/player-type-definition.h"
17 #include "system/redrawing-flags-updater.h"
18 #include "term/gameterm.h"
19 #include "term/screen-processor.h"
20 #include "term/term-color-types.h"
21 #include "util/angband-files.h"
22 #include "view/display-messages.h"
23 #include <optional>
24 #include <string>
25 #include <string_view>
26
27 // Encode the screen colors
28 static char hack[17] = "dwsorgbuDWvyRGBU";
29
30 static concptr tags[4] = {
31     "HEADER_START:",
32     "HEADER_END:",
33     "FOOTER_START:",
34     "FOOTER_END:",
35 };
36 static concptr html_head[3] = {
37     "<html>\n<body text=\"#ffffff\" bgcolor=\"#000000\">\n",
38     "<pre>",
39     0,
40 };
41 static concptr html_foot[3] = {
42     "</pre>\n",
43     "</body>\n</html>\n",
44     0,
45 };
46
47 /*!
48  * @brief 一時ファイルを読み込み、ファイルに書き出す
49  * @param fff ファイルへの参照ポインタ
50  * @param tempfff 一時ファイルへの参照ポインタ
51  * @param num_tag タグ番号
52  * @todo io/ 以下に移したいところだが、このファイルの行数も大したことがないので一旦保留
53  */
54 static void read_temporary_file(FILE *fff, FILE *tmpfff, int num_tag)
55 {
56     bool is_first_line = true;
57     int next_tag = num_tag + 1;
58     char buf[2048]{};
59     while (!angband_fgets(tmpfff, buf, sizeof(buf))) {
60         if (is_first_line) {
61             if (strncmp(buf, tags[num_tag], strlen(tags[num_tag])) == 0) {
62                 is_first_line = false;
63             }
64
65             continue;
66         }
67
68         if (strncmp(buf, tags[next_tag], strlen(tags[next_tag])) == 0) {
69             break;
70         }
71
72         fprintf(fff, "%s\n", buf);
73     }
74 }
75
76 /*!
77  * @brief 記念撮影を1行ダンプする
78  * @param wid 幅
79  * @param y 現在の行位置
80  * @param fff 記念撮影ファイルへの参照ポインタ
81  */
82 static void screen_dump_one_line(int wid, int y, FILE *fff)
83 {
84     TERM_COLOR a = 0, old_a = 0;
85     char c = ' ';
86     for (TERM_LEN x = 0; x < wid - 1; x++) {
87         concptr cc = nullptr;
88         (void)(term_what(x, y, &a, &c));
89         switch (c) {
90         case '&':
91             cc = "&amp;";
92             break;
93         case '<':
94             cc = "&lt;";
95             break;
96         case '>':
97             cc = "&gt;";
98             break;
99 #ifdef WINDOWS
100         case 0x1f:
101             c = '.';
102             break;
103         case 0x7f:
104             c = (a == 0x09) ? '%' : '#';
105             break;
106 #endif
107         }
108
109         a = a & 0x0F;
110         if ((y == 0 && x == 0) || a != old_a) {
111             int rv = angband_color_table[a][1];
112             int gv = angband_color_table[a][2];
113             int bv = angband_color_table[a][3];
114             fprintf(fff, "%s<font color=\"#%02x%02x%02x\">",
115                 ((y == 0 && x == 0) ? "" : "</font>"), rv, gv, bv);
116             old_a = a;
117         }
118
119         if (cc) {
120             fprintf(fff, "%s", cc);
121         } else {
122             fprintf(fff, "%c", c);
123         }
124     }
125 }
126
127 /*!
128  * @brief 記念撮影を行方向にスイープする
129  * @param wid 幅
130  * @param hgt 高さ
131  * @param fff 記念撮影ファイルへの参照ポインタ
132  */
133 static void screen_dump_lines(int wid, int hgt, FILE *fff)
134 {
135     for (TERM_LEN y = 0; y < hgt; y++) {
136         if (y != 0) {
137             fprintf(fff, "\n");
138         }
139
140         screen_dump_one_line(wid, y, fff);
141     }
142 }
143
144 /*!
145  * @brief ファイルへ書き込めない場合にエラーを表示する
146  * @param fff ダンプファイルへの参照ポインタ
147  * @param path 保存先HTMLファイルのパス
148  * @param need_message メッセージ表示の必要性
149  * @return メッセージを表示して後続処理を実行するか否か
150  */
151 static bool check_screen_html_can_open(FILE *fff, const std::filesystem::path &path, bool need_message)
152 {
153     if (fff) {
154         return true;
155     }
156
157     if (!need_message) {
158         return false;
159     }
160
161     const auto &path_str = path.string();
162     const auto mes = format(_("ファイル %s を開けませんでした。", "Failed to open file %s."), path_str.data());
163     THROW_EXCEPTION(std::runtime_error, mes);
164 }
165
166 /*!
167  * @brief HTMLヘッダを書き込む
168  * @param tmpfff 一時ファイルへの参照ポインタ
169  * @param fff 記念撮影ファイルへの参照ポインタ
170  */
171 static void write_html_header(FILE *tmpfff, FILE *fff)
172 {
173     if (tmpfff) {
174         read_temporary_file(fff, tmpfff, 0);
175         return;
176     }
177
178     for (int i = 0; html_head[i]; i++) {
179         fputs(html_head[i], fff);
180     }
181 }
182
183 /*!
184  * @brief HTMLフッタを書き込む
185  * @param tmpfff 一時ファイルへの参照ポインタ
186  * @param fff 記念撮影ファイルへの参照ポインタ
187  */
188 static void write_html_footer(FILE *tmpfff, FILE *fff)
189 {
190     fprintf(fff, "</font>");
191     if (!tmpfff) {
192         for (int i = 0; html_foot[i]; i++) {
193             fputs(html_foot[i], fff);
194         }
195     } else {
196         rewind(tmpfff);
197         read_temporary_file(fff, tmpfff, 2);
198         angband_fclose(tmpfff);
199     }
200
201     fprintf(fff, "\n");
202 }
203
204 void exe_cmd_save_screen_html(const std::filesystem::path &path, bool need_message)
205 {
206     TERM_LEN wid, hgt;
207     term_get_size(&wid, &hgt);
208     auto *fff = angband_fopen(filename, FileOpenMode::WRITE, false,
209         FileOpenType::HTML);
210     if (!check_screen_html_can_open(fff, filename, need_message)) {
211         return;
212     }
213
214     if (need_message) {
215         screen_save();
216     }
217
218     const auto &path_prf = path_build(ANGBAND_DIR_USER, "htmldump.prf");
219     auto *tmpfff = angband_fopen(path_prf, FileOpenMode::READ);
220     write_html_header(tmpfff, fff);
221     screen_dump_lines(wid, hgt, fff);
222     write_html_footer(tmpfff, fff);
223     angband_fclose(fff);
224     if (!need_message) {
225         return;
226     }
227
228     msg_print(_("画面(記念撮影)をファイルに書き出しました。", "Screen dump saved."));
229     msg_print(nullptr);
230     screen_load();
231 }
232
233 /*!
234  * @brief HTML方式で記念撮影する / Save a screen dump to a file
235  * @param なし
236  */
237 static void exe_cmd_save_screen_html_with_naming()
238 {
239     char tmp[256] = "screen.html";
240     if (!get_string(_("ファイル名: ", "File name: "), tmp, 80)) {
241         return;
242     }
243
244     auto path = path_build(ANGBAND_DIR_USER, tmp);
245     msg_print(nullptr);
246     exe_cmd_save_screen_html(path, true);
247 }
248
249 /*!
250  * @brief 記念撮影の方式を問い合わせる
251  * @param html_dump HTMLダンプするか否か
252  * @return ダンプするならTRUE、キャンセルならFALSE
253  */
254 static bool ask_html_dump(bool *html_dump)
255 {
256     while (true) {
257         char c = inkey();
258         if (c == 'Y' || c == 'y') {
259             *html_dump = false;
260             return true;
261         }
262
263         if (c == 'H' || c == 'h') {
264             *html_dump = true;
265             return true;
266         }
267
268         prt("", 0, 0);
269         return false;
270     }
271
272     // コンパイル警告対応.
273     return false;
274 }
275
276 /*!
277  * @brief ファイルへ書き込めない場合にエラーを表示する
278  * @param fff ダンプファイルへの参照ポインタ
279  * @return ファイルへ書き込めるならTRUE、書き込めないならFALSE
280  */
281 static bool check_screen_text_can_open(FILE *fff, const std::string_view filename)
282 {
283     if (fff) {
284         return true;
285     }
286
287     msg_format(_("ファイル %s を開けませんでした。", "Failed to open file %s."), filename.data());
288     msg_print(nullptr);
289     return false;
290 }
291
292 /*!
293  * @brief テキスト方式で記念撮影する
294  * @param wid 幅
295  * @param hgt 高さ
296  * @return 記念撮影に成功したらTRUE、ファイルが開けなかったらFALSE
297  * @todo どこかバグっていて、(恐らく初期化されていない)変な文字列まで出力される
298  */
299 static bool do_cmd_save_screen_text(int wid, int hgt)
300 {
301     TERM_COLOR a = 0;
302     auto c = ' ';
303     const auto &path = path_build(ANGBAND_DIR_USER, "dump.txt");
304     auto *fff = angband_fopen(path, FileOpenMode::WRITE);
305     if (!check_screen_text_can_open(fff, path.string())) {
306         return false;
307     }
308
309     screen_save();
310     for (TERM_LEN y = 0; y < hgt; y++) {
311         TERM_LEN x;
312         char buf[1024]{};
313         for (x = 0; x < wid - 1; x++) {
314             (void)(term_what(x, y, &a, &c));
315             buf[x] = c;
316         }
317
318         buf[x] = '\0';
319         fprintf(fff, "%s\n", buf);
320     }
321
322     fprintf(fff, "\n");
323     for (TERM_LEN y = 0; y < hgt; y++) {
324         TERM_LEN x;
325         char buf[1024]{};
326         for (x = 0; x < wid - 1; x++) {
327             (void)(term_what(x, y, &a, &c));
328             buf[x] = hack[a & 0x0F];
329         }
330
331         buf[x] = '\0';
332         fprintf(fff, "%s\n", buf);
333     }
334
335     fprintf(fff, "\n");
336     angband_fclose(fff);
337     msg_print(_("画面(記念撮影)をファイルに書き出しました。", "Screen dump saved."));
338     msg_print(nullptr);
339     screen_load();
340     return true;
341 }
342
343 /*!
344  * @brief 記念撮影のためにグラフィック使用をOFFにする
345  * @param player_ptr プレイヤーへの参照ポインタ
346  * @return 記念撮影直前のグラフィックオプション
347  */
348 static bool update_use_graphics(PlayerType *player_ptr)
349 {
350     if (!use_graphics) {
351         return true;
352     }
353
354     use_graphics = false;
355     reset_visuals(player_ptr);
356     static constexpr auto flags = {
357         MainWindowRedrawingFlag::WIPE,
358         MainWindowRedrawingFlag::BASIC,
359         MainWindowRedrawingFlag::EXTRA,
360         MainWindowRedrawingFlag::MAP,
361         MainWindowRedrawingFlag::EQUIPPY,
362     };
363     RedrawingFlagsUpdater::get_instance().set_flags(flags);
364     handle_stuff(player_ptr);
365     return false;
366 }
367
368 /*
369  * Save a screen dump to a file
370  * @param player_ptr プレイヤーへの参照ポインタ
371  */
372 void do_cmd_save_screen(PlayerType *player_ptr)
373 {
374     prt(_("記念撮影しますか? [(y)es/(h)tml/(n)o] ", "Save screen dump? [(y)es/(h)tml/(n)o] "), 0, 0);
375     bool html_dump;
376     if (!ask_html_dump(&html_dump)) {
377         return;
378     }
379
380     int wid, hgt;
381     term_get_size(&wid, &hgt);
382
383     bool old_use_graphics = update_use_graphics(player_ptr);
384
385     if (html_dump) {
386         exe_cmd_save_screen_html_with_naming();
387         do_cmd_redraw(player_ptr);
388     } else if (!do_cmd_save_screen_text(wid, hgt)) {
389         return;
390     }
391
392     if (old_use_graphics) {
393         return;
394     }
395
396     use_graphics = true;
397     reset_visuals(player_ptr);
398     static constexpr auto flags = {
399         MainWindowRedrawingFlag::WIPE,
400         MainWindowRedrawingFlag::BASIC,
401         MainWindowRedrawingFlag::EXTRA,
402         MainWindowRedrawingFlag::MAP,
403         MainWindowRedrawingFlag::EQUIPPY,
404     };
405     RedrawingFlagsUpdater::get_instance().set_flags(flags);
406     handle_stuff(player_ptr);
407 }
408
409 /*!
410  * @brief 白文字だけ画面に描画する
411  * @param buf 描画用バッファ
412  * @param fff 記念撮影ファイルへの参照ポインタ
413  * @param wid 幅
414  * @param hgt 高さ
415  * @todo 目的は不明瞭
416  * @return ファイルが読み込めなくなったらFALSEで抜ける
417  */
418 static bool draw_white_characters(FILE *fff, int wid, int hgt)
419 {
420     bool okay = true;
421     for (TERM_LEN y = 0; okay; y++) {
422         char buf[1024]{};
423         if (!fgets(buf, sizeof(buf), fff)) {
424             okay = false;
425         }
426
427         if (buf[0] == '\n' || buf[0] == '\0') {
428             break;
429         }
430         if (y >= hgt) {
431             continue;
432         }
433
434         for (TERM_LEN x = 0; x < wid - 1; x++) {
435             if (buf[x] == '\n' || buf[x] == '\0') {
436                 break;
437             }
438
439             term_draw(x, y, TERM_WHITE, buf[x]);
440         }
441     }
442
443     return okay;
444 }
445
446 /*!
447  * @brief 白以外の文字を画面に描画する
448  * @param fff 記念撮影ファイルへの参照ポインタ
449  * @param wid 幅
450  * @param hgt 高さ
451  * @param 白文字が途中で読み込めなくなっていたらTRUE
452  * @todo 目的は不明瞭
453  */
454 static void draw_colored_characters(FILE *fff, int wid, int hgt, bool okay)
455 {
456     TERM_COLOR a = TERM_DARK;
457     auto c = ' ';
458     for (TERM_LEN y = 0; okay; y++) {
459         char buf[1024]{};
460         if (!fgets(buf, sizeof(buf), fff)) {
461             okay = false;
462         }
463
464         if (buf[0] == '\n' || buf[0] == '\0') {
465             break;
466         }
467         if (y >= hgt) {
468             continue;
469         }
470
471         for (TERM_LEN x = 0; x < wid - 1; x++) {
472             if (buf[x] == '\n' || buf[x] == '\0') {
473                 break;
474             }
475
476             (void)(term_what(x, y, &a, &c));
477             for (int i = 0; i < 16; i++) {
478                 if (hack[i] == buf[x]) {
479                     a = (byte)i;
480                 }
481             }
482
483             term_draw(x, y, a, c);
484         }
485     }
486 }
487
488 /*
489  * @brief Load a screen dump from a file
490  * @param なし
491  */
492 void do_cmd_load_screen(void)
493 {
494     TERM_LEN wid, hgt;
495     term_get_size(&wid, &hgt);
496     const auto path = path_build(ANGBAND_DIR_USER, "dump.txt");
497     auto *fff = angband_fopen(path, FileOpenMode::READ);
498     if (!fff) {
499         const auto filename = path.string();
500         msg_format(_("%s を開くことができませんでした。", "Failed to open %s."), filename.data());
501         msg_print(nullptr);
502         return;
503     }
504
505     screen_save();
506     term_clear();
507     bool okay = draw_white_characters(fff, wid, hgt);
508     draw_colored_characters(fff, wid, hgt, okay);
509
510     angband_fclose(fff);
511     prt(_("ファイルに書き出された画面(記念撮影)をロードしました。", "Screen dump loaded."), 0, 0);
512     flush();
513     inkey();
514     screen_load();
515 }