5 * @author Hengband Team
9 #include "core/asking-player.h"
10 #include "core/player-redraw-types.h"
11 #include "core/stuff-handler.h"
12 #include "core/turn-compensator.h"
13 #include "core/visuals-reseter.h"
14 #include "dungeon/dungeon.h"
15 #include "game-option/special-options.h"
16 #include "io-dump/character-dump.h"
18 #include "io/input-key-acceptor.h"
19 #include "mind/mind-elementalist.h"
20 #include "player-info/class-info.h"
21 #include "player-info/race-info.h"
22 #include "player/player-personality.h"
23 #include "player/player-status.h"
24 #include "realm/realm-names-table.h"
25 #include "system/angband-version.h"
26 #include "system/floor-type-definition.h"
27 #include "system/player-type-definition.h"
28 #include "system/system-variables.h"
29 #include "term/gameterm.h"
30 #include "term/screen-processor.h"
31 #include "util/angband-files.h"
32 #include "view/display-messages.h"
33 #include "world/world.h"
38 #define CURL_STATICLIB
40 #include <curl/curl.h>
42 concptr screen_dump = nullptr;
45 * internet resource value
47 #define HTTP_TIMEOUT 30 /*!< デフォルトのタイムアウト時間(秒) / Timeout length (second) */
50 #define SCORE_PATH "http://mars.kmc.gr.jp/~dis/heng_score/register_score.php" /*!< スコア開示URL */
52 #define SCORE_PATH "http://moon.kmc.gr.jp/hengband/hengscore-en/score.cgi" /*!< スコア開示URL */
56 * simple buffer library
65 #define BUFSIZE (65536) /*!< スコアサーバ転送バッファサイズ */
69 * @return 確保したバッファの参照ポインタ
71 static BUF *buf_new(void)
74 p = static_cast<BUF *>(malloc(sizeof(BUF)));
79 p->max_size = BUFSIZE;
80 p->data = static_cast<char *>(malloc(BUFSIZE));
91 * @param b 解放するバッファの参照ポインタ
93 static void buf_delete(BUF *b)
100 * @brief 転送用バッファにデータを追加する
101 * @param buf 追加先バッファの参照ポインタ
106 static int buf_append(BUF *buf, concptr data, size_t size)
108 while (buf->size + size > buf->max_size) {
110 if ((tmp = static_cast<char *>(malloc(buf->max_size * 2))) == nullptr)
113 memcpy(tmp, buf->data, buf->max_size);
120 memcpy(buf->data + buf->size, data, size);
127 * @brief 転送用バッファにフォーマット指定した文字列データを追加する
128 * @param buf 追加先バッファの参照ポインタ
129 * @param fmt 文字列フォーマット
132 static int buf_sprintf(BUF *buf, concptr fmt, ...)
139 #if defined(HAVE_VSNPRINTF)
140 ret = vsnprintf(tmpbuf, sizeof(tmpbuf), fmt, ap);
142 ret = vsprintf(tmpbuf, fmt, ap);
149 ret = buf_append(buf, tmpbuf, strlen(tmpbuf));
153 size_t read_callback(char *buffer, size_t size, size_t nitems, void *userdata)
155 BUF *buf = static_cast<BUF *>(userdata);
156 const size_t remain = buf->size - buf->read_head;
157 const size_t copy_size = std::min<size_t>(size * nitems, remain);
159 strncpy(buffer, buf->data + buf->read_head, copy_size);
160 buf->read_head += copy_size;
166 * @brief HTTPによるダンプ内容伝送
168 * @param buf 伝送内容バッファ
169 * @return 送信に成功した場合TRUE、失敗した場合FALSE
171 static bool http_post(concptr url, BUF *buf)
173 bool succeeded = false;
174 CURL *curl = curl_easy_init();
175 if (curl == nullptr) {
179 struct curl_slist *slist = nullptr;
180 slist = curl_slist_append(slist,
183 "Content-Type: text/plain; charset=SHIFT_JIS"
186 "Content-Type: text/plain; charset=EUC-JP"
189 "Content-Type: text/plain; charset=ASCII"
193 curl_easy_setopt(curl, CURLOPT_URL, url);
194 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
197 snprintf(user_agent, sizeof(user_agent), "Hengband %d.%d.%d", FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH);
198 curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent);
200 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
201 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
203 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
204 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, HTTP_TIMEOUT);
205 curl_easy_setopt(curl, CURLOPT_TIMEOUT, HTTP_TIMEOUT);
207 curl_easy_setopt(curl, CURLOPT_POST, 1);
209 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
210 curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 10);
211 curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
214 curl_easy_setopt(curl, CURLOPT_READDATA, buf);
215 curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
216 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, buf->size);
218 if (curl_easy_perform(curl) == CURLE_OK) {
220 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
221 if (response_code == 200) {
226 curl_slist_free_all(slist);
227 curl_easy_cleanup(curl);
233 * @brief キャラクタダンプを作って BUFに保存
234 * @param player_ptr プレイヤーへの参照ポインタ
235 * @param dumpbuf 伝送内容バッファ
238 static errr make_dump(player_type *player_ptr, BUF *dumpbuf, display_player_pf display_player)
242 GAME_TEXT file_name[1024];
244 /* Open a new file */
245 fff = angband_fopen_temp(file_name, 1024);
248 msg_format("一時ファイル %s を作成できませんでした。", file_name);
250 msg_format("Failed to create temporary file %s.", file_name);
256 /* 一旦一時ファイルを作る。通常のダンプ出力と共通化するため。 */
257 make_character_dump(player_ptr, fff, display_player);
261 fff = angband_fopen(file_name, "r");
263 while (fgets(buf, 1024, fff)) {
264 (void)buf_sprintf(dumpbuf, "%s", buf);
274 * @brief スクリーンダンプを作成する/ Make screen dump to buffer
275 * @return 作成したスクリーンダンプの参照ポインタ
277 concptr make_screen_dump(player_type *player_ptr)
279 static concptr html_head[] = {
280 "<html>\n<body text=\"#ffffff\" bgcolor=\"#000000\">\n",
284 static concptr html_foot[] = {
286 "</body>\n</html>\n",
291 term_get_size(&wid, &hgt);
295 screen_buf = buf_new();
296 if (screen_buf == nullptr)
299 bool old_use_graphics = use_graphics;
300 if (old_use_graphics) {
301 /* Clear -more- prompt first */
304 use_graphics = false;
305 reset_visuals(player_ptr);
307 player_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIPPY);
308 handle_stuff(player_ptr);
311 for (int i = 0; html_head[i]; i++)
312 buf_sprintf(screen_buf, html_head[i]);
314 /* Dump the screen */
315 for (int y = 0; y < hgt; y++) {
318 buf_sprintf(screen_buf, "\n");
321 TERM_COLOR a = 0, old_a = 0;
323 for (int x = 0; x < wid - 1; x++) {
325 concptr cc = nullptr;
326 /* Get the attr/char */
327 (void)(term_what(x, y, &a, &c));
350 c = (a == 0x09) ? '%' : '#';
356 if ((y == 0 && x == 0) || a != old_a) {
357 rv = angband_color_table[a][1];
358 gv = angband_color_table[a][2];
359 bv = angband_color_table[a][3];
360 buf_sprintf(screen_buf, "%s<font color=\"#%02x%02x%02x\">", ((y == 0 && x == 0) ? "" : "</font>"), rv, gv, bv);
365 buf_sprintf(screen_buf, "%s", cc);
367 buf_sprintf(screen_buf, "%c", c);
371 buf_sprintf(screen_buf, "</font>");
373 for (int i = 0; html_foot[i]; i++)
374 buf_sprintf(screen_buf, html_foot[i]);
376 /* Screen dump size is too big ? */
378 if (screen_buf->size + 1 > SCREEN_BUF_MAX_SIZE) {
381 /* Terminate string */
382 buf_append(screen_buf, "", 1);
384 ret = string_make(screen_buf->data);
388 buf_delete(screen_buf);
390 if (!old_use_graphics)
394 reset_visuals(player_ptr);
396 player_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIPPY);
397 handle_stuff(player_ptr);
402 * @brief スコア転送処理のメインルーチン
403 * @param player_ptr プレイヤーへの参照ポインタ
404 * @return 正常にスコアを送信できたらtrue、失敗時に送信を中止したらfalse
406 bool report_score(player_type *player_ptr, display_player_pf display_player)
408 auto *score = buf_new();
409 char personality_desc[128];
413 sprintf(personality_desc, "%s%s", ap_ptr->title, (ap_ptr->no ? "の" : ""));
415 sprintf(personality_desc, "%s ", ap_ptr->title);
418 auto realm1_name = player_ptr->pclass == PlayerClassType::ELEMENTALIST ? get_element_title(player_ptr->element) : realm_names[player_ptr->realm1];
419 buf_sprintf(score, "name: %s\n", player_ptr->name);
420 buf_sprintf(score, "version: %s\n", title);
421 buf_sprintf(score, "score: %d\n", calc_score(player_ptr));
422 buf_sprintf(score, "level: %d\n", player_ptr->lev);
423 buf_sprintf(score, "depth: %d\n", player_ptr->current_floor_ptr->dun_level);
424 buf_sprintf(score, "maxlv: %d\n", player_ptr->max_plv);
425 buf_sprintf(score, "maxdp: %d\n", max_dlv[DUNGEON_ANGBAND]);
426 buf_sprintf(score, "au: %d\n", player_ptr->au);
427 buf_sprintf(score, "turns: %d\n", turn_real(player_ptr, w_ptr->game_turn));
428 buf_sprintf(score, "sex: %d\n", player_ptr->psex);
429 buf_sprintf(score, "race: %s\n", rp_ptr->title);
430 buf_sprintf(score, "class: %s\n", cp_ptr->title);
431 buf_sprintf(score, "seikaku: %s\n", personality_desc);
432 buf_sprintf(score, "realm1: %s\n", realm1_name);
433 buf_sprintf(score, "realm2: %s\n", realm_names[player_ptr->realm2]);
434 buf_sprintf(score, "killer: %s\n", player_ptr->died_from);
435 buf_sprintf(score, "-----charcter dump-----\n");
437 make_dump(player_ptr, score, display_player);
439 buf_sprintf(score, "-----screen shot-----\n");
440 buf_append(score, screen_dump, strlen(screen_dump));
446 prt(_("スコア送信中...", "Sending the score..."), 0, 0);
448 if (http_post(SCORE_PATH, score)) {
453 prt(_("スコア・サーバへの送信に失敗しました。", "Failed to send to the score server."), 0, 0);
455 if (get_check_strict(player_ptr, _("もう一度接続を試みますか? ", "Try again? "), CHECK_NO_HISTORY)) {
464 concptr screen_dump = nullptr;
465 #endif /* WORLD_SCORE */