OSDN Git Service

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