OSDN Git Service

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