OSDN Git Service

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