6 2014 Deskull rearranged comment for Doxygen.
9 #include "system/angband.h"
11 #include "io/signal-handlers.h"
15 #include "cmd/cmd-dump.h"
16 #include "cmd/cmd-draw.h"
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
37 #include <arpa/inet.h>
43 #define MAX_HOSTNAME 256
46 #define RINGBUF_SIZE 1024*1024
47 #define FRESH_QUEUE_SIZE 4096
51 #define WAIT 100*1000 /* ブラウズ側のウエイト(us単位) */
53 #define DEFAULT_DELAY 50
54 #define RECVBUF_SIZE 1024
63 static long epoch_time; /* バッファ開始時刻 */
64 static int browse_delay; /* 表示するまでの時間(100ms単位)(この間にラグを吸収する) */
66 static int sd; /* ソケットのファイルディスクリプタ */
67 static long time_diff; /* プレイ側との時間のずれ(これを見ながらディレイを調整していく) */
68 static int server_port;
69 static GAME_TEXT server_name[MAX_HOSTNAME];
73 static int movie_mode;
77 #define close closesocket
81 /* 描画する時刻を覚えておくキュー構造体 */
84 int time[FRESH_QUEUE_SIZE];
102 static errr (*old_xtra_hook)(int n, int v);
103 static errr (*old_curs_hook)(int x, int y);
104 static errr (*old_bigcurs_hook)(int x, int y);
105 static errr (*old_wipe_hook)(int x, int y, int n);
106 static errr (*old_text_hook)(int x, int y, int n, TERM_COLOR a, concptr s);
108 static void disable_chuukei_server(void)
110 term *t = angband_term[0];
112 chuukei_server = FALSE;
114 t->xtra_hook = old_xtra_hook;
115 t->curs_hook = old_curs_hook;
116 t->bigcurs_hook = old_bigcurs_hook;
117 t->wipe_hook = old_wipe_hook;
118 t->text_hook = old_text_hook;
121 /* ANSI Cによればstatic変数は0で初期化されるが一応初期化する */
122 static errr init_buffer(void)
124 fresh_queue.next = fresh_queue.tail = 0;
125 ring.wptr = ring.rptr = ring.inlen = 0;
126 fresh_queue.time[0] = 0;
127 ring.buf = malloc(RINGBUF_SIZE);
128 if (ring.buf == NULL) return -1;
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);
147 /* リングバッファ構造体に buf の内容を加える */
148 static errr insert_ringbuf(char *buf)
151 len = strlen(buf) + 1; /* +1は終端文字分 */
155 fd_write(movie_fd, buf, len);
157 if (!chuukei_server) return 0;
164 if (ring.inlen + len >= RINGBUF_SIZE)
167 if (chuukei_server) disable_chuukei_server();
168 else chuukei_client = FALSE;
170 prt("送受信バッファが溢れました。サーバとの接続を切断します。", 0, 0);
179 if (ring.wptr + len < RINGBUF_SIZE)
181 memcpy(ring.buf + ring.wptr, buf, len);
184 /* バッファの終端までに収まらない(ピッタリ収まる場合も含む) */
187 int head = RINGBUF_SIZE - ring.wptr; /* 前半 */
188 int tail = len - head; /* 後半 */
190 memcpy(ring.buf + ring.wptr, buf, head);
191 memcpy(ring.buf, buf + head, tail);
202 void flush_ringbuf(void)
207 if (!chuukei_server) return;
209 if (ring.inlen == 0) return;
224 /* ソケットにデータを書き込めるかどうか調べる */
225 select(sd+1, (fd_set *)NULL, &tmp_fdset, (fd_set *)NULL, &tv);
228 if (FD_ISSET(sd, &tmp_fdset) == 0) break;
230 result = send(sd, ring.buf + ring.rptr, ((ring.wptr > ring.rptr ) ? ring.wptr : RINGBUF_SIZE) - ring.rptr, 0);
235 if (chuukei_server) disable_chuukei_server();
237 prt("サーバとの接続が切断されました。", 0, 0);
245 ring.inlen -= result;
247 if (ring.rptr == RINGBUF_SIZE) ring.rptr = 0;
248 if (ring.inlen == 0) break;
253 static int read_chuukei_prf(concptr prf_name)
258 path_build(buf, sizeof(buf), ANGBAND_DIR_XTRA, prf_name);
259 fp = my_fopen(buf, "r");
266 browse_delay = DEFAULT_DELAY;
268 while (0 == my_fgets(fp, buf, sizeof(buf)))
271 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))
280 server_port = atoi(buf + 5);
284 if (!strncmp(buf, "delay:", 6))
286 browse_delay = atoi(buf + 6);
293 if (server_port == -1 || server_name[0] == 0) return -1;
298 int connect_chuukei_server(char *prf_name)
302 WORD wVersionRequested = (WORD) (( 1) | ( 1 << 8));
305 struct sockaddr_in ask;
308 if (read_chuukei_prf(prf_name) < 0)
310 printf("Wrong prf file\n");
314 if (init_buffer() < 0)
316 printf("Malloc error\n");
321 if (WSAStartup(wVersionRequested, &wsaData))
323 msg_print("Report: WSAStartup failed.");
328 printf("server = %s\nport = %d\n", server_name, server_port);
330 if ((hp = gethostbyname(server_name)) != NULL)
332 memset(&ask, 0, sizeof(ask));
333 memcpy(&ask.sin_addr, hp->h_addr_list[0], hp->h_length);
337 if ((ask.sin_addr.s_addr=inet_addr(server_name)) == 0)
339 printf("Bad hostname\n");
344 ask.sin_family = AF_INET;
345 ask.sin_port = htons((unsigned short)server_port);
348 if ((sd=socket(PF_INET,SOCK_STREAM, 0)) < 0)
350 if ((sd=socket(PF_INET,SOCK_STREAM, 0)) == INVALID_SOCKET)
353 printf("Can't create socket\n");
357 if (connect(sd, (struct sockaddr *)&ask, sizeof(ask)) < 0)
360 printf("Can't connect %s port %d\n", server_name, server_port);
368 /* strが同じ文字の繰り返しかどうか調べる */
369 static bool string_is_repeat(char *str, int len)
374 if (len < 2) return FALSE;
376 if (iskanji(c)) return FALSE;
379 for (i = 1; i < len; i++)
382 if(c != str[i] || iskanji(str[i])) return FALSE;
384 if(c != str[i]) return FALSE;
391 static errr send_text_to_chuukei_server(TERM_LEN x, TERM_LEN y, int len, TERM_COLOR col, concptr str)
396 strncpy(buf2, str, len);
401 sprintf(buf, "s%c%c%c%c", x+1, y+1, col, buf2[0]);
403 else if(string_is_repeat(buf2, len))
406 for (i = len; i > 0; i -= 127)
408 sprintf(buf, "n%c%c%c%c%c", x+1, y+1, MIN(i, 127), col, buf2[0]);
413 #if defined(SJIS) && defined(JP)
416 sprintf(buf, "t%c%c%c%c%s", x+1, y+1, len, col, buf2);
421 return (*old_text_hook)(x, y, len, col, str);
424 static errr send_wipe_to_chuukei_server(int x, int y, int len)
428 sprintf(buf, "w%c%c%c", x+1, y+1, len);
432 return (*old_wipe_hook)(x, y, len);
435 static errr send_xtra_to_chuukei_server(int n, int v)
439 if (n == TERM_XTRA_CLEAR || n == TERM_XTRA_FRESH || n == TERM_XTRA_SHAPE)
441 sprintf(buf, "x%c", n+1);
445 if (n == TERM_XTRA_FRESH)
447 sprintf(buf, "d%ld", get_current_time() - epoch_time);
452 /* Verify the hook */
453 if (!old_xtra_hook) return -1;
455 return (*old_xtra_hook)(n, v);
458 static errr send_curs_to_chuukei_server(int x, int y)
462 sprintf(buf, "c%c%c", x+1, y+1);
466 return (*old_curs_hook)(x, y);
469 static errr send_bigcurs_to_chuukei_server(int x, int y)
473 sprintf(buf, "C%c%c", x+1, y+1);
477 return (*old_bigcurs_hook)(x, y);
482 * Prepare z-term hooks to call send_*_to_chuukei_server()'s
484 void prepare_chuukei_hooks(void)
486 term *t0 = angband_term[0];
488 /* Save original z-term hooks */
489 old_xtra_hook = t0->xtra_hook;
490 old_curs_hook = t0->curs_hook;
491 old_bigcurs_hook = t0->bigcurs_hook;
492 old_wipe_hook = t0->wipe_hook;
493 old_text_hook = t0->text_hook;
495 /* Prepare z-term hooks */
496 t0->xtra_hook = send_xtra_to_chuukei_server;
497 t0->curs_hook = send_curs_to_chuukei_server;
498 t0->bigcurs_hook = send_bigcurs_to_chuukei_server;
499 t0->wipe_hook = send_wipe_to_chuukei_server;
500 t0->text_hook = send_text_to_chuukei_server;
505 * Prepare z-term hooks to call send_*_to_chuukei_server()'s
507 void prepare_movie_hooks(void)
516 if (!chuukei_server) disable_chuukei_server();
518 disable_chuukei_server();
521 msg_print(_("録画を終了しました。", "Stopped recording."));
525 sprintf(tmp, "%s.amv", p_ptr->base_name);
526 if (get_string(_("ムービー記録ファイル: ", "Movie file name: "), tmp, 80))
530 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, tmp);
532 fd = fd_open(buf, O_RDONLY);
541 (void)sprintf(out_val, _("現存するファイルに上書きしますか? (%s)", "Replace existing file %s? "), buf);
544 if (!get_check(out_val)) return;
546 movie_fd = fd_open(buf, O_WRONLY | O_TRUNC);
550 movie_fd = fd_make(buf, 0644);
555 msg_print(_("ファイルを開けません!", "Can not open file."));
561 if (!chuukei_server) prepare_chuukei_hooks();
563 prepare_chuukei_hooks();
565 do_cmd_redraw(p_ptr);
571 static int handle_timestamp_data(int timestamp)
573 long current_time = get_current_time();
576 if (fresh_queue.tail == fresh_queue.next)
578 /* バッファリングし始めの時間を保存しておく */
579 epoch_time = current_time;
580 epoch_time += browse_delay;
581 epoch_time -= timestamp;
582 time_diff = current_time - timestamp;
585 /* 描画キューに保存し、保存位置を進める */
586 fresh_queue.time[fresh_queue.tail] = timestamp;
589 /* キューの最後尾に到達したら先頭に戻す */
590 fresh_queue.tail %= FRESH_QUEUE_SIZE;
592 if (fresh_queue.tail == fresh_queue.next)
595 prt("描画タイミングキューが溢れました。サーバとの接続を切断します。", 0, 0);
603 if (time_diff != current_time - timestamp)
605 long old_time_diff = time_diff;
606 time_diff = current_time - timestamp;
607 epoch_time -= (old_time_diff - time_diff);
615 static int handle_movie_timestamp_data(int timestamp)
617 static int initialized = FALSE;
622 /* バッファリングし始めの時間を保存しておく */
623 epoch_time = get_current_time();
624 epoch_time += browse_delay;
625 epoch_time -= timestamp;
626 //time_diff = current_time - timestamp;
630 /* 描画キューに保存し、保存位置を進める */
631 fresh_queue.time[fresh_queue.tail] = timestamp;
634 /* キューの最後尾に到達したら先頭に戻す */
635 fresh_queue.tail %= FRESH_QUEUE_SIZE;
642 static int read_sock(void)
644 static char recv_buf[RECVBUF_SIZE];
645 static int remain_bytes = 0;
649 /* 前回残ったデータの後につづけて配信サーバからデータ受信 */
650 recv_bytes = recv(sd, recv_buf + remain_bytes, RECVBUF_SIZE - remain_bytes, 0);
655 /* 前回残ったデータ量に今回読んだデータ量を追加 */
656 remain_bytes += recv_bytes;
658 for (i = 0; i < remain_bytes; i ++)
660 /* データのくぎり('\0')を探す */
661 if (recv_buf[i] == '\0')
663 /* 'd'で始まるデータ(タイムスタンプ)の場合は
665 if ((recv_buf[0] == 'd') &&
666 (handle_timestamp_data(atoi(recv_buf + 1)) < 0))
670 if (insert_ringbuf(recv_buf) < 0)
673 /* 次のデータ移行をrecv_bufの先頭に移動 */
674 memmove(recv_buf, recv_buf + i + 1, remain_bytes - i - 1);
676 remain_bytes -= (i+1);
685 static int read_movie_file(void)
687 static char recv_buf[RECVBUF_SIZE];
688 static int remain_bytes = 0;
692 recv_bytes = read(movie_fd, recv_buf + remain_bytes, RECVBUF_SIZE - remain_bytes);
697 /* 前回残ったデータ量に今回読んだデータ量を追加 */
698 remain_bytes += recv_bytes;
700 for (i = 0; i < remain_bytes; i ++)
702 /* データのくぎり('\0')を探す */
703 if (recv_buf[i] == '\0')
705 /* 'd'で始まるデータ(タイムスタンプ)の場合は
707 if ((recv_buf[0] == 'd') &&
708 (handle_movie_timestamp_data(atoi(recv_buf + 1)) < 0))
712 if (insert_ringbuf(recv_buf) < 0)
715 /* 次のデータ移行をrecv_bufの先頭に移動 */
716 memmove(recv_buf, recv_buf + i + 1, remain_bytes - i - 1);
718 remain_bytes -= (i+1);
728 /* Win版の床の中点と壁の豆腐をピリオドとシャープにする。*/
729 static void win2unix(int col, char *buf)
732 if ( col == 9 ) kabe = '%';
744 if (*buf == 127) *buf = kabe;
745 else if(*buf == 31) *buf = '.';
751 static bool get_nextbuf(char *buf)
757 *ptr = ring.buf[ring.rptr ++];
759 if (ring.rptr == RINGBUF_SIZE) ring.rptr = 0;
760 if (*ptr++ == '\0') break;
763 if (buf[0] == 'd') return FALSE;
768 /* プレイホストのマップが大きいときクライアントのマップもリサイズする */
769 static void update_term_size(int x, int y, int len)
773 Term_get_size(&ox, &oy);
778 if (x + len > ox) nx = x + len;
780 if (y + 1 > oy) ny = y + 1;
782 if (nx != ox || ny != oy) Term_resize(nx, ny);
785 static bool flush_ringbuf_client(void)
790 if (fresh_queue.next == fresh_queue.tail) return FALSE;
793 if (fresh_queue.time[fresh_queue.next] > get_current_time() - epoch_time) return FALSE;
795 /* 時間情報(区切り)が得られるまで書く */
796 while (get_nextbuf(buf))
802 unsigned char tmp1, tmp2, tmp3, tmp4;
805 sscanf(buf, "%c%c%c%c%c", &id, &tmp1, &tmp2, &tmp3, &tmp4);
806 x = tmp1-1; y = tmp2-1; len = tmp3; col = tmp4;
820 #if defined(SJIS) && defined(JP)
823 update_term_size(x, y, len);
824 (void)((*angband_term[0]->text_hook)(x, y, len, (byte)col, mesg));
825 strncpy(&Term->scr->c[y][x], mesg, len);
826 for (i = x; i < x+len; i++)
828 Term->scr->a[y][i] = col;
833 for (i = 1; i < len; i++)
838 update_term_size(x, y, len);
839 (void)((*angband_term[0]->text_hook)(x, y, len, (byte)col, mesg));
840 strncpy(&Term->scr->c[y][x], mesg, len);
841 for (i = x; i < x+len; i++)
843 Term->scr->a[y][i] = col;
848 update_term_size(x, y, 1);
849 (void)((*angband_term[0]->text_hook)(x, y, 1, (byte)col, mesg));
850 strncpy(&Term->scr->c[y][x], mesg, 1);
851 Term->scr->a[y][x] = col;
855 update_term_size(x, y, len);
856 (void)((*angband_term[0]->wipe_hook)(x, y, len));
860 if (x == TERM_XTRA_CLEAR) Term_clear();
861 (void)((*angband_term[0]->xtra_hook)(x, 0));
865 update_term_size(x, y, 1);
866 (void)((*angband_term[0]->curs_hook)(x, y));
869 update_term_size(x, y, 1);
870 (void)((*angband_term[0]->bigcurs_hook)(x, y));
876 if (fresh_queue.next == FRESH_QUEUE_SIZE) fresh_queue.next = 0;
881 void browse_chuukei()
894 Term_xtra(TERM_XTRA_REACT, 0);
899 struct timeval tmp_tv;
901 if (flush_ringbuf_client()) continue;
906 /* ソケットにデータが来ているかどうか調べる */
907 select(sd+1, &tmp_fdset, (fd_set *)NULL, (fd_set *)NULL, &tmp_tv);
908 if (FD_ISSET(sd, &tmp_fdset) == 0)
910 Term_xtra(TERM_XTRA_FLUSH, 0);
916 chuukei_client = FALSE;
919 /* 接続が切れた状態で書くべきデータがなくなっていたら終了 */
920 if (!chuukei_client && fresh_queue.next == fresh_queue.tail ) break;
925 void prepare_browse_movie_aux(concptr filename)
927 movie_fd = fd_open(filename, O_RDONLY);
929 browsing_movie = TRUE;
934 void prepare_browse_movie(concptr filename)
937 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, filename);
939 prepare_browse_movie_aux(buf);
942 void browse_movie(void)
946 Term_xtra(TERM_XTRA_REACT, 0);
948 while (read_movie_file() == 0)
950 while (fresh_queue.next != fresh_queue.tail)
952 if (!flush_ringbuf_client())
954 Term_xtra(TERM_XTRA_FLUSH, 0);
956 /* ソケットにデータが来ているかどうか調べる */