4 * @author 2014 Deskull rearranged comment for Doxygen.
7 #include "io/record-play-movie.h"
8 #include "cmd-io/cmd-dump.h"
9 #include "cmd-visual/cmd-draw.h"
10 #include "core/asking-player.h"
11 #include "io/files-util.h"
13 #include "io/signal-handlers.h"
14 #include "term/gameterm.h"
15 #include "util/angband-files.h"
16 #include "view/display-messages.h"
18 #include "locale/japanese.h"
24 #include "system/h-basic.h"
25 #ifdef HAVE_SYS_TIME_H
30 #define RINGBUF_SIZE 1024 * 1024
31 #define FRESH_QUEUE_SIZE 4096
35 #define WAIT 100 * 1000 /* ブラウズ側のウエイト(us単位) */
37 #define DEFAULT_DELAY 50
38 #define RECVBUF_SIZE 1024
40 static long epoch_time; /* バッファ開始時刻 */
41 static int browse_delay; /* 表示するまでの時間(100ms単位)(この間にラグを吸収する) */
43 static int movie_mode;
45 /* 描画する時刻を覚えておくキュー構造体 */
47 int time[FRESH_QUEUE_SIZE];
63 static errr (*old_xtra_hook)(int n, int v);
64 static errr (*old_curs_hook)(int x, int y);
65 static errr (*old_bigcurs_hook)(int x, int y);
66 static errr (*old_wipe_hook)(int x, int y, int n);
67 static errr (*old_text_hook)(int x, int y, int n, TERM_COLOR a, concptr s);
69 static void disable_chuukei_server(void)
71 term_type *t = angband_term[0];
72 t->xtra_hook = old_xtra_hook;
73 t->curs_hook = old_curs_hook;
74 t->bigcurs_hook = old_bigcurs_hook;
75 t->wipe_hook = old_wipe_hook;
76 t->text_hook = old_text_hook;
79 /* ANSI Cによればstatic変数は0で初期化されるが一応初期化する */
80 static errr init_buffer(void)
82 fresh_queue.next = fresh_queue.tail = 0;
83 ring.wptr = ring.rptr = ring.inlen = 0;
84 fresh_queue.time[0] = 0;
85 ring.buf = static_cast<char*>(malloc(RINGBUF_SIZE));
92 /* 現在の時間を100ms単位で取得する */
93 static long get_current_time(void)
96 return timeGetTime() / 100;
99 gettimeofday(&tv, NULL);
101 return (tv.tv_sec * 10 + tv.tv_usec / 100000);
105 /* リングバッファ構造体に buf の内容を加える */
106 static errr insert_ringbuf(char *buf)
109 len = strlen(buf) + 1; /* +1は終端文字分 */
112 fd_write(movie_fd, buf, len);
117 if (ring.inlen + len >= RINGBUF_SIZE)
121 if (ring.wptr + len < RINGBUF_SIZE) {
122 memcpy(ring.buf + ring.wptr, buf, len);
125 /* バッファの終端までに収まらない(ピッタリ収まる場合も含む) */
127 int head = RINGBUF_SIZE - ring.wptr; /* 前半 */
128 int tail = len - head; /* 後半 */
130 memcpy(ring.buf + ring.wptr, buf, head);
131 memcpy(ring.buf, buf + head, tail);
141 /* strが同じ文字の繰り返しかどうか調べる */
142 static bool string_is_repeat(char *str, int len)
154 for (i = 1; i < len; i++) {
156 if (c != str[i] || iskanji(str[i]))
167 static errr send_text_to_chuukei_server(TERM_LEN x, TERM_LEN y, int len, TERM_COLOR col, concptr str)
172 strncpy(buf2, str, len);
176 sprintf(buf, "s%c%c%c%c", x + 1, y + 1, col, buf2[0]);
177 } else if (string_is_repeat(buf2, len)) {
179 for (i = len; i > 0; i -= 127) {
180 sprintf(buf, "n%c%c%c%c%c", x + 1, y + 1, MIN(i, 127), col, buf2[0]);
183 #if defined(SJIS) && defined(JP)
186 sprintf(buf, "t%c%c%c%c%s", x + 1, y + 1, len, col, buf2);
191 return (*old_text_hook)(x, y, len, col, str);
194 static errr send_wipe_to_chuukei_server(int x, int y, int len)
198 sprintf(buf, "w%c%c%c", x + 1, y + 1, len);
202 return (*old_wipe_hook)(x, y, len);
205 static errr send_xtra_to_chuukei_server(int n, int v)
209 if (n == TERM_XTRA_CLEAR || n == TERM_XTRA_FRESH || n == TERM_XTRA_SHAPE) {
210 sprintf(buf, "x%c", n + 1);
214 if (n == TERM_XTRA_FRESH) {
215 sprintf(buf, "d%ld", get_current_time() - epoch_time);
220 /* Verify the hook */
224 return (*old_xtra_hook)(n, v);
227 static errr send_curs_to_chuukei_server(int x, int y)
231 sprintf(buf, "c%c%c", x + 1, y + 1);
235 return (*old_curs_hook)(x, y);
238 static errr send_bigcurs_to_chuukei_server(int x, int y)
242 sprintf(buf, "C%c%c", x + 1, y + 1);
246 return (*old_bigcurs_hook)(x, y);
250 * Prepare z-term hooks to call send_*_to_chuukei_server()'s
252 void prepare_chuukei_hooks(void)
254 term_type *t0 = angband_term[0];
256 /* Save original z-term hooks */
257 old_xtra_hook = t0->xtra_hook;
258 old_curs_hook = t0->curs_hook;
259 old_bigcurs_hook = t0->bigcurs_hook;
260 old_wipe_hook = t0->wipe_hook;
261 old_text_hook = t0->text_hook;
263 /* Prepare z-term hooks */
264 t0->xtra_hook = send_xtra_to_chuukei_server;
265 t0->curs_hook = send_curs_to_chuukei_server;
266 t0->bigcurs_hook = send_bigcurs_to_chuukei_server;
267 t0->wipe_hook = send_wipe_to_chuukei_server;
268 t0->text_hook = send_text_to_chuukei_server;
272 * Prepare z-term hooks to call send_*_to_chuukei_server()'s
274 void prepare_movie_hooks(player_type *player_ptr)
281 disable_chuukei_server();
283 msg_print(_("録画を終了しました。", "Stopped recording."));
285 sprintf(tmp, "%s.amv", player_ptr->base_name);
286 if (get_string(_("ムービー記録ファイル: ", "Movie file name: "), tmp, 80)) {
289 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, tmp);
291 fd = fd_open(buf, O_RDONLY);
295 char out_val[sizeof(buf) + 128];
299 (void)sprintf(out_val, _("現存するファイルに上書きしますか? (%s)", "Replace existing file %s? "), buf);
302 if (!get_check(out_val))
305 movie_fd = fd_open(buf, O_WRONLY | O_TRUNC);
307 movie_fd = fd_make(buf, 0644);
311 msg_print(_("ファイルを開けません!", "Can not open file."));
316 prepare_chuukei_hooks();
317 do_cmd_redraw(player_ptr);
322 static int handle_movie_timestamp_data(int timestamp)
324 static int initialized = FALSE;
328 /* バッファリングし始めの時間を保存しておく */
329 epoch_time = get_current_time();
330 epoch_time += browse_delay;
331 epoch_time -= timestamp;
332 // time_diff = current_time - timestamp;
336 /* 描画キューに保存し、保存位置を進める */
337 fresh_queue.time[fresh_queue.tail] = timestamp;
340 /* キューの最後尾に到達したら先頭に戻す */
341 fresh_queue.tail %= FRESH_QUEUE_SIZE;
347 static int read_movie_file(void)
349 static char recv_buf[RECVBUF_SIZE];
350 static int remain_bytes = 0;
354 recv_bytes = read(movie_fd, recv_buf + remain_bytes, RECVBUF_SIZE - remain_bytes);
359 /* 前回残ったデータ量に今回読んだデータ量を追加 */
360 remain_bytes += recv_bytes;
362 for (i = 0; i < remain_bytes; i++) {
363 /* データのくぎり('\0')を探す */
364 if (recv_buf[i] == '\0') {
365 /* 'd'で始まるデータ(タイムスタンプ)の場合は
367 if ((recv_buf[0] == 'd') && (handle_movie_timestamp_data(atoi(recv_buf + 1)) < 0))
371 if (insert_ringbuf(recv_buf) < 0)
374 /* 次のデータ移行をrecv_bufの先頭に移動 */
375 memmove(recv_buf, recv_buf + i + 1, remain_bytes - i - 1);
377 remain_bytes -= (i + 1);
386 /* Win版の床の中点と壁の豆腐をピリオドとシャープにする。*/
387 static void win2unix(int col, char *buf)
411 static bool get_nextbuf(char *buf)
416 *ptr = ring.buf[ring.rptr++];
418 if (ring.rptr == RINGBUF_SIZE)
430 /* プレイホストのマップが大きいときクライアントのマップもリサイズする */
431 static void update_term_size(int x, int y, int len)
435 term_get_size(&ox, &oy);
446 if (nx != ox || ny != oy)
450 static bool flush_ringbuf_client(void)
455 if (fresh_queue.next == fresh_queue.tail)
459 if (fresh_queue.time[fresh_queue.next] > get_current_time() - epoch_time)
462 /* 時間情報(区切り)が得られるまで書く */
463 while (get_nextbuf(buf)) {
468 unsigned char tmp1, tmp2, tmp3, tmp4;
471 sscanf(buf, "%c%c%c%c%c", &id, &tmp1, &tmp2, &tmp3, &tmp4);
487 #if defined(SJIS) && defined(JP)
490 update_term_size(x, y, len);
491 (void)((*angband_term[0]->text_hook)(x, y, len, (byte)col, mesg));
492 memcpy(&Term->scr->c[y][x], mesg, len);
493 for (i = x; i < x + len; i++) {
494 Term->scr->a[y][i] = col;
499 for (i = 1; i < len; i++) {
503 update_term_size(x, y, len);
504 (void)((*angband_term[0]->text_hook)(x, y, len, (byte)col, mesg));
505 memcpy(&Term->scr->c[y][x], mesg, len);
506 for (i = x; i < x + len; i++) {
507 Term->scr->a[y][i] = col;
512 update_term_size(x, y, 1);
513 (void)((*angband_term[0]->text_hook)(x, y, 1, (byte)col, mesg));
514 memcpy(&Term->scr->c[y][x], mesg, 1);
515 Term->scr->a[y][x] = col;
519 update_term_size(x, y, len);
520 (void)((*angband_term[0]->wipe_hook)(x, y, len));
524 if (x == TERM_XTRA_CLEAR)
526 (void)((*angband_term[0]->xtra_hook)(x, 0));
530 update_term_size(x, y, 1);
531 (void)((*angband_term[0]->curs_hook)(x, y));
534 update_term_size(x, y, 1);
535 (void)((*angband_term[0]->bigcurs_hook)(x, y));
541 if (fresh_queue.next == FRESH_QUEUE_SIZE)
542 fresh_queue.next = 0;
546 void prepare_browse_movie_without_path_build(concptr filename)
548 movie_fd = fd_open(filename, O_RDONLY);
552 void browse_movie(void)
556 term_xtra(TERM_XTRA_REACT, 0);
558 while (read_movie_file() == 0) {
559 while (fresh_queue.next != fresh_queue.tail) {
560 if (!flush_ringbuf_client()) {
561 term_xtra(TERM_XTRA_FLUSH, 0);
563 /* ソケットにデータが来ているかどうか調べる */
575 void prepare_browse_movie_with_path_build(concptr filename)
578 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, filename);
579 movie_fd = fd_open(buf, O_RDONLY);