OSDN Git Service

[Refactor] クラシックMac (ver9.Xまで)のサポート打ち切り / Finished support for Classic Mac (Up to...
[hengband/hengband.git] / src / chuukei.c
1 /*!
2     @file chuukei.c
3     @brief 中継機能の実装
4     @date 2014/01/02
5     @author
6     2014 Deskull rearranged comment for Doxygen.
7  */
8
9 #include "angband.h"
10
11 #include "files.h"
12 #include "util.h"
13 #include "term.h"
14 #include "cmd-dump.h"
15 #include "inet.h"
16 #include "japanese.h"
17 #include "term.h"
18
19 #include <stdio.h>
20 #include <stdarg.h>
21 #include <ctype.h>
22 #ifdef WINDOWS
23 #include <windows.h>
24 #endif
25
26 #ifdef CHUUKEI
27 #if defined(WINDOWS)
28 #include <winsock.h>
29 #else
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <netdb.h>
34 #include <sys/time.h>
35 #include <arpa/inet.h>
36
37 #include <setjmp.h>
38 #include <signal.h>
39 #endif
40
41 #define MAX_HOSTNAME 256
42 #endif
43
44 #define RINGBUF_SIZE 1024*1024
45 #define FRESH_QUEUE_SIZE 4096
46 #ifdef WINDOWS
47 #define WAIT 100
48 #else
49 #define WAIT 100*1000 /* ブラウズ側のウエイト(us単位) */
50 #endif
51 #define DEFAULT_DELAY 50
52 #define RECVBUF_SIZE 1024
53
54 #ifdef CHUUKEI
55 bool chuukei_server;
56 bool chuukei_client;
57 char *server_name;
58 int server_port;
59 #endif
60
61 static long epoch_time;  /* バッファ開始時刻 */
62 static int browse_delay; /* 表示するまでの時間(100ms単位)(この間にラグを吸収する) */
63 #ifdef CHUUKEI
64 static int sd; /* ソケットのファイルディスクリプタ */
65 static long time_diff;   /* プレイ側との時間のずれ(これを見ながらディレイを調整していく) */
66 static int server_port;
67 static GAME_TEXT server_name[MAX_HOSTNAME];
68 #endif
69
70 static int movie_fd;
71 static int movie_mode;
72
73 #ifdef CHUUKEI
74 #ifdef WINDOWS
75 #define close closesocket
76 #endif
77 #endif
78
79 /* 描画する時刻を覚えておくキュー構造体 */
80 static struct
81 {
82         int time[FRESH_QUEUE_SIZE];
83         int next;
84         int tail;
85 }fresh_queue;
86
87
88 /* リングバッファ構造体 */
89 static struct
90 {
91         char *buf;
92         int wptr;
93         int rptr;
94         int inlen;
95 }ring;
96
97 /*
98  * Original hooks
99  */
100 static errr (*old_xtra_hook)(int n, int v);
101 static errr (*old_curs_hook)(int x, int y);
102 static errr (*old_bigcurs_hook)(int x, int y);
103 static errr (*old_wipe_hook)(int x, int y, int n);
104 static errr (*old_text_hook)(int x, int y, int n, TERM_COLOR a, concptr s);
105
106 static void disable_chuukei_server(void)
107 {
108         term *t = angband_term[0];
109 #ifdef CHUUKEI
110         chuukei_server = FALSE;
111 #endif /* CHUUKEI */
112         t->xtra_hook = old_xtra_hook;
113         t->curs_hook = old_curs_hook;
114         t->bigcurs_hook = old_bigcurs_hook;
115         t->wipe_hook = old_wipe_hook;
116         t->text_hook = old_text_hook;
117 }
118
119 /* ANSI Cによればstatic変数は0で初期化されるが一応初期化する */
120 static errr init_buffer(void)
121 {
122         fresh_queue.next = fresh_queue.tail = 0;
123         ring.wptr = ring.rptr = ring.inlen = 0;
124         fresh_queue.time[0] = 0;
125         ring.buf = malloc(RINGBUF_SIZE);
126         if (ring.buf == NULL) return -1;
127
128         return 0;
129 }
130
131 /* 現在の時間を100ms単位で取得する */
132 static long get_current_time(void)
133 {
134 #ifdef WINDOWS
135         return timeGetTime() / 100;
136 #else
137         struct timeval tv;
138         gettimeofday(&tv, NULL);
139
140         return (tv.tv_sec * 10 + tv.tv_usec / 100000);
141 #endif
142 }
143
144
145 /* リングバッファ構造体に buf の内容を加える */
146 static errr insert_ringbuf(char *buf)
147 {
148         int len;
149         len = strlen(buf) + 1; /* +1は終端文字分 */
150
151         if (movie_mode)
152         {
153                 fd_write(movie_fd, buf, len);
154 #ifdef CHUUKEI
155                 if (!chuukei_server) return 0;
156 #else
157                 return 0;
158 #endif
159         }
160
161         /* バッファをオーバー */
162         if (ring.inlen + len >= RINGBUF_SIZE)
163         {
164 #ifdef CHUUKEI
165                 if (chuukei_server) disable_chuukei_server();
166                 else chuukei_client = FALSE;
167
168                 prt("送受信バッファが溢れました。サーバとの接続を切断します。", 0, 0);
169                 inkey();
170
171                 close(sd);
172 #endif
173                 return -1;
174         }
175
176         /* バッファの終端までに収まる */
177         if (ring.wptr + len < RINGBUF_SIZE)
178         {
179                 memcpy(ring.buf + ring.wptr, buf, len);
180                 ring.wptr += len;
181         }
182         /* バッファの終端までに収まらない(ピッタリ収まる場合も含む) */
183         else
184         {
185                 int head = RINGBUF_SIZE - ring.wptr;  /* 前半 */
186                 int tail = len - head;               /* 後半 */
187
188                 memcpy(ring.buf + ring.wptr, buf, head);
189                 memcpy(ring.buf, buf + head, tail);
190                 ring.wptr = tail;
191         }
192
193         ring.inlen += len;
194
195         /* Success */
196         return 0;
197 }
198
199 #ifdef CHUUKEI
200 void flush_ringbuf(void)
201 {
202         fd_set fdset;
203         struct timeval tv;
204
205         if (!chuukei_server) return;
206
207         if (ring.inlen == 0) return;
208
209         tv.tv_sec = 0;
210         tv.tv_usec = 0;
211
212         FD_ZERO(&fdset);
213         FD_SET(sd, &fdset);
214
215         while (TRUE)
216         {
217                 fd_set tmp_fdset;
218                 int result;
219
220                 tmp_fdset = fdset;
221
222                 /* ソケットにデータを書き込めるかどうか調べる */
223                 select(sd+1, (fd_set *)NULL, &tmp_fdset, (fd_set *)NULL, &tv);
224
225                 /* 書き込めなければ戻る */
226                 if (FD_ISSET(sd, &tmp_fdset) == 0) break;
227
228                 result = send(sd, ring.buf + ring.rptr, ((ring.wptr > ring.rptr ) ? ring.wptr : RINGBUF_SIZE) - ring.rptr, 0);
229
230                 if (result <= 0)
231                 {
232                         /* サーバとの接続断? */
233                         if (chuukei_server) disable_chuukei_server();
234
235                         prt("サーバとの接続が切断されました。", 0, 0);
236                         inkey();
237                         close(sd);
238
239                         return;
240                 }
241
242                 ring.rptr += result;
243                 ring.inlen -= result;
244
245                 if (ring.rptr == RINGBUF_SIZE) ring.rptr = 0;
246                 if (ring.inlen == 0) break;
247         }
248 }
249
250
251 static int read_chuukei_prf(concptr prf_name)
252 {
253         char buf[1024];
254         FILE *fp;
255
256         path_build(buf, sizeof(buf), ANGBAND_DIR_XTRA, prf_name);
257         fp = my_fopen(buf, "r");
258
259         if (!fp) return -1;
260
261         /* 初期化 */
262         server_port = -1;
263         server_name[0] = 0;
264         browse_delay = DEFAULT_DELAY;
265
266         while (0 == my_fgets(fp, buf, sizeof(buf)))
267         {
268                 /* サーバ名 */
269                 if (!strncmp(buf, "server:", 7))
270                 {
271                         strncpy(server_name, buf + 7, MAX_HOSTNAME - 1);
272                         server_name[MAX_HOSTNAME - 1] = '\0';
273                 }
274
275                 /* ポート番号 */
276                 if (!strncmp(buf, "port:", 5))
277                 {
278                         server_port = atoi(buf + 5);
279                 }
280
281                 /* ディレイ */
282                 if (!strncmp(buf, "delay:", 6))
283                 {
284                         browse_delay = atoi(buf + 6);
285                 }
286         }
287
288         my_fclose(fp);
289
290         /* prfファイルが完全でない */
291         if (server_port == -1 || server_name[0] == 0) return -1;
292
293         return 0;
294 }
295
296 int connect_chuukei_server(char *prf_name)
297 {
298 #ifdef WINDOWS
299         WSADATA wsaData;
300         WORD wVersionRequested = (WORD) (( 1) |  ( 1 << 8));
301 #endif
302
303         struct sockaddr_in ask;
304         struct hostent *hp;
305
306         if (read_chuukei_prf(prf_name) < 0)
307         {
308                 printf("Wrong prf file\n");
309                 return -1;
310         }
311
312         if (init_buffer() < 0)
313         {
314                 printf("Malloc error\n");
315                 return -1;
316         }
317
318 #ifdef WINDOWS
319         if (WSAStartup(wVersionRequested, &wsaData))
320         {
321                 msg_print("Report: WSAStartup failed.");
322                 return -1;
323         }
324 #endif
325
326         printf("server = %s\nport = %d\n", server_name, server_port);
327
328         if ((hp = gethostbyname(server_name)) != NULL)
329         {
330                 memset(&ask, 0, sizeof(ask));
331                 memcpy(&ask.sin_addr, hp->h_addr_list[0], hp->h_length);
332         }
333         else
334         {
335                 if ((ask.sin_addr.s_addr=inet_addr(server_name)) == 0)
336                 {
337                         printf("Bad hostname\n");
338                         return -1;
339                 }
340         }
341
342         ask.sin_family = AF_INET;
343         ask.sin_port = htons((unsigned short)server_port);
344
345 #ifndef WINDOWS
346         if ((sd=socket(PF_INET,SOCK_STREAM, 0)) < 0)
347 #else
348         if ((sd=socket(PF_INET,SOCK_STREAM, 0)) == INVALID_SOCKET)
349 #endif
350         {
351                 printf("Can't create socket\n");
352                 return -1;
353         }
354
355         if (connect(sd, (struct sockaddr *)&ask, sizeof(ask)) < 0)
356         {
357                 close(sd);
358                 printf("Can't connect %s port %d\n", server_name, server_port);
359                 return -1;
360         }
361
362         return 0;
363 }
364 #endif /* CHUUKEI */
365
366 /* strが同じ文字の繰り返しかどうか調べる */
367 static bool string_is_repeat(char *str, int len)
368 {
369         char c = str[0];
370         int i;
371
372         if (len < 2) return FALSE;
373 #ifdef JP
374         if (iskanji(c)) return FALSE;
375 #endif
376
377         for (i = 1; i < len; i++)
378         {
379 #ifdef JP
380                 if(c != str[i] || iskanji(str[i])) return FALSE;
381 #else
382                 if(c != str[i]) return FALSE;
383 #endif
384         }
385
386         return TRUE;
387 }
388
389 static errr send_text_to_chuukei_server(TERM_LEN x, TERM_LEN y, int len, TERM_COLOR col, concptr str)
390 {
391         char buf[1024];
392         char buf2[1024];
393
394         strncpy(buf2, str, len);
395         buf2[len] = '\0';
396
397         if (len == 1)
398         {
399                 sprintf(buf, "s%c%c%c%c", x+1, y+1, col, buf2[0]);
400         }
401         else if(string_is_repeat(buf2, len))
402         {
403                 int i;
404                 for (i = len; i > 0; i -= 127)
405                 {
406                         sprintf(buf, "n%c%c%c%c%c", x+1, y+1, MIN(i, 127), col, buf2[0]);
407                 }
408         }
409         else
410         {
411 #if defined(SJIS) && defined(JP)
412                 sjis2euc(buf2);
413 #endif
414                 sprintf(buf, "t%c%c%c%c%s", x+1, y+1, len, col, buf2);
415         }
416
417         insert_ringbuf(buf);
418
419         return (*old_text_hook)(x, y, len, col, str);
420 }
421
422 static errr send_wipe_to_chuukei_server(int x, int y, int len)
423 {
424         char buf[1024];
425
426         sprintf(buf, "w%c%c%c", x+1, y+1, len);
427
428         insert_ringbuf(buf);
429
430         return (*old_wipe_hook)(x, y, len);
431 }
432
433 static errr send_xtra_to_chuukei_server(int n, int v)
434 {
435         char buf[1024];
436
437         if (n == TERM_XTRA_CLEAR || n == TERM_XTRA_FRESH || n == TERM_XTRA_SHAPE)
438         {
439                 sprintf(buf, "x%c", n+1);
440                 
441                 insert_ringbuf(buf);
442                 
443                 if (n == TERM_XTRA_FRESH)
444                 {
445                         sprintf(buf, "d%ld", get_current_time() - epoch_time);
446                         insert_ringbuf(buf);
447                 }
448         }
449
450         /* Verify the hook */
451         if (!old_xtra_hook) return -1;
452
453         return (*old_xtra_hook)(n, v);
454 }
455
456 static errr send_curs_to_chuukei_server(int x, int y)
457 {
458         char buf[1024];
459
460         sprintf(buf, "c%c%c", x+1, y+1);
461
462         insert_ringbuf(buf);
463
464         return (*old_curs_hook)(x, y);
465 }
466
467 static errr send_bigcurs_to_chuukei_server(int x, int y)
468 {
469         char buf[1024];
470
471         sprintf(buf, "C%c%c", x+1, y+1);
472
473         insert_ringbuf(buf);
474
475         return (*old_bigcurs_hook)(x, y);
476 }
477
478
479 /*
480  * Prepare z-term hooks to call send_*_to_chuukei_server()'s
481  */
482 void prepare_chuukei_hooks(void)
483 {
484         term *t0 = angband_term[0];
485
486         /* Save original z-term hooks */
487         old_xtra_hook = t0->xtra_hook;
488         old_curs_hook = t0->curs_hook;
489         old_bigcurs_hook = t0->bigcurs_hook;
490         old_wipe_hook = t0->wipe_hook;
491         old_text_hook = t0->text_hook;
492
493         /* Prepare z-term hooks */
494         t0->xtra_hook = send_xtra_to_chuukei_server;
495         t0->curs_hook = send_curs_to_chuukei_server;
496         t0->bigcurs_hook = send_bigcurs_to_chuukei_server;
497         t0->wipe_hook = send_wipe_to_chuukei_server;
498         t0->text_hook = send_text_to_chuukei_server;
499 }
500
501
502 /*
503  * Prepare z-term hooks to call send_*_to_chuukei_server()'s
504  */
505 void prepare_movie_hooks(void)
506 {
507         char buf[1024];
508         char tmp[80];
509
510         if (movie_mode)
511         {
512                 movie_mode = 0;
513 #ifdef CHUUKEI
514                 if (!chuukei_server) disable_chuukei_server();
515 #else
516                 disable_chuukei_server();
517 #endif
518                 fd_close(movie_fd);
519                 msg_print(_("録画を終了しました。", "Stopped recording."));
520         }
521         else
522         {
523                 sprintf(tmp, "%s.amv", p_ptr->base_name);
524                 if (get_string(_("ムービー記録ファイル: ", "Movie file name: "), tmp, 80))
525                 {
526                         int fd;
527
528                         path_build(buf, sizeof(buf), ANGBAND_DIR_USER, tmp);
529
530                         fd = fd_open(buf, O_RDONLY);
531
532                         /* Existing file */
533                         if (fd >= 0)
534                         {
535                                 char out_val[160];
536                                 (void)fd_close(fd);
537
538                                 /* Build query */
539                                 (void)sprintf(out_val, _("現存するファイルに上書きしますか? (%s)", "Replace existing file %s? "), buf);
540
541                                 /* Ask */
542                                 if (!get_check(out_val)) return;
543
544                                 movie_fd = fd_open(buf, O_WRONLY | O_TRUNC);
545                         }
546                         else
547                         {
548                                 movie_fd = fd_make(buf, 0644);
549                         }
550
551                         if (!movie_fd)
552                         {
553                                 msg_print(_("ファイルを開けません!", "Can not open file."));
554                                 return;
555                         }
556
557                         movie_mode = 1;
558 #ifdef CHUUKEI
559                         if (!chuukei_server) prepare_chuukei_hooks();
560 #else
561                         prepare_chuukei_hooks();
562 #endif
563                         do_cmd_redraw(p_ptr);
564                 }
565         }
566 }
567
568 #ifdef CHUUKEI
569 static int handle_timestamp_data(int timestamp)
570 {
571         long current_time = get_current_time();
572
573         /* 描画キューは空かどうか? */
574         if (fresh_queue.tail == fresh_queue.next)
575         {
576                 /* バッファリングし始めの時間を保存しておく */
577                 epoch_time = current_time;
578                 epoch_time += browse_delay;
579                 epoch_time -= timestamp;
580                 time_diff = current_time - timestamp;
581         }
582
583         /* 描画キューに保存し、保存位置を進める */
584         fresh_queue.time[fresh_queue.tail] = timestamp;
585         fresh_queue.tail ++;
586
587         /* キューの最後尾に到達したら先頭に戻す */
588         fresh_queue.tail %= FRESH_QUEUE_SIZE;
589
590         if (fresh_queue.tail == fresh_queue.next)
591         {
592                 /* 描画キュー溢れ */
593                 prt("描画タイミングキューが溢れました。サーバとの接続を切断します。", 0, 0);
594                 inkey();
595                 close(sd);
596
597                 return -1;
598         }
599
600         /* プレイ側とのディレイを調整 */
601         if (time_diff != current_time - timestamp)
602         {
603                 long old_time_diff = time_diff;
604                 time_diff = current_time - timestamp;
605                 epoch_time -= (old_time_diff - time_diff);
606         }
607
608         /* Success */
609         return 0;
610 }
611 #endif /* CHUUKEI */
612
613 static int handle_movie_timestamp_data(int timestamp)
614 {
615         static int initialized = FALSE;
616
617         /* 描画キューは空かどうか? */
618         if (!initialized)
619         {
620                 /* バッファリングし始めの時間を保存しておく */
621                 epoch_time = get_current_time();
622                 epoch_time += browse_delay;
623                 epoch_time -= timestamp;
624                 //time_diff = current_time - timestamp;
625                 initialized = TRUE;
626         }
627
628         /* 描画キューに保存し、保存位置を進める */
629         fresh_queue.time[fresh_queue.tail] = timestamp;
630         fresh_queue.tail ++;
631
632         /* キューの最後尾に到達したら先頭に戻す */
633         fresh_queue.tail %= FRESH_QUEUE_SIZE;
634
635         /* Success */
636         return 0;
637 }
638
639 #ifdef CHUUKEI
640 static int read_sock(void)
641 {
642         static char recv_buf[RECVBUF_SIZE];
643         static int remain_bytes = 0;
644         int recv_bytes;
645         int i;
646
647         /* 前回残ったデータの後につづけて配信サーバからデータ受信 */
648         recv_bytes = recv(sd, recv_buf + remain_bytes, RECVBUF_SIZE - remain_bytes, 0);
649
650         if (recv_bytes <= 0)
651                 return -1;
652
653         /* 前回残ったデータ量に今回読んだデータ量を追加 */
654         remain_bytes += recv_bytes;
655
656         for (i = 0; i < remain_bytes; i ++)
657         {
658                 /* データのくぎり('\0')を探す */
659                 if (recv_buf[i] == '\0')
660                 {
661                         /* 'd'で始まるデータ(タイムスタンプ)の場合は
662                            描画キューに保存する処理を呼ぶ */
663                         if ((recv_buf[0] == 'd') &&
664                             (handle_timestamp_data(atoi(recv_buf + 1)) < 0))
665                                 return -1;
666
667                         /* 受信データを保存 */
668                         if (insert_ringbuf(recv_buf) < 0) 
669                                 return -1;
670
671                         /* 次のデータ移行をrecv_bufの先頭に移動 */
672                         memmove(recv_buf, recv_buf + i + 1, remain_bytes - i - 1);
673
674                         remain_bytes -= (i+1);
675                         i = 0;
676                 }
677         }
678
679         return 0;
680 }
681 #endif
682
683 static int read_movie_file(void)
684 {
685         static char recv_buf[RECVBUF_SIZE];
686         static int remain_bytes = 0;
687         int recv_bytes;
688         int i;
689
690         recv_bytes = read(movie_fd, recv_buf + remain_bytes, RECVBUF_SIZE - remain_bytes);
691
692         if (recv_bytes <= 0)
693                 return -1;
694
695         /* 前回残ったデータ量に今回読んだデータ量を追加 */
696         remain_bytes += recv_bytes;
697
698         for (i = 0; i < remain_bytes; i ++)
699         {
700                 /* データのくぎり('\0')を探す */
701                 if (recv_buf[i] == '\0')
702                 {
703                         /* 'd'で始まるデータ(タイムスタンプ)の場合は
704                            描画キューに保存する処理を呼ぶ */
705                         if ((recv_buf[0] == 'd') &&
706                             (handle_movie_timestamp_data(atoi(recv_buf + 1)) < 0))
707                                 return -1;
708
709                         /* 受信データを保存 */
710                         if (insert_ringbuf(recv_buf) < 0) 
711                                 return -1;
712
713                         /* 次のデータ移行をrecv_bufの先頭に移動 */
714                         memmove(recv_buf, recv_buf + i + 1, remain_bytes - i - 1);
715
716                         remain_bytes -= (i+1);
717                         i = 0;
718                 }
719         }
720
721         return 0;
722 }
723
724
725 #ifndef WINDOWS
726 /* Win版の床の中点と壁の豆腐をピリオドとシャープにする。*/
727 static void win2unix(int col, char *buf)
728 {
729         char kabe;
730         if ( col == 9 ) kabe = '%';
731         else            kabe = '#';
732
733         while (*buf)
734         {
735 #ifdef JP
736                 if (iskanji(*buf))
737                 {
738                         buf += 2;
739                         continue;
740                 }
741 #endif
742                 if (*buf == 127) *buf = kabe;
743                 else if(*buf == 31) *buf = '.';
744                 buf++;
745         }
746 }
747 #endif
748
749 static bool get_nextbuf(char *buf)
750 {
751         char *ptr = buf;
752
753         while (TRUE)
754         {
755                 *ptr = ring.buf[ring.rptr ++];
756                 ring.inlen --;
757                 if (ring.rptr == RINGBUF_SIZE) ring.rptr = 0;
758                 if (*ptr++ == '\0') break;
759         }
760
761         if (buf[0] == 'd') return FALSE;
762
763         return TRUE;
764 }
765
766 /* プレイホストのマップが大きいときクライアントのマップもリサイズする */
767 static void update_term_size(int x, int y, int len)
768 {
769         int ox, oy;
770         int nx, ny;
771         Term_get_size(&ox, &oy);
772         nx = ox;
773         ny = oy;
774
775         /* 横方向のチェック */
776         if (x + len > ox) nx = x + len;
777         /* 縦方向のチェック */
778         if (y + 1 > oy) ny = y + 1;
779
780         if (nx != ox || ny != oy) Term_resize(nx, ny);
781 }
782
783 static bool flush_ringbuf_client(void)
784 {
785         char buf[1024];
786
787         /* 書くデータなし */
788         if (fresh_queue.next == fresh_queue.tail) return FALSE;
789
790         /* まだ書くべき時でない */
791         if (fresh_queue.time[fresh_queue.next] > get_current_time() - epoch_time) return FALSE;
792
793         /* 時間情報(区切り)が得られるまで書く */
794         while (get_nextbuf(buf))
795         {
796                 char id;
797                 int x, y, len;
798                 TERM_COLOR col;
799                 int i;
800                 unsigned char tmp1, tmp2, tmp3, tmp4;
801                 char *mesg;
802
803                 sscanf(buf, "%c%c%c%c%c", &id, &tmp1, &tmp2, &tmp3, &tmp4);
804                 x = tmp1-1; y = tmp2-1; len = tmp3; col = tmp4;
805                 if (id == 's')
806                 {
807                         col = tmp3;
808                         mesg = &buf[4];
809                 }
810                 else mesg = &buf[5];
811 #ifndef WINDOWS
812                 win2unix(col, mesg);
813 #endif
814
815                 switch (id)
816                 {
817                 case 't': /* 通常 */
818 #if defined(SJIS) && defined(JP)
819                         euc2sjis(mesg);
820 #endif
821                         update_term_size(x, y, len);
822                         (void)((*angband_term[0]->text_hook)(x, y, len, (byte)col, mesg));
823                         strncpy(&Term->scr->c[y][x], mesg, len);
824                         for (i = x; i < x+len; i++)
825                         {
826                                 Term->scr->a[y][i] = col;
827                         }
828                         break;
829
830                 case 'n': /* 繰り返し */
831                         for (i = 1; i < len; i++)
832                         {
833                                 mesg[i] = mesg[0];
834                         }
835                         mesg[i] = '\0';
836                         update_term_size(x, y, len);
837                         (void)((*angband_term[0]->text_hook)(x, y, len, (byte)col, mesg));
838                         strncpy(&Term->scr->c[y][x], mesg, len);
839                         for (i = x; i < x+len; i++)
840                         {
841                                 Term->scr->a[y][i] = col;
842                         }
843                         break;
844
845                 case 's': /* 一文字 */
846                         update_term_size(x, y, 1);
847                         (void)((*angband_term[0]->text_hook)(x, y, 1, (byte)col, mesg));
848                         strncpy(&Term->scr->c[y][x], mesg, 1);
849                         Term->scr->a[y][x] = col;
850                         break;
851
852                 case 'w':
853                         update_term_size(x, y, len);
854                         (void)((*angband_term[0]->wipe_hook)(x, y, len));
855                         break;
856
857                 case 'x':
858                         if (x == TERM_XTRA_CLEAR) Term_clear();
859                         (void)((*angband_term[0]->xtra_hook)(x, 0));
860                         break;
861
862                 case 'c':
863                         update_term_size(x, y, 1);
864                         (void)((*angband_term[0]->curs_hook)(x, y));
865                         break;
866                 case 'C':
867                         update_term_size(x, y, 1);
868                         (void)((*angband_term[0]->bigcurs_hook)(x, y));
869                         break;
870                 }
871         }
872
873         fresh_queue.next++;
874         if (fresh_queue.next == FRESH_QUEUE_SIZE) fresh_queue.next = 0;
875         return TRUE;
876 }
877
878 #ifdef CHUUKEI
879 void browse_chuukei()
880 {
881         fd_set fdset;
882         struct timeval tv;
883
884         tv.tv_sec = 0;
885         tv.tv_usec = WAIT;
886
887         FD_ZERO(&fdset);
888         FD_SET(sd, &fdset);
889
890         Term_clear();
891         Term_fresh();
892         Term_xtra(TERM_XTRA_REACT, 0);
893
894         while (TRUE)
895         {
896                 fd_set tmp_fdset;
897                 struct timeval tmp_tv;
898
899                 if (flush_ringbuf_client()) continue;
900
901                 tmp_fdset = fdset;
902                 tmp_tv = tv;
903
904                 /* ソケットにデータが来ているかどうか調べる */
905                 select(sd+1, &tmp_fdset, (fd_set *)NULL, (fd_set *)NULL, &tmp_tv);
906                 if (FD_ISSET(sd, &tmp_fdset) == 0)
907                 {
908                         Term_xtra(TERM_XTRA_FLUSH, 0);
909                         continue;
910                 }
911
912                 if (read_sock() < 0)
913                 {
914                         chuukei_client = FALSE;
915                 }
916
917                 /* 接続が切れた状態で書くべきデータがなくなっていたら終了 */
918                 if (!chuukei_client && fresh_queue.next == fresh_queue.tail ) break;
919         }
920 }
921 #endif /* CHUUKEI */
922
923 void prepare_browse_movie_aux(concptr filename)
924 {
925         movie_fd = fd_open(filename, O_RDONLY);
926         
927         browsing_movie = TRUE;
928
929         init_buffer();
930 }
931
932 void prepare_browse_movie(concptr filename)
933 {
934         char buf[1024];
935         path_build(buf, sizeof(buf), ANGBAND_DIR_USER, filename);
936
937         prepare_browse_movie_aux(buf);
938 }
939
940 void browse_movie(void)
941 {
942         Term_clear();
943         Term_fresh();
944         Term_xtra(TERM_XTRA_REACT, 0);
945
946         while (read_movie_file() == 0)
947         {
948                 while (fresh_queue.next != fresh_queue.tail)
949                 {
950                         if (!flush_ringbuf_client())
951                         {
952                                 Term_xtra(TERM_XTRA_FLUSH, 0);
953
954                                 /* ソケットにデータが来ているかどうか調べる */
955 #ifdef WINDOWS
956                                 Sleep(WAIT);
957 #else
958                                 usleep(WAIT);
959 #endif
960                         }
961                 }
962         }
963 }