6 2014 Deskull rearranged comment for Doxygen.
9 #include "io/chuukei.h"
10 #include "cmd-io/cmd-dump.h"
11 #include "cmd-visual/cmd-draw.h"
12 #include "core/asking-player.h"
13 #include "io/files-util.h"
15 #include "io/signal-handlers.h"
16 #include "term/gameterm.h"
17 #include "util/angband-files.h"
18 #include "view/display-messages.h"
20 #include "locale/japanese.h"
34 #include <arpa/inet.h>
36 #include <netinet/in.h>
37 #include <sys/socket.h>
39 #include <sys/types.h>
45 #define MAX_HOSTNAME 256
48 #define RINGBUF_SIZE 1024 * 1024
49 #define FRESH_QUEUE_SIZE 4096
53 #define WAIT 100 * 1000 /* ブラウズ側のウエイト(us単位) */
55 #define DEFAULT_DELAY 50
56 #define RECVBUF_SIZE 1024
65 static long epoch_time; /* バッファ開始時刻 */
66 static int browse_delay; /* 表示するまでの時間(100ms単位)(この間にラグを吸収する) */
68 static int sd; /* ソケットのファイルディスクリプタ */
69 static long time_diff; /* プレイ側との時間のずれ(これを見ながらディレイを調整していく) */
70 static int server_port;
71 static GAME_TEXT server_name[MAX_HOSTNAME];
75 static int movie_mode;
79 #define close closesocket
83 /* 描画する時刻を覚えておくキュー構造体 */
85 int time[FRESH_QUEUE_SIZE];
101 static errr (*old_xtra_hook)(int n, int v);
102 static errr (*old_curs_hook)(int x, int y);
103 static errr (*old_bigcurs_hook)(int x, int y);
104 static errr (*old_wipe_hook)(int x, int y, int n);
105 static errr (*old_text_hook)(int x, int y, int n, TERM_COLOR a, concptr s);
107 static void disable_chuukei_server(void)
109 term_type *t = angband_term[0];
111 chuukei_server = FALSE;
113 t->xtra_hook = old_xtra_hook;
114 t->curs_hook = old_curs_hook;
115 t->bigcurs_hook = old_bigcurs_hook;
116 t->wipe_hook = old_wipe_hook;
117 t->text_hook = old_text_hook;
120 /* ANSI Cによればstatic変数は0で初期化されるが一応初期化する */
121 static errr init_buffer(void)
123 fresh_queue.next = fresh_queue.tail = 0;
124 ring.wptr = ring.rptr = ring.inlen = 0;
125 fresh_queue.time[0] = 0;
126 ring.buf = malloc(RINGBUF_SIZE);
127 if (ring.buf == NULL)
133 /* 現在の時間を100ms単位で取得する */
134 static long get_current_time(void)
137 return timeGetTime() / 100;
140 gettimeofday(&tv, NULL);
142 return (tv.tv_sec * 10 + tv.tv_usec / 100000);
146 /* リングバッファ構造体に buf の内容を加える */
147 static errr insert_ringbuf(char *buf)
150 len = strlen(buf) + 1; /* +1は終端文字分 */
153 fd_write(movie_fd, buf, len);
163 if (ring.inlen + len >= RINGBUF_SIZE) {
166 disable_chuukei_server();
168 chuukei_client = FALSE;
170 prt("送受信バッファが溢れました。サーバとの接続を切断します。", 0, 0);
179 if (ring.wptr + len < RINGBUF_SIZE) {
180 memcpy(ring.buf + ring.wptr, buf, len);
183 /* バッファの終端までに収まらない(ピッタリ収まる場合も含む) */
185 int head = RINGBUF_SIZE - ring.wptr; /* 前半 */
186 int tail = len - head; /* 後半 */
188 memcpy(ring.buf + ring.wptr, buf, head);
189 memcpy(ring.buf, buf + head, tail);
200 void flush_ringbuf(void)
223 /* ソケットにデータを書き込めるかどうか調べる */
224 select(sd + 1, (fd_set *)NULL, &tmp_fdset, (fd_set *)NULL, &tv);
227 if (FD_ISSET(sd, &tmp_fdset) == 0)
230 result = send(sd, ring.buf + ring.rptr, ((ring.wptr > ring.rptr) ? ring.wptr : RINGBUF_SIZE) - ring.rptr, 0);
235 disable_chuukei_server();
237 prt("サーバとの接続が切断されました。", 0, 0);
245 ring.inlen -= result;
247 if (ring.rptr == RINGBUF_SIZE)
254 static int read_chuukei_prf(concptr prf_name)
259 path_build(buf, sizeof(buf), ANGBAND_DIR_XTRA, prf_name);
260 fp = angband_fopen(buf, "r");
268 browse_delay = DEFAULT_DELAY;
270 while (0 == angband_fgets(fp, buf, sizeof(buf))) {
272 if (!strncmp(buf, "server:", 7)) {
273 strncpy(server_name, buf + 7, MAX_HOSTNAME - 1);
274 server_name[MAX_HOSTNAME - 1] = '\0';
278 if (!strncmp(buf, "port:", 5)) {
279 server_port = atoi(buf + 5);
283 if (!strncmp(buf, "delay:", 6)) {
284 browse_delay = atoi(buf + 6);
291 if (server_port == -1 || server_name[0] == 0)
297 int connect_chuukei_server(char *prf_name)
301 WORD wVersionRequested = (WORD)((1) | (1 << 8));
304 struct sockaddr_in ask;
307 if (read_chuukei_prf(prf_name) < 0) {
308 printf("Wrong prf file\n");
312 if (init_buffer() < 0) {
313 printf("Malloc error\n");
318 if (WSAStartup(wVersionRequested, &wsaData)) {
319 msg_print("Report: WSAStartup failed.");
324 printf("server = %s\nport = %d\n", server_name, server_port);
326 if ((hp = gethostbyname(server_name)) != NULL) {
327 memset(&ask, 0, sizeof(ask));
328 memcpy(&ask.sin_addr, hp->h_addr_list[0], hp->h_length);
330 if ((ask.sin_addr.s_addr = inet_addr(server_name)) == 0) {
331 printf("Bad hostname\n");
336 ask.sin_family = AF_INET;
337 ask.sin_port = htons((unsigned short)server_port);
340 if ((sd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
342 if ((sd = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
345 printf("Can't create socket\n");
349 if (connect(sd, (struct sockaddr *)&ask, sizeof(ask)) < 0) {
351 printf("Can't connect %s port %d\n", server_name, server_port);
359 /* strが同じ文字の繰り返しかどうか調べる */
360 static bool string_is_repeat(char *str, int len)
372 for (i = 1; i < len; i++) {
374 if (c != str[i] || iskanji(str[i]))
385 static errr send_text_to_chuukei_server(TERM_LEN x, TERM_LEN y, int len, TERM_COLOR col, concptr str)
390 strncpy(buf2, str, len);
394 sprintf(buf, "s%c%c%c%c", x + 1, y + 1, col, buf2[0]);
395 } else if (string_is_repeat(buf2, len)) {
397 for (i = len; i > 0; i -= 127) {
398 sprintf(buf, "n%c%c%c%c%c", x + 1, y + 1, MIN(i, 127), col, buf2[0]);
401 #if defined(SJIS) && defined(JP)
404 sprintf(buf, "t%c%c%c%c%s", x + 1, y + 1, len, col, buf2);
409 return (*old_text_hook)(x, y, len, col, str);
412 static errr send_wipe_to_chuukei_server(int x, int y, int len)
416 sprintf(buf, "w%c%c%c", x + 1, y + 1, len);
420 return (*old_wipe_hook)(x, y, len);
423 static errr send_xtra_to_chuukei_server(int n, int v)
427 if (n == TERM_XTRA_CLEAR || n == TERM_XTRA_FRESH || n == TERM_XTRA_SHAPE) {
428 sprintf(buf, "x%c", n + 1);
432 if (n == TERM_XTRA_FRESH) {
433 sprintf(buf, "d%ld", get_current_time() - epoch_time);
438 /* Verify the hook */
442 return (*old_xtra_hook)(n, v);
445 static errr send_curs_to_chuukei_server(int x, int y)
449 sprintf(buf, "c%c%c", x + 1, y + 1);
453 return (*old_curs_hook)(x, y);
456 static errr send_bigcurs_to_chuukei_server(int x, int y)
460 sprintf(buf, "C%c%c", x + 1, y + 1);
464 return (*old_bigcurs_hook)(x, y);
468 * Prepare z-term hooks to call send_*_to_chuukei_server()'s
470 void prepare_chuukei_hooks(void)
472 term_type *t0 = angband_term[0];
474 /* Save original z-term hooks */
475 old_xtra_hook = t0->xtra_hook;
476 old_curs_hook = t0->curs_hook;
477 old_bigcurs_hook = t0->bigcurs_hook;
478 old_wipe_hook = t0->wipe_hook;
479 old_text_hook = t0->text_hook;
481 /* Prepare z-term hooks */
482 t0->xtra_hook = send_xtra_to_chuukei_server;
483 t0->curs_hook = send_curs_to_chuukei_server;
484 t0->bigcurs_hook = send_bigcurs_to_chuukei_server;
485 t0->wipe_hook = send_wipe_to_chuukei_server;
486 t0->text_hook = send_text_to_chuukei_server;
490 * Prepare z-term hooks to call send_*_to_chuukei_server()'s
492 void prepare_movie_hooks(player_type *player_ptr)
501 disable_chuukei_server();
503 disable_chuukei_server();
506 msg_print(_("録画を終了しました。", "Stopped recording."));
508 sprintf(tmp, "%s.amv", player_ptr->base_name);
509 if (get_string(_("ムービー記録ファイル: ", "Movie file name: "), tmp, 80)) {
512 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, tmp);
514 fd = fd_open(buf, O_RDONLY);
522 (void)sprintf(out_val, _("現存するファイルに上書きしますか? (%s)", "Replace existing file %s? "), buf);
525 if (!get_check(out_val))
528 movie_fd = fd_open(buf, O_WRONLY | O_TRUNC);
530 movie_fd = fd_make(buf, 0644);
534 msg_print(_("ファイルを開けません!", "Can not open file."));
541 prepare_chuukei_hooks();
543 prepare_chuukei_hooks();
545 do_cmd_redraw(player_ptr);
551 static int handle_timestamp_data(int timestamp)
553 long current_time = get_current_time();
556 if (fresh_queue.tail == fresh_queue.next) {
557 /* バッファリングし始めの時間を保存しておく */
558 epoch_time = current_time;
559 epoch_time += browse_delay;
560 epoch_time -= timestamp;
561 time_diff = current_time - timestamp;
564 /* 描画キューに保存し、保存位置を進める */
565 fresh_queue.time[fresh_queue.tail] = timestamp;
568 /* キューの最後尾に到達したら先頭に戻す */
569 fresh_queue.tail %= FRESH_QUEUE_SIZE;
571 if (fresh_queue.tail == fresh_queue.next) {
573 prt("描画タイミングキューが溢れました。サーバとの接続を切断します。", 0, 0);
581 if (time_diff != current_time - timestamp) {
582 long old_time_diff = time_diff;
583 time_diff = current_time - timestamp;
584 epoch_time -= (old_time_diff - time_diff);
592 static int handle_movie_timestamp_data(int timestamp)
594 static int initialized = FALSE;
598 /* バッファリングし始めの時間を保存しておく */
599 epoch_time = get_current_time();
600 epoch_time += browse_delay;
601 epoch_time -= timestamp;
602 // time_diff = current_time - timestamp;
606 /* 描画キューに保存し、保存位置を進める */
607 fresh_queue.time[fresh_queue.tail] = timestamp;
610 /* キューの最後尾に到達したら先頭に戻す */
611 fresh_queue.tail %= FRESH_QUEUE_SIZE;
618 static int read_sock(void)
620 static char recv_buf[RECVBUF_SIZE];
621 static int remain_bytes = 0;
625 /* 前回残ったデータの後につづけて配信サーバからデータ受信 */
626 recv_bytes = recv(sd, recv_buf + remain_bytes, RECVBUF_SIZE - remain_bytes, 0);
631 /* 前回残ったデータ量に今回読んだデータ量を追加 */
632 remain_bytes += recv_bytes;
634 for (i = 0; i < remain_bytes; i++) {
635 /* データのくぎり('\0')を探す */
636 if (recv_buf[i] == '\0') {
637 /* 'd'で始まるデータ(タイムスタンプ)の場合は
639 if ((recv_buf[0] == 'd') && (handle_timestamp_data(atoi(recv_buf + 1)) < 0))
643 if (insert_ringbuf(recv_buf) < 0)
646 /* 次のデータ移行をrecv_bufの先頭に移動 */
647 memmove(recv_buf, recv_buf + i + 1, remain_bytes - i - 1);
649 remain_bytes -= (i + 1);
658 static int read_movie_file(void)
660 static char recv_buf[RECVBUF_SIZE];
661 static int remain_bytes = 0;
665 recv_bytes = read(movie_fd, recv_buf + remain_bytes, RECVBUF_SIZE - remain_bytes);
670 /* 前回残ったデータ量に今回読んだデータ量を追加 */
671 remain_bytes += recv_bytes;
673 for (i = 0; i < remain_bytes; i++) {
674 /* データのくぎり('\0')を探す */
675 if (recv_buf[i] == '\0') {
676 /* 'd'で始まるデータ(タイムスタンプ)の場合は
678 if ((recv_buf[0] == 'd') && (handle_movie_timestamp_data(atoi(recv_buf + 1)) < 0))
682 if (insert_ringbuf(recv_buf) < 0)
685 /* 次のデータ移行をrecv_bufの先頭に移動 */
686 memmove(recv_buf, recv_buf + i + 1, remain_bytes - i - 1);
688 remain_bytes -= (i + 1);
697 /* Win版の床の中点と壁の豆腐をピリオドとシャープにする。*/
698 static void win2unix(int col, char *buf)
722 static bool get_nextbuf(char *buf)
727 *ptr = ring.buf[ring.rptr++];
729 if (ring.rptr == RINGBUF_SIZE)
741 /* プレイホストのマップが大きいときクライアントのマップもリサイズする */
742 static void update_term_size(int x, int y, int len)
746 term_get_size(&ox, &oy);
757 if (nx != ox || ny != oy)
761 static bool flush_ringbuf_client(void)
766 if (fresh_queue.next == fresh_queue.tail)
770 if (fresh_queue.time[fresh_queue.next] > get_current_time() - epoch_time)
773 /* 時間情報(区切り)が得られるまで書く */
774 while (get_nextbuf(buf)) {
779 unsigned char tmp1, tmp2, tmp3, tmp4;
782 sscanf(buf, "%c%c%c%c%c", &id, &tmp1, &tmp2, &tmp3, &tmp4);
798 #if defined(SJIS) && defined(JP)
801 update_term_size(x, y, len);
802 (void)((*angband_term[0]->text_hook)(x, y, len, (byte)col, mesg));
803 strncpy(&Term->scr->c[y][x], mesg, len);
804 for (i = x; i < x + len; i++) {
805 Term->scr->a[y][i] = col;
810 for (i = 1; i < len; i++) {
814 update_term_size(x, y, len);
815 (void)((*angband_term[0]->text_hook)(x, y, len, (byte)col, mesg));
816 strncpy(&Term->scr->c[y][x], mesg, len);
817 for (i = x; i < x + len; i++) {
818 Term->scr->a[y][i] = col;
823 update_term_size(x, y, 1);
824 (void)((*angband_term[0]->text_hook)(x, y, 1, (byte)col, mesg));
825 strncpy(&Term->scr->c[y][x], mesg, 1);
826 Term->scr->a[y][x] = col;
830 update_term_size(x, y, len);
831 (void)((*angband_term[0]->wipe_hook)(x, y, len));
835 if (x == TERM_XTRA_CLEAR)
837 (void)((*angband_term[0]->xtra_hook)(x, 0));
841 update_term_size(x, y, 1);
842 (void)((*angband_term[0]->curs_hook)(x, y));
845 update_term_size(x, y, 1);
846 (void)((*angband_term[0]->bigcurs_hook)(x, y));
852 if (fresh_queue.next == FRESH_QUEUE_SIZE)
853 fresh_queue.next = 0;
858 void browse_chuukei()
871 term_xtra(TERM_XTRA_REACT, 0);
875 struct timeval tmp_tv;
877 if (flush_ringbuf_client())
883 /* ソケットにデータが来ているかどうか調べる */
884 select(sd + 1, &tmp_fdset, (fd_set *)NULL, (fd_set *)NULL, &tmp_tv);
885 if (FD_ISSET(sd, &tmp_fdset) == 0) {
886 term_xtra(TERM_XTRA_FLUSH, 0);
890 if (read_sock() < 0) {
891 chuukei_client = FALSE;
894 /* 接続が切れた状態で書くべきデータがなくなっていたら終了 */
895 if (!chuukei_client && fresh_queue.next == fresh_queue.tail)
901 void prepare_browse_movie_aux(concptr filename)
903 movie_fd = fd_open(filename, O_RDONLY);
905 browsing_movie = TRUE;
910 void prepare_browse_movie(concptr filename)
913 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, filename);
915 prepare_browse_movie_aux(buf);
918 void browse_movie(void)
922 term_xtra(TERM_XTRA_REACT, 0);
924 while (read_movie_file() == 0) {
925 while (fresh_queue.next != fresh_queue.tail) {
926 if (!flush_ringbuf_client()) {
927 term_xtra(TERM_XTRA_FLUSH, 0);
929 /* ソケットにデータが来ているかどうか調べる */