OSDN Git Service

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