OSDN Git Service

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