OSDN Git Service

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