OSDN Git Service

[Refactor] #39962 files.c からcharacter-dump.c/h を分離 / Separated character-dump.c/h...
[hengband/hengband.git] / src / report.c
1 /*!
2  * @file report.c
3  * @brief スコアサーバ転送機能の実装
4  * @date 2014/07/14
5  * @author Hengband Team
6  */
7
8 #include "angband.h"
9 #include "core.h"
10 #include "inet.h"
11 #include "dungeon.h"
12
13 #include "player-personality.h"
14 #include "character-dump.h"
15 #include "files.h"
16 #include "world.h"
17 #include "term.h"
18
19 #ifdef WORLD_SCORE
20
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <ctype.h>
24 #include <string.h>
25
26 #if defined(WINDOWS)
27 #include <winsock.h>
28 #else
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <netdb.h>
33 #include <sys/time.h>
34
35 #include <setjmp.h>
36 #include <signal.h>
37 #endif
38
39 concptr screen_dump = NULL;
40
41 /*
42  * internet resource value
43  */
44 #define HTTP_PROXY ""                   /*!< デフォルトのプロキシURL / Default proxy url */
45 #define HTTP_PROXY_PORT 0               /*!< デフォルトのプロキシポート / Default proxy port */
46 #define HTTP_TIMEOUT    20              /*!< デフォルトのタイムアウト時間(秒) / Timeout length (second) */
47 #define SCORE_SERVER "hengband.osdn.jp" /*!< デフォルトのスコアサーバURL / Default score server url */
48 #define SCORE_PORT 80                   /*!< デフォルトのスコアサーバポート / Default score server port */
49
50 #ifdef JP
51 #define SCORE_PATH "http://hengband.osdn.jp/score/register_score.php" /*!< スコア開示URL */
52 #else
53 #define SCORE_PATH "http://moon.kmc.gr.jp/hengband/hengscore-en/score.cgi" /*!< スコア開示URL */
54 #endif
55
56  /*
57   * simple buffer library
58   */
59 typedef struct {
60         size_t max_size;
61         size_t size;
62         char *data;
63 } BUF;
64
65 #define BUFSIZE (65536) /*!< スコアサーバ転送バッファサイズ */
66
67 /*!
68  * @brief 転送用バッファの確保
69  * @return 確保したバッファの参照ポインタ
70  */
71 static BUF* buf_new(void)
72 {
73         BUF *p;
74         p = malloc(sizeof(BUF));
75         if (!p) return NULL;
76
77         p->size = 0;
78         p->max_size = BUFSIZE;
79         p->data = malloc(BUFSIZE);
80         if (!p->data)
81         {
82                 free(p);
83                 return NULL;
84         }
85
86         return p;
87 }
88
89
90 /*!
91  * @brief 転送用バッファの解放
92  * @param b 解放するバッファの参照ポインタ
93  */
94 static void buf_delete(BUF *b)
95 {
96         free(b->data);
97         free(b);
98 }
99
100
101 /*!
102  * @brief 転送用バッファにデータを追加する
103  * @param buf 追加先バッファの参照ポインタ
104  * @param data 追加元データ
105  * @param size 追加サイズ
106  * @return 追加後のバッファ容量
107  */
108 static int buf_append(BUF *buf, concptr data, size_t size)
109 {
110         while (buf->size + size > buf->max_size)
111         {
112                 char *tmp;
113                 if ((tmp = malloc(buf->max_size * 2)) == NULL) return -1;
114
115                 memcpy(tmp, buf->data, buf->max_size);
116                 free(buf->data);
117
118                 buf->data = tmp;
119
120                 buf->max_size *= 2;
121         }
122         memcpy(buf->data + buf->size, data, size);
123         buf->size += size;
124
125         return buf->size;
126 }
127
128
129 /*!
130  * @brief 転送用バッファにフォーマット指定した文字列データを追加する
131  * @param buf 追加先バッファの参照ポインタ
132  * @param fmt 文字列フォーマット
133  * @return 追加後のバッファ容量
134  */
135 static int buf_sprintf(BUF *buf, concptr fmt, ...)
136 {
137         int             ret;
138         char    tmpbuf[8192];
139         va_list ap;
140
141         va_start(ap, fmt);
142 #if defined(HAVE_VSNPRINTF)
143         ret = vsnprintf(tmpbuf, sizeof(tmpbuf), fmt, ap);
144 #else
145         ret = vsprintf(tmpbuf, fmt, ap);
146 #endif
147         va_end(ap);
148
149         if (ret < 0) return -1;
150
151 #if ('\r' == 0x0a && '\n' == 0x0d)
152         {
153                 /*
154                  * Originally '\r'= CR (= 0x0d) and '\n'= LF (= 0x0a)
155                  * But for MPW (Macintosh Programers Workbench), these
156                  * are reversed so that '\r'=LF and '\n'=CR unless the
157                  * -noMapCR option is not defined.
158                  *
159                  * We need to swap back these here since the score
160                  * dump text should be written using LF as the end of
161                  * line.
162                  */
163                 char *ptr;
164                 for (ptr = tmpbuf; *ptr; ptr++)
165                 {
166                         if (0x0d == *ptr) *ptr = 0x0a;
167                 }
168         }
169 #endif
170
171         ret = buf_append(buf, tmpbuf, strlen(tmpbuf));
172
173         return ret;
174 }
175
176
177 /*!
178  * @brief HTTPによるダンプ内容伝送
179  * @param sd ソケットID
180  * @param url 伝送先URL
181  * @param buf 伝送内容バッファ
182  * @return なし
183  */
184 static bool http_post(int sd, concptr url, BUF *buf)
185 {
186         BUF *output;
187         char response_buf[1024] = "";
188         concptr HTTP_RESPONSE_CODE_OK = "HTTP/1.1 200 OK";
189
190         output = buf_new();
191         buf_sprintf(output, "POST %s HTTP/1.0\r\n", url);
192         buf_sprintf(output, "User-Agent: Hengband %d.%d.%d\r\n",
193                 FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH);
194
195         buf_sprintf(output, "Content-Length: %d\r\n", buf->size);
196         buf_sprintf(output, "Content-Encoding: binary\r\n");
197 #ifdef JP
198 #ifdef SJIS
199         buf_sprintf(output, "Content-Type: text/plain; charset=SHIFT_JIS\r\n");
200 #endif
201 #ifdef EUC
202         buf_sprintf(output, "Content-Type: text/plain; charset=EUC-JP\r\n");
203 #endif
204 #else
205         buf_sprintf(output, "Content-Type: text/plain; charset=ASCII\r\n");
206 #endif
207         buf_sprintf(output, "\r\n");
208         buf_append(output, buf->data, buf->size);
209
210         soc_write(sd, output->data, output->size);
211
212         soc_read(sd, response_buf, sizeof(response_buf));
213
214         return strncmp(response_buf, HTTP_RESPONSE_CODE_OK, strlen(HTTP_RESPONSE_CODE_OK)) == 0;
215 }
216
217
218 /*!
219  * @brief キャラクタダンプを作って BUFに保存
220  * @param creature_ptr プレーヤーへの参照ポインタ
221  * @param dumpbuf 伝送内容バッファ
222  * @return エラーコード
223  */
224 static errr make_dump(player_type *creature_ptr, BUF* dumpbuf, void(*update_playtime)(void), void(*display_player)(player_type*, int))
225 {
226         char            buf[1024];
227         FILE *fff;
228         GAME_TEXT file_name[1024];
229
230         /* Open a new file */
231         fff = my_fopen_temp(file_name, 1024);
232         if (!fff)
233         {
234 #ifdef JP
235                 msg_format("一時ファイル %s を作成できませんでした。", file_name);
236 #else
237                 msg_format("Failed to create temporary file %s.", file_name);
238 #endif
239                 msg_print(NULL);
240                 return 1;
241         }
242
243         /* 一旦一時ファイルを作る。通常のダンプ出力と共通化するため。 */
244         (void)make_character_dump(creature_ptr, fff, update_playtime, display_player);
245         my_fclose(fff);
246
247         /* Open for read */
248         fff = my_fopen(file_name, "r");
249
250         while (fgets(buf, 1024, fff))
251         {
252                 (void)buf_sprintf(dumpbuf, "%s", buf);
253         }
254         my_fclose(fff);
255         fd_kill(file_name);
256
257         /* Success */
258         return 0;
259 }
260
261
262 /*!
263  * @brief スクリーンダンプを作成する/ Make screen dump to buffer
264  * @return 作成したスクリーンダンプの参照ポインタ
265  */
266 concptr make_screen_dump(player_type *creature_ptr)
267 {
268         static concptr html_head[] = {
269                 "<html>\n<body text=\"#ffffff\" bgcolor=\"#000000\">\n",
270                 "<pre>",
271                 0,
272         };
273         static concptr html_foot[] = {
274                 "</pre>\n",
275                 "</body>\n</html>\n",
276                 0,
277         };
278
279         int wid, hgt;
280         Term_get_size(&wid, &hgt);
281
282         /* Alloc buffer */
283         BUF *screen_buf;
284         screen_buf = buf_new();
285         if (screen_buf == NULL) return (NULL);
286
287         bool old_use_graphics = use_graphics;
288         if (old_use_graphics)
289         {
290                 /* Clear -more- prompt first */
291                 msg_print(NULL);
292
293                 use_graphics = FALSE;
294                 reset_visuals(creature_ptr);
295
296                 creature_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIPPY);
297                 handle_stuff(creature_ptr);
298         }
299
300         for (int i = 0; html_head[i]; i++)
301                 buf_sprintf(screen_buf, html_head[i]);
302
303         /* Dump the screen */
304         for (int y = 0; y < hgt; y++)
305         {
306                 /* Start the row */
307                 if (y != 0)
308                         buf_sprintf(screen_buf, "\n");
309
310                 /* Dump each row */
311                 TERM_COLOR a = 0, old_a = 0;
312                 SYMBOL_CODE c = ' ';
313                 for (int x = 0; x < wid - 1; x++)
314                 {
315                         int rv, gv, bv;
316                         concptr cc = NULL;
317                         /* Get the attr/char */
318                         (void)(Term_what(x, y, &a, &c));
319
320                         switch (c)
321                         {
322                         case '&': cc = "&amp;"; break;
323                         case '<': cc = "&lt;"; break;
324                         case '>': cc = "&gt;"; break;
325                         case '"': cc = "&quot;"; break;
326                         case '\'': cc = "&#39;"; break;
327 #ifdef WINDOWS
328                         case 0x1f: c = '.'; break;
329                         case 0x7f: c = (a == 0x09) ? '%' : '#'; break;
330 #endif
331                         }
332
333                         a = a & 0x0F;
334                         if ((y == 0 && x == 0) || a != old_a) {
335                                 rv = angband_color_table[a][1];
336                                 gv = angband_color_table[a][2];
337                                 bv = angband_color_table[a][3];
338                                 buf_sprintf(screen_buf, "%s<font color=\"#%02x%02x%02x\">",
339                                         ((y == 0 && x == 0) ? "" : "</font>"), rv, gv, bv);
340                                 old_a = a;
341                         }
342
343                         if (cc)
344                                 buf_sprintf(screen_buf, "%s", cc);
345                         else
346                                 buf_sprintf(screen_buf, "%c", c);
347                 }
348         }
349
350         buf_sprintf(screen_buf, "</font>");
351
352         for (int i = 0; html_foot[i]; i++)
353                 buf_sprintf(screen_buf, html_foot[i]);
354
355         /* Screen dump size is too big ? */
356         concptr ret;
357         if (screen_buf->size + 1 > SCREEN_BUF_MAX_SIZE)
358         {
359                 ret = NULL;
360         }
361         else
362         {
363                 /* Terminate string */
364                 buf_append(screen_buf, "", 1);
365
366                 ret = string_make(screen_buf->data);
367         }
368
369         /* Free buffer */
370         buf_delete(screen_buf);
371
372         if (!old_use_graphics) return ret;
373
374         use_graphics = TRUE;
375         reset_visuals(creature_ptr);
376
377         creature_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIPPY);
378         handle_stuff(creature_ptr);
379         return ret;
380 }
381
382
383 /*!
384  * todo メッセージは言語選択の関数マクロで何とかならんか?
385  * @brief スコア転送処理のメインルーチン
386  * @param creature_ptr プレーヤーへの参照ポインタ
387  * @return 正常終了の時0、異常があったら1
388  */
389 errr report_score(player_type *creature_ptr, void(*update_playtime)(void), void(*display_player)(player_type*, int))
390 {
391 #ifdef WINDOWS
392         WSADATA wsaData;
393         WORD wVersionRequested = (WORD)((1) | (1 << 8));
394 #endif
395
396         BUF *score;
397         score = buf_new();
398
399         char seikakutmp[128];
400 #ifdef JP
401         sprintf(seikakutmp, "%s%s", ap_ptr->title, (ap_ptr->no ? "の" : ""));
402 #else
403         sprintf(seikakutmp, "%s ", ap_ptr->title);
404 #endif
405
406         buf_sprintf(score, "name: %s\n", creature_ptr->name);
407 #ifdef JP
408         buf_sprintf(score, "version: 変愚蛮怒 %d.%d.%d\n",
409                 FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH);
410 #else
411         buf_sprintf(score, "version: Hengband %d.%d.%d\n",
412                 FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH);
413 #endif
414         buf_sprintf(score, "score: %d\n", calc_score(creature_ptr));
415         buf_sprintf(score, "level: %d\n", creature_ptr->lev);
416         buf_sprintf(score, "depth: %d\n", creature_ptr->current_floor_ptr->dun_level);
417         buf_sprintf(score, "maxlv: %d\n", creature_ptr->max_plv);
418         buf_sprintf(score, "maxdp: %d\n", max_dlv[DUNGEON_ANGBAND]);
419         buf_sprintf(score, "au: %d\n", creature_ptr->au);
420         buf_sprintf(score, "turns: %d\n", turn_real(creature_ptr, current_world_ptr->game_turn));
421         buf_sprintf(score, "sex: %d\n", creature_ptr->psex);
422         buf_sprintf(score, "race: %s\n", rp_ptr->title);
423         buf_sprintf(score, "class: %s\n", cp_ptr->title);
424         buf_sprintf(score, "seikaku: %s\n", seikakutmp);
425         buf_sprintf(score, "realm1: %s\n", realm_names[creature_ptr->realm1]);
426         buf_sprintf(score, "realm2: %s\n", realm_names[creature_ptr->realm2]);
427         buf_sprintf(score, "killer: %s\n", creature_ptr->died_from);
428         buf_sprintf(score, "-----charcter dump-----\n");
429
430         make_dump(creature_ptr, score, update_playtime, display_player);
431
432         if (screen_dump)
433         {
434                 buf_sprintf(score, "-----screen shot-----\n");
435                 buf_append(score, screen_dump, strlen(screen_dump));
436         }
437
438 #ifdef WINDOWS
439         if (WSAStartup(wVersionRequested, &wsaData))
440         {
441                 msg_print("Report: WSAStartup failed.");
442 #ifdef WINDOWS
443                 WSACleanup();
444 #endif
445                 return 1;
446         }
447 #endif
448
449         Term_clear();
450
451         int sd;
452         while (TRUE)
453         {
454                 char buff[160];
455 #ifdef JP
456                 prt("接続中...", 0, 0);
457 #else
458                 prt("connecting...", 0, 0);
459 #endif
460                 Term_fresh();
461
462                 /* プロキシを設定する */
463                 set_proxy(HTTP_PROXY, HTTP_PROXY_PORT);
464
465                 /* Connect to the score server */
466                 sd = connect_server(HTTP_TIMEOUT, SCORE_SERVER, SCORE_PORT);
467
468
469                 if (sd < 0) {
470 #ifdef JP
471                         sprintf(buff, "スコア・サーバへの接続に失敗しました。(%s)", soc_err());
472 #else
473                         sprintf(buff, "Failed to connect to the score server.(%s)", soc_err());
474 #endif
475                         prt(buff, 0, 0);
476                         (void)inkey();
477
478 #ifdef JP
479                         if (!get_check_strict("もう一度接続を試みますか? ", CHECK_NO_HISTORY))
480 #else
481                         if (!get_check_strict("Try again? ", CHECK_NO_HISTORY))
482 #endif
483                         {
484 #ifdef WINDOWS
485                                 WSACleanup();
486 #endif
487                                 return 1;
488                         }
489
490                         continue;
491                 }
492
493 #ifdef JP
494                 prt("スコア送信中...", 0, 0);
495 #else
496                 prt("Sending the score...", 0, 0);
497 #endif
498                 Term_fresh();
499
500                 if (!http_post(sd, SCORE_PATH, score)) {
501                         disconnect_server(sd);
502 #ifdef JP
503                         sprintf(buff, "スコア・サーバへの送信に失敗しました。");
504 #else
505                         sprintf(buff, "Failed to send to the score server.");
506 #endif
507                         prt(buff, 0, 0);
508                         (void)inkey();
509
510 #ifdef JP
511                         if (!get_check_strict("もう一度接続を試みますか? ", CHECK_NO_HISTORY))
512 #else
513                         if (!get_check_strict("Try again? ", CHECK_NO_HISTORY))
514 #endif
515                         {
516 #ifdef WINDOWS
517                                 WSACleanup();
518 #endif
519                                 return 1;
520                         }
521
522                         continue;
523                 }
524
525                 disconnect_server(sd);
526                 break;
527         }
528
529 #ifdef WINDOWS
530         WSACleanup();
531 #endif
532
533         return 0;
534 }
535 #endif /* WORLD_SCORE */