OSDN Git Service

Merge pull request #3280 from Hourier/Fix-Write-Background-Setting
[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/player-redraw-types.h"
11 #include "core/stuff-handler.h"
12 #include "core/visuals-reseter.h"
13 #include "game-option/special-options.h"
14 #include "io/files-util.h"
15 #include "io/input-key-acceptor.h"
16 #include "system/player-type-definition.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     player_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIPPY);
353     handle_stuff(player_ptr);
354     return false;
355 }
356
357 /*
358  * Save a screen dump to a file
359  * @param player_ptr プレイヤーへの参照ポインタ
360  */
361 void do_cmd_save_screen(PlayerType *player_ptr)
362 {
363     prt(_("記念撮影しますか? [(y)es/(h)tml/(n)o] ", "Save screen dump? [(y)es/(h)tml/(n)o] "), 0, 0);
364     bool html_dump;
365     if (!ask_html_dump(&html_dump)) {
366         return;
367     }
368
369     int wid, hgt;
370     term_get_size(&wid, &hgt);
371
372     bool old_use_graphics = update_use_graphics(player_ptr);
373
374     if (html_dump) {
375         do_cmd_save_screen_html();
376         do_cmd_redraw(player_ptr);
377     } else if (!do_cmd_save_screen_text(wid, hgt)) {
378         return;
379     }
380
381     if (old_use_graphics) {
382         return;
383     }
384
385     use_graphics = true;
386     reset_visuals(player_ptr);
387     player_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIPPY);
388     handle_stuff(player_ptr);
389 }
390
391 /*!
392  * @brief 白文字だけ画面に描画する
393  * @param buf 描画用バッファ
394  * @param fff 記念撮影ファイルへの参照ポインタ
395  * @param wid 幅
396  * @param hgt 高さ
397  * @todo 目的は不明瞭
398  * @return ファイルが読み込めなくなったらFALSEで抜ける
399  */
400 static bool draw_white_characters(FILE *fff, int wid, int hgt)
401 {
402     bool okay = true;
403     for (TERM_LEN y = 0; okay; y++) {
404         char buf[1024]{};
405         if (!fgets(buf, sizeof(buf), fff)) {
406             okay = false;
407         }
408
409         if (buf[0] == '\n' || buf[0] == '\0') {
410             break;
411         }
412         if (y >= hgt) {
413             continue;
414         }
415
416         for (TERM_LEN x = 0; x < wid - 1; x++) {
417             if (buf[x] == '\n' || buf[x] == '\0') {
418                 break;
419             }
420
421             term_draw(x, y, TERM_WHITE, buf[x]);
422         }
423     }
424
425     return okay;
426 }
427
428 /*!
429  * @brief 白以外の文字を画面に描画する
430  * @param fff 記念撮影ファイルへの参照ポインタ
431  * @param wid 幅
432  * @param hgt 高さ
433  * @param 白文字が途中で読み込めなくなっていたらTRUE
434  * @todo 目的は不明瞭
435  */
436 static void draw_colored_characters(FILE *fff, int wid, int hgt, bool okay)
437 {
438     TERM_COLOR a = TERM_DARK;
439     auto c = ' ';
440     for (TERM_LEN y = 0; okay; y++) {
441         char buf[1024]{};
442         if (!fgets(buf, sizeof(buf), fff)) {
443             okay = false;
444         }
445
446         if (buf[0] == '\n' || buf[0] == '\0') {
447             break;
448         }
449         if (y >= hgt) {
450             continue;
451         }
452
453         for (TERM_LEN x = 0; x < wid - 1; x++) {
454             if (buf[x] == '\n' || buf[x] == '\0') {
455                 break;
456             }
457
458             (void)(term_what(x, y, &a, &c));
459             for (int i = 0; i < 16; i++) {
460                 if (hack[i] == buf[x]) {
461                     a = (byte)i;
462                 }
463             }
464
465             term_draw(x, y, a, c);
466         }
467     }
468 }
469
470 /*
471  * @brief Load a screen dump from a file
472  * @param なし
473  */
474 void do_cmd_load_screen(void)
475 {
476     TERM_LEN wid, hgt;
477     term_get_size(&wid, &hgt);
478     const auto path = path_build(ANGBAND_DIR_USER, "dump.txt");
479     auto *fff = angband_fopen(path, FileOpenMode::READ);
480     if (!fff) {
481         const auto filename = path.string();
482         msg_format(_("%s を開くことができませんでした。", "Failed to open %s."), filename.data());
483         msg_print(nullptr);
484         return;
485     }
486
487     screen_save();
488     term_clear();
489     bool okay = draw_white_characters(fff, wid, hgt);
490     draw_colored_characters(fff, wid, hgt, okay);
491
492     angband_fclose(fff);
493     prt(_("ファイルに書き出された画面(記念撮影)をロードしました。", "Screen dump loaded."), 0, 0);
494     flush();
495     inkey();
496     screen_load();
497 }