OSDN Git Service

Merge pull request #3569 from sikabane-works/release/3.0.0.88-alpha
[hengbandforosx/hengbandosx.git] / src / io / report.cpp
1 /*!
2  * @file report.c
3  * @brief スコアサーバ転送機能の実装
4  * @date 2014/07/14
5  * @author Hengband Team
6  */
7
8 #include "io/report.h"
9 #include "core/asking-player.h"
10 #include "core/stuff-handler.h"
11 #include "core/turn-compensator.h"
12 #include "core/visuals-reseter.h"
13 #include "game-option/special-options.h"
14 #include "io-dump/character-dump.h"
15 #include "io/input-key-acceptor.h"
16 #include "mind/mind-elementalist.h"
17 #include "net/http-client.h"
18 #include "player-base/player-class.h"
19 #include "player-info/class-info.h"
20 #include "player-info/race-info.h"
21 #include "player/player-personality.h"
22 #include "player/player-status.h"
23 #include "realm/realm-names-table.h"
24 #include "system/angband-version.h"
25 #include "system/dungeon-info.h"
26 #include "system/floor-type-definition.h"
27 #include "system/player-type-definition.h"
28 #include "system/redrawing-flags-updater.h"
29 #include "system/system-variables.h"
30 #include "term/gameterm.h"
31 #include "term/screen-processor.h"
32 #include "term/z-form.h"
33 #include "util/angband-files.h"
34 #include "view/display-messages.h"
35 #include "world/world.h"
36 #include <algorithm>
37 #include <fstream>
38 #include <span>
39 #include <sstream>
40 #include <string>
41 #include <string_view>
42 #include <vector>
43
44 #ifdef WORLD_SCORE
45
46 concptr screen_dump = nullptr;
47
48 #ifdef JP
49 constexpr auto SCORE_POST_URL = "http://mars.kmc.gr.jp/~dis/heng_score/register_score.php"; /*!< スコア開示URL */
50 #endif
51
52 static constexpr auto get_score_content_type()
53 {
54 #ifdef JP
55 #ifdef SJIS
56     return "text/plain; charset=SHIFT_JIS";
57 #endif
58 #ifdef EUC
59     return "text/plain; charset=EUC-JP";
60 #endif
61 #else
62     return "text/plain; charset=ASCII";
63 #endif
64 }
65
66 /*!
67  * @brief スコアサーバにスコアを送信する
68  * @param score_data 送信するスコアデータ
69  * @return 送信に成功した場合true、失敗した場合false
70  */
71 static bool post_score_to_score_server(PlayerType *player_ptr, const std::string &score_data)
72 {
73     http::Client client;
74     client.user_agent = format("Hengband %d.%d.%d", H_VER_MAJOR, H_VER_MINOR, H_VER_PATCH);
75
76     term_clear();
77
78     while (true) {
79         term_fresh();
80         prt(_("スコア送信中...", "Sending the score..."), 0, 0);
81         term_fresh();
82
83         const auto res = client.post(SCORE_POST_URL, score_data, get_score_content_type());
84         if (res.has_value() && (res->status == 200)) {
85             return true;
86         }
87
88         prt(_("スコア・サーバへの送信に失敗しました。", "Failed to send to the score server."), 0, 0);
89         (void)inkey();
90         if (!input_check_strict(player_ptr, _("もう一度接続を試みますか? ", "Try again? "), UserCheck::NO_HISTORY)) {
91             return false;
92         }
93     }
94 }
95
96 /*!
97  * @brief キャラクタダンプを引数で指定した出力ストリームに書き込む
98  * @param player_ptr プレイヤーへの参照ポインタ
99  * @param stream 書き込む出力ストリーム
100  * @return エラーコード
101  */
102 static errr make_dump(PlayerType *player_ptr, std::ostream &stream)
103 {
104     FILE *fff;
105     GAME_TEXT file_name[1024];
106
107     /* Open a new file */
108     fff = angband_fopen_temp(file_name, 1024);
109     if (!fff) {
110 #ifdef JP
111         msg_format("一時ファイル %s を作成できませんでした。", file_name);
112 #else
113         msg_format("Failed to create temporary file %s.", file_name);
114 #endif
115         msg_print(nullptr);
116         return 1;
117     }
118
119     /* 一旦一時ファイルを作る。通常のダンプ出力と共通化するため。 */
120     make_character_dump(player_ptr, fff);
121     angband_fclose(fff);
122
123     // 一時ファイルを削除する前に閉じるためブロックにする
124     {
125         std::ifstream ifs(file_name);
126         stream << ifs.rdbuf();
127     }
128
129     fd_kill(file_name);
130
131     /* Success */
132     return 0;
133 }
134
135 /*!
136  * @brief スクリーンダンプを作成する/ Make screen dump to buffer
137  * @return 作成したスクリーンダンプの参照ポインタ
138  */
139 concptr make_screen_dump(PlayerType *player_ptr)
140 {
141     constexpr auto html_head =
142         "<html>\n<body text=\"#ffffff\" bgcolor=\"#000000\">\n"
143         "<pre>\n";
144     constexpr auto html_foot =
145         "</pre>\n"
146         "</body>\n</html>\n";
147
148     const auto [wid, hgt] = term_get_size();
149     std::stringstream screen_ss;
150
151     auto &rfu = RedrawingFlagsUpdater::get_instance();
152     bool old_use_graphics = use_graphics;
153     if (old_use_graphics) {
154         /* Clear -more- prompt first */
155         msg_print(nullptr);
156
157         use_graphics = false;
158         reset_visuals(player_ptr);
159
160         static constexpr auto flags = {
161             MainWindowRedrawingFlag::WIPE,
162             MainWindowRedrawingFlag::BASIC,
163             MainWindowRedrawingFlag::EXTRA,
164             MainWindowRedrawingFlag::MAP,
165             MainWindowRedrawingFlag::EQUIPPY,
166         };
167         rfu.set_flags(flags);
168         handle_stuff(player_ptr);
169     }
170
171     screen_ss << html_head;
172
173     /* Dump the screen */
174     for (int y = 0; y < hgt; y++) {
175         /* Start the row */
176         if (y != 0) {
177             screen_ss << '\n';
178         }
179
180         /* Dump each row */
181         TERM_COLOR a = 0, old_a = 0;
182         auto c = ' ';
183         for (int x = 0; x < wid - 1; x++) {
184             int rv, gv, bv;
185             concptr cc = nullptr;
186             /* Get the attr/char */
187             (void)(term_what(x, y, &a, &c));
188
189             switch (c) {
190             case '&':
191                 cc = "&amp;";
192                 break;
193             case '<':
194                 cc = "&lt;";
195                 break;
196             case '>':
197                 cc = "&gt;";
198                 break;
199             case '"':
200                 cc = "&quot;";
201                 break;
202             case '\'':
203                 cc = "&#39;";
204                 break;
205 #ifdef WINDOWS
206             case 0x1f:
207                 c = '.';
208                 break;
209             case 0x7f:
210                 c = (a == 0x09) ? '%' : '#';
211                 break;
212 #endif
213             }
214
215             a = a & 0x0F;
216             if ((y == 0 && x == 0) || a != old_a) {
217                 rv = angband_color_table[a][1];
218                 gv = angband_color_table[a][2];
219                 bv = angband_color_table[a][3];
220                 screen_ss << format("%s<font color=\"#%02x%02x%02x\">", ((y == 0 && x == 0) ? "" : "</font>"), rv, gv, bv);
221                 old_a = a;
222             }
223
224             if (cc) {
225                 screen_ss << cc;
226             } else {
227                 screen_ss << c;
228             }
229         }
230     }
231
232     screen_ss << "</font>\n";
233
234     screen_ss << html_foot;
235
236     concptr ret;
237     if (const auto screen_dump_size = screen_ss.tellp();
238         (0 <= screen_dump_size) && (screen_dump_size < SCREEN_BUF_MAX_SIZE)) {
239         ret = string_make(screen_ss.str().data());
240     } else {
241         ret = nullptr;
242     }
243
244     if (!old_use_graphics) {
245         return ret;
246     }
247
248     use_graphics = true;
249     reset_visuals(player_ptr);
250     static constexpr auto flags = {
251         MainWindowRedrawingFlag::WIPE,
252         MainWindowRedrawingFlag::BASIC,
253         MainWindowRedrawingFlag::EXTRA,
254         MainWindowRedrawingFlag::MAP,
255         MainWindowRedrawingFlag::EQUIPPY,
256     };
257     rfu.set_flags(flags);
258     handle_stuff(player_ptr);
259     return ret;
260 }
261
262 /*!
263  * @brief スコア転送処理のメインルーチン
264  * @param player_ptr プレイヤーへの参照ポインタ
265  * @return 正常にスコアを送信できたらtrue、失敗時に送信を中止したらfalse
266  */
267 bool report_score(PlayerType *player_ptr)
268 {
269     std::stringstream score_ss;
270     std::string personality_desc = ap_ptr->title;
271     personality_desc.append(_(ap_ptr->no ? "の" : "", " "));
272
273     auto realm1_name = PlayerClass(player_ptr).equals(PlayerClassType::ELEMENTALIST) ? get_element_title(player_ptr->element) : realm_names[player_ptr->realm1];
274     score_ss << format("name: %s\n", player_ptr->name)
275              << format("version: %s\n", get_version().data())
276              << format("score: %ld\n", calc_score(player_ptr))
277              << format("level: %d\n", player_ptr->lev)
278              << format("depth: %d\n", player_ptr->current_floor_ptr->dun_level)
279              << format("maxlv: %d\n", player_ptr->max_plv)
280              << format("maxdp: %d\n", max_dlv[DUNGEON_ANGBAND])
281              << format("au: %d\n", player_ptr->au)
282              << format("turns: %d\n", turn_real(player_ptr, w_ptr->game_turn))
283              << format("sex: %d\n", player_ptr->psex)
284              << format("race: %s\n", rp_ptr->title)
285              << format("class: %s\n", cp_ptr->title)
286              << format("seikaku: %s\n", personality_desc.data())
287              << format("realm1: %s\n", realm1_name)
288              << format("realm2: %s\n", realm_names[player_ptr->realm2])
289              << format("killer: %s\n", player_ptr->died_from.data())
290              << "-----charcter dump-----\n";
291
292     make_dump(player_ptr, score_ss);
293     if (screen_dump) {
294         score_ss << "-----screen shot-----\n"
295                  << screen_dump;
296     }
297
298     return post_score_to_score_server(player_ptr, score_ss.str());
299 }
300 #else
301 concptr screen_dump = nullptr;
302 #endif /* WORLD_SCORE */