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 "locale/japanese.h"
15 #include "system/player-type-definition.h"
16 #include "term/gameterm.h"
17 #include "util/angband-files.h"
18 #include "view/display-messages.h"
25 #include "system/h-basic.h"
26 #ifdef HAVE_SYS_TIME_H
29 #define WAIT 100 * 1000 /* ブラウズ側のウエイト(us単位) */
32 #define RINGBUF_SIZE 1024 * 1024
33 #define FRESH_QUEUE_SIZE 4096
34 #define DEFAULT_DELAY 50
35 #define RECVBUF_SIZE 1024
37 static long epoch_time; /* バッファ開始時刻 */
38 static int browse_delay; /* 表示するまでの時間(100ms単位)(この間にラグを吸収する) */
40 static int movie_mode;
42 /* 描画する時刻を覚えておくキュー構造体 */
44 int time[FRESH_QUEUE_SIZE];
60 static errr (*old_xtra_hook)(int n, int v);
61 static errr (*old_curs_hook)(int x, int y);
62 static errr (*old_bigcurs_hook)(int x, int y);
63 static errr (*old_wipe_hook)(int x, int y, int n);
64 static errr (*old_text_hook)(int x, int y, int n, TERM_COLOR a, concptr s);
66 static void disable_chuukei_server(void)
68 term_type *t = angband_term[0];
69 t->xtra_hook = old_xtra_hook;
70 t->curs_hook = old_curs_hook;
71 t->bigcurs_hook = old_bigcurs_hook;
72 t->wipe_hook = old_wipe_hook;
73 t->text_hook = old_text_hook;
76 /* ANSI Cによればstatic変数は0で初期化されるが一応初期化する */
77 static errr init_buffer(void)
79 fresh_queue.next = fresh_queue.tail = 0;
80 ring.wptr = ring.rptr = ring.inlen = 0;
81 fresh_queue.time[0] = 0;
82 ring.buf = static_cast<char *>(malloc(RINGBUF_SIZE));
83 if (ring.buf == nullptr)
89 /* 現在の時間を100ms単位で取得する */
90 static long get_current_time(void)
93 return timeGetTime() / 100;
96 gettimeofday(&tv, nullptr);
98 return tv.tv_sec * 10 + tv.tv_usec / 100000;
102 /* リングバッファ構造体に buf の内容を加える */
103 static errr insert_ringbuf(char *buf)
106 len = strlen(buf) + 1; /* +1は終端文字分 */
109 fd_write(movie_fd, buf, len);
114 if (ring.inlen + len >= RINGBUF_SIZE)
118 if (ring.wptr + len < RINGBUF_SIZE) {
119 memcpy(ring.buf + ring.wptr, buf, len);
122 /* バッファの終端までに収まらない(ピッタリ収まる場合も含む) */
124 int head = RINGBUF_SIZE - ring.wptr; /* 前半 */
125 int tail = len - head; /* 後半 */
127 memcpy(ring.buf + ring.wptr, buf, head);
128 memcpy(ring.buf, buf + head, tail);
138 /* strが同じ文字の繰り返しかどうか調べる */
139 static bool string_is_repeat(char *str, int len)
151 for (i = 1; i < len; i++) {
153 if (c != str[i] || iskanji(str[i]))
164 static errr send_text_to_chuukei_server(TERM_LEN x, TERM_LEN y, int len, TERM_COLOR col, concptr str)
169 strncpy(buf2, str, len);
173 sprintf(buf, "s%c%c%c%c", x + 1, y + 1, col, buf2[0]);
174 } else if (string_is_repeat(buf2, len)) {
176 for (i = len; i > 0; i -= 127) {
177 sprintf(buf, "n%c%c%c%c%c", x + 1, y + 1, std::min<int>(i, 127), col, buf2[0]);
180 #if defined(SJIS) && defined(JP)
183 sprintf(buf, "t%c%c%c%c%s", x + 1, y + 1, len, col, buf2);
188 return (*old_text_hook)(x, y, len, col, str);
191 static errr send_wipe_to_chuukei_server(int x, int y, int len)
195 sprintf(buf, "w%c%c%c", x + 1, y + 1, len);
199 return (*old_wipe_hook)(x, y, len);
202 static errr send_xtra_to_chuukei_server(int n, int v)
206 if (n == TERM_XTRA_CLEAR || n == TERM_XTRA_FRESH || n == TERM_XTRA_SHAPE) {
207 sprintf(buf, "x%c", n + 1);
211 if (n == TERM_XTRA_FRESH) {
212 sprintf(buf, "d%ld", get_current_time() - epoch_time);
217 /* Verify the hook */
221 return (*old_xtra_hook)(n, v);
224 static errr send_curs_to_chuukei_server(int x, int y)
228 sprintf(buf, "c%c%c", x + 1, y + 1);
232 return (*old_curs_hook)(x, y);
235 static errr send_bigcurs_to_chuukei_server(int x, int y)
239 sprintf(buf, "C%c%c", x + 1, y + 1);
243 return (*old_bigcurs_hook)(x, y);
247 * Prepare z-term hooks to call send_*_to_chuukei_server()'s
249 void prepare_chuukei_hooks(void)
251 term_type *t0 = angband_term[0];
253 /* Save original z-term hooks */
254 old_xtra_hook = t0->xtra_hook;
255 old_curs_hook = t0->curs_hook;
256 old_bigcurs_hook = t0->bigcurs_hook;
257 old_wipe_hook = t0->wipe_hook;
258 old_text_hook = t0->text_hook;
260 /* Prepare z-term hooks */
261 t0->xtra_hook = send_xtra_to_chuukei_server;
262 t0->curs_hook = send_curs_to_chuukei_server;
263 t0->bigcurs_hook = send_bigcurs_to_chuukei_server;
264 t0->wipe_hook = send_wipe_to_chuukei_server;
265 t0->text_hook = send_text_to_chuukei_server;
269 * Prepare z-term hooks to call send_*_to_chuukei_server()'s
271 void prepare_movie_hooks(PlayerType *player_ptr)
278 disable_chuukei_server();
280 msg_print(_("録画を終了しました。", "Stopped recording."));
282 sprintf(tmp, "%s.amv", player_ptr->base_name);
283 if (get_string(_("ムービー記録ファイル: ", "Movie file name: "), tmp, 80)) {
286 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, tmp);
288 fd = fd_open(buf, O_RDONLY);
292 char out_val[sizeof(buf) + 128];
296 (void)sprintf(out_val, _("現存するファイルに上書きしますか? (%s)", "Replace existing file %s? "), buf);
299 if (!get_check(out_val))
302 movie_fd = fd_open(buf, O_WRONLY | O_TRUNC);
304 movie_fd = fd_make(buf, 0644);
308 msg_print(_("ファイルを開けません!", "Can not open file."));
313 prepare_chuukei_hooks();
314 do_cmd_redraw(player_ptr);
319 static int handle_movie_timestamp_data(int timestamp)
321 static int initialized = false;
325 /* バッファリングし始めの時間を保存しておく */
326 epoch_time = get_current_time();
327 epoch_time += browse_delay;
328 epoch_time -= timestamp;
329 // time_diff = current_time - timestamp;
333 /* 描画キューに保存し、保存位置を進める */
334 fresh_queue.time[fresh_queue.tail] = timestamp;
337 /* キューの最後尾に到達したら先頭に戻す */
338 fresh_queue.tail %= FRESH_QUEUE_SIZE;
344 static int read_movie_file(void)
346 static char recv_buf[RECVBUF_SIZE];
347 static int remain_bytes = 0;
351 recv_bytes = read(movie_fd, recv_buf + remain_bytes, RECVBUF_SIZE - remain_bytes);
356 /* 前回残ったデータ量に今回読んだデータ量を追加 */
357 remain_bytes += recv_bytes;
359 for (i = 0; i < remain_bytes; i++) {
360 /* データのくぎり('\0')を探す */
361 if (recv_buf[i] == '\0') {
362 /* 'd'で始まるデータ(タイムスタンプ)の場合は
364 if ((recv_buf[0] == 'd') && (handle_movie_timestamp_data(atoi(recv_buf + 1)) < 0))
368 if (insert_ringbuf(recv_buf) < 0)
371 /* 次のデータ移行をrecv_bufの先頭に移動 */
372 memmove(recv_buf, recv_buf + i + 1, remain_bytes - i - 1);
374 remain_bytes -= (i + 1);
383 /* Win版の床の中点と壁の豆腐をピリオドとシャープにする。*/
384 static void win2unix(int col, char *buf)
408 static bool get_nextbuf(char *buf)
413 *ptr = ring.buf[ring.rptr++];
415 if (ring.rptr == RINGBUF_SIZE)
427 /* プレイホストのマップが大きいときクライアントのマップもリサイズする */
428 static void update_term_size(int x, int y, int len)
432 term_get_size(&ox, &oy);
443 if (nx != ox || ny != oy)
447 static bool flush_ringbuf_client(void)
452 if (fresh_queue.next == fresh_queue.tail)
456 if (fresh_queue.time[fresh_queue.next] > get_current_time() - epoch_time)
459 /* 時間情報(区切り)が得られるまで書く */
460 while (get_nextbuf(buf)) {
465 unsigned char tmp1, tmp2, tmp3, tmp4;
468 sscanf(buf, "%c%c%c%c%c", &id, &tmp1, &tmp2, &tmp3, &tmp4);
484 #if defined(SJIS) && defined(JP)
487 update_term_size(x, y, len);
488 (void)((*angband_term[0]->text_hook)(x, y, len, (byte)col, mesg));
489 memcpy(&Term->scr->c[y][x], mesg, len);
490 for (i = x; i < x + len; i++) {
491 Term->scr->a[y][i] = col;
496 for (i = 1; i < len; i++) {
500 update_term_size(x, y, len);
501 (void)((*angband_term[0]->text_hook)(x, y, len, (byte)col, mesg));
502 memcpy(&Term->scr->c[y][x], mesg, len);
503 for (i = x; i < x + len; i++) {
504 Term->scr->a[y][i] = col;
509 update_term_size(x, y, 1);
510 (void)((*angband_term[0]->text_hook)(x, y, 1, (byte)col, mesg));
511 memcpy(&Term->scr->c[y][x], mesg, 1);
512 Term->scr->a[y][x] = col;
516 update_term_size(x, y, len);
517 (void)((*angband_term[0]->wipe_hook)(x, y, len));
521 if (x == TERM_XTRA_CLEAR)
523 (void)((*angband_term[0]->xtra_hook)(x, 0));
527 update_term_size(x, y, 1);
528 (void)((*angband_term[0]->curs_hook)(x, y));
531 update_term_size(x, y, 1);
532 (void)((*angband_term[0]->bigcurs_hook)(x, y));
538 if (fresh_queue.next == FRESH_QUEUE_SIZE)
539 fresh_queue.next = 0;
543 void prepare_browse_movie_without_path_build(concptr filename)
545 movie_fd = fd_open(filename, O_RDONLY);
549 void browse_movie(void)
553 term_xtra(TERM_XTRA_REACT, 0);
555 while (read_movie_file() == 0) {
556 while (fresh_queue.next != fresh_queue.tail) {
557 if (!flush_ringbuf_client()) {
558 term_xtra(TERM_XTRA_FLUSH, 0);
560 /* ソケットにデータが来ているかどうか調べる */
572 void prepare_browse_movie_with_path_build(concptr filename)
575 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, filename);
576 movie_fd = fd_open(buf, O_RDONLY);