OSDN Git Service

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