X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=src%2Fio%2Frecord-play-movie.cpp;h=c4839132b43d75160b5a93315fa19f5c18c2d8d7;hb=1fbd8be7ac9284de337efae6937d77164bb27be4;hp=4209b5d2bbcac4b3c4b5361bfdd8a7fedc494355;hpb=e47db2e1ce30e3e8edeaf08b6790f9f4a6a78242;p=hengbandforosx%2Fhengbandosx.git diff --git a/src/io/record-play-movie.cpp b/src/io/record-play-movie.cpp index 4209b5d2b..c4839132b 100644 --- a/src/io/record-play-movie.cpp +++ b/src/io/record-play-movie.cpp @@ -11,32 +11,32 @@ #include "io/files-util.h" #include "io/inet.h" #include "io/signal-handlers.h" +#include "locale/japanese.h" #include "system/player-type-definition.h" #include "term/gameterm.h" +#include "term/z-form.h" #include "util/angband-files.h" #include "view/display-messages.h" -#ifdef JP -#include "locale/japanese.h" -#endif +#include +#include #ifdef WINDOWS #include +#define WAIT 100 #else #include "system/h-basic.h" #ifdef HAVE_SYS_TIME_H #include #endif +#define WAIT 100 * 1000 /* ブラウズ側のウエイト(us単位) */ #endif #define RINGBUF_SIZE 1024 * 1024 #define FRESH_QUEUE_SIZE 4096 -#ifdef WINDOWS -#define WAIT 100 -#else -#define WAIT 100 * 1000 /* ブラウズ側のウエイト(us単位) */ -#endif #define DEFAULT_DELAY 50 #define RECVBUF_SIZE 1024 +/* 「n」、「t」、および「w」コマンドでは、長さが「signed char」に配置されるときに負の値を回避するために、これよりも長い長さを使用しないでください。 */ +static constexpr auto SPLIT_MAX = 127; static long epoch_time; /* バッファ開始時刻 */ static int browse_delay; /* 表示するまでの時間(100ms単位)(この間にラグを吸収する) */ @@ -69,7 +69,7 @@ static errr (*old_text_hook)(int x, int y, int n, TERM_COLOR a, concptr s); static void disable_chuukei_server(void) { - term_type *t = angband_term[0]; + term_type *t = angband_terms[0]; t->xtra_hook = old_xtra_hook; t->curs_hook = old_curs_hook; t->bigcurs_hook = old_bigcurs_hook; @@ -83,9 +83,10 @@ static errr init_buffer(void) fresh_queue.next = fresh_queue.tail = 0; ring.wptr = ring.rptr = ring.inlen = 0; fresh_queue.time[0] = 0; - ring.buf = static_cast(malloc(RINGBUF_SIZE)); - if (ring.buf == nullptr) + ring.buf = static_cast(malloc(RINGBUF_SIZE)); + if (ring.buf == nullptr) { return -1; + } return 0; } @@ -99,150 +100,210 @@ static long get_current_time(void) struct timeval tv; gettimeofday(&tv, nullptr); - return (tv.tv_sec * 10 + tv.tv_usec / 100000); + return tv.tv_sec * 10 + tv.tv_usec / 100000; #endif } -/* リングバッファ構造体に buf の内容を加える */ -static errr insert_ringbuf(char *buf) +/*! + * @brief リングバッファにヘッダとペイロードを追加する + * @param header ヘッダ + * @param payload ペイロード (オプション) + * @return エラーコード + */ +static errr insert_ringbuf(std::string_view header, std::string_view payload = "") { - int len; - len = strlen(buf) + 1; /* +1は終端文字分 */ - if (movie_mode) { - fd_write(movie_fd, buf, len); + fd_write(movie_fd, header.data(), header.length()); + if (!payload.empty()) { + fd_write(movie_fd, payload.data(), payload.length()); + } + fd_write(movie_fd, "", 1); return 0; } /* バッファをオーバー */ - if (ring.inlen + len >= RINGBUF_SIZE) + auto all_length = header.length() + payload.length(); + if (ring.inlen + all_length + 1 >= RINGBUF_SIZE) { return -1; + } /* バッファの終端までに収まる */ - if (ring.wptr + len < RINGBUF_SIZE) { - memcpy(ring.buf + ring.wptr, buf, len); - ring.wptr += len; + if (ring.wptr + all_length + 1 < RINGBUF_SIZE) { + memcpy(ring.buf + ring.wptr, header.data(), header.length()); + if (!payload.empty()) { + memcpy(ring.buf + ring.wptr + header.length(), payload.data(), payload.length()); + } + *(ring.buf + ring.wptr + all_length) = '\0'; + ring.wptr += all_length + 1; } /* バッファの終端までに収まらない(ピッタリ収まる場合も含む) */ else { int head = RINGBUF_SIZE - ring.wptr; /* 前半 */ - int tail = len - head; /* 後半 */ + int tail = all_length - head; /* 後半 */ - memcpy(ring.buf + ring.wptr, buf, head); - memcpy(ring.buf, buf + head, tail); - ring.wptr = tail; + if ((int)header.length() <= head) { + memcpy(ring.buf + ring.wptr, header.data(), header.length()); + head -= header.length(); + if (head > 0) { + memcpy(ring.buf + ring.wptr + header.length(), payload.data(), head); + } + memcpy(ring.buf, payload.data() + head, tail); + } else { + memcpy(ring.buf + ring.wptr, header.data(), head); + int part = header.length() - head; + memcpy(ring.buf, header.data() + head, part); + if (tail > part) { + memcpy(ring.buf + part, payload.data(), tail - part); + } + } + *(ring.buf + tail) = '\0'; + ring.wptr = tail + 1; } - ring.inlen += len; + ring.inlen += all_length + 1; /* Success */ return 0; } /* strが同じ文字の繰り返しかどうか調べる */ -static bool string_is_repeat(char *str, int len) +static bool string_is_repeat(concptr str, int len) { char c = str[0]; int i; - if (len < 2) + if (len < 2) { return false; + } #ifdef JP - if (iskanji(c)) + if (iskanji(c)) { return false; + } #endif for (i = 1; i < len; i++) { #ifdef JP - if (c != str[i] || iskanji(str[i])) + if (c != str[i] || iskanji(str[i])) { return false; + } #else - if (c != str[i]) + if (c != str[i]) { return false; + } #endif } return true; } +#ifdef JP +/* マルチバイト文字を分割せずに str を分割する長さを len 以下で返す */ +static int find_split(concptr str, int len) +{ + /* + * SJIS が定義されている場合でも、str は常に EUC-JP としてエンコードされます。 + * 他の場所で行われているような 3 バイトのエンコーディングは想定していません。 + */ + int last_split = 0, i = 0; + while (i < len) { + if (iseuckanji(str[i])) { + if (i < len - 1) { + last_split = i + 2; + } + i += 2; + } else { + last_split = i + 1; + ++i; + } + } + return last_split; +} +#endif + static errr send_text_to_chuukei_server(TERM_LEN x, TERM_LEN y, int len, TERM_COLOR col, concptr str) { - char buf[1024 + 32]; - char buf2[1024]; + if (len == 1) { + insert_ringbuf(format("s%c%c%c%c", x + 1, y + 1, col, *str)); + return (*old_text_hook)(x, y, len, col, str); + } - strncpy(buf2, str, len); - buf2[len] = '\0'; + if (string_is_repeat(str, len)) { + while (len > SPLIT_MAX) { + insert_ringbuf(format("n%c%c%c%c%c", x + 1, y + 1, SPLIT_MAX, col, *str)); + x += SPLIT_MAX; + len -= SPLIT_MAX; + } - if (len == 1) { - sprintf(buf, "s%c%c%c%c", x + 1, y + 1, col, buf2[0]); - } else if (string_is_repeat(buf2, len)) { - int i; - for (i = len; i > 0; i -= 127) { - sprintf(buf, "n%c%c%c%c%c", x + 1, y + 1, MIN(i, 127), col, buf2[0]); + std::string formatted_text; + if (len > 1) { + formatted_text = format("n%c%c%c%c%c", x + 1, y + 1, len, col, *str); + } else { + formatted_text = format("s%c%c%c%c", x + 1, y + 1, col, *str); } - } else { + + insert_ringbuf(formatted_text); + return (*old_text_hook)(x, y, len, col, str); + } + #if defined(SJIS) && defined(JP) - sjis2euc(buf2); + std::string buffer = str; // strは書き換わって欲しくないのでコピーする. + auto *payload = buffer.data(); + sjis2euc(payload); +#else + const auto *payload = str; #endif - sprintf(buf, "t%c%c%c%c%s", x + 1, y + 1, len, col, buf2); + while (len > SPLIT_MAX) { + auto split_len = _(find_split(payload, SPLIT_MAX), SPLIT_MAX); + insert_ringbuf(format("t%c%c%c%c", x + 1, y + 1, split_len, col), std::string_view(payload, split_len)); + x += split_len; + len -= split_len; + payload += split_len; } - insert_ringbuf(buf); - + insert_ringbuf(format("t%c%c%c%c", x + 1, y + 1, len, col), std::string_view(payload, len)); return (*old_text_hook)(x, y, len, col, str); } static errr send_wipe_to_chuukei_server(int x, int y, int len) { - char buf[1024]; - - sprintf(buf, "w%c%c%c", x + 1, y + 1, len); - - insert_ringbuf(buf); + while (len > SPLIT_MAX) { + insert_ringbuf(format("w%c%c%c", x + 1, y + 1, SPLIT_MAX)); + x += SPLIT_MAX; + len -= SPLIT_MAX; + } + insert_ringbuf(format("w%c%c%c", x + 1, y + 1, len)); return (*old_wipe_hook)(x, y, len); } static errr send_xtra_to_chuukei_server(int n, int v) { - char buf[1024]; - if (n == TERM_XTRA_CLEAR || n == TERM_XTRA_FRESH || n == TERM_XTRA_SHAPE) { - sprintf(buf, "x%c", n + 1); - - insert_ringbuf(buf); + insert_ringbuf(format("x%c", n + 1)); if (n == TERM_XTRA_FRESH) { - sprintf(buf, "d%ld", get_current_time() - epoch_time); - insert_ringbuf(buf); + insert_ringbuf("d", std::to_string(get_current_time() - epoch_time)); } } /* Verify the hook */ - if (!old_xtra_hook) + if (!old_xtra_hook) { return -1; + } return (*old_xtra_hook)(n, v); } static errr send_curs_to_chuukei_server(int x, int y) { - char buf[1024]; - - sprintf(buf, "c%c%c", x + 1, y + 1); - - insert_ringbuf(buf); + insert_ringbuf(format("c%c%c", x + 1, y + 1)); return (*old_curs_hook)(x, y); } static errr send_bigcurs_to_chuukei_server(int x, int y) { - char buf[1024]; - - sprintf(buf, "C%c%c", x + 1, y + 1); - - insert_ringbuf(buf); + insert_ringbuf(format("C%c%c", x + 1, y + 1)); return (*old_bigcurs_hook)(x, y); } @@ -252,7 +313,7 @@ static errr send_bigcurs_to_chuukei_server(int x, int y) */ void prepare_chuukei_hooks(void) { - term_type *t0 = angband_term[0]; + term_type *t0 = angband_terms[0]; /* Save original z-term hooks */ old_xtra_hook = t0->xtra_hook; @@ -272,52 +333,50 @@ void prepare_chuukei_hooks(void) /* * Prepare z-term hooks to call send_*_to_chuukei_server()'s */ -void prepare_movie_hooks(player_type *player_ptr) +void prepare_movie_hooks(PlayerType *player_ptr) { - char buf[1024]; - char tmp[80]; + TermCenteredOffsetSetter tcos(std::nullopt, std::nullopt); if (movie_mode) { movie_mode = 0; disable_chuukei_server(); fd_close(movie_fd); msg_print(_("録画を終了しました。", "Stopped recording.")); - } else { - sprintf(tmp, "%s.amv", player_ptr->base_name); - if (get_string(_("ムービー記録ファイル: ", "Movie file name: "), tmp, 80)) { - int fd; - - path_build(buf, sizeof(buf), ANGBAND_DIR_USER, tmp); - - fd = fd_open(buf, O_RDONLY); - - /* Existing file */ - if (fd >= 0) { - char out_val[sizeof(buf) + 128]; - (void)fd_close(fd); - - /* Build query */ - (void)sprintf(out_val, _("現存するファイルに上書きしますか? (%s)", "Replace existing file %s? "), buf); + return; + } - /* Ask */ - if (!get_check(out_val)) - return; + std::stringstream ss; + ss << player_ptr->base_name << ".amv"; + auto movie_filename = ss.str(); + if (!get_string(_("ムービー記録ファイル: ", "Movie file name: "), movie_filename.data(), 80)) { + return; + } - movie_fd = fd_open(buf, O_WRONLY | O_TRUNC); - } else { - movie_fd = fd_make(buf, 0644); - } + const auto &path = path_build(ANGBAND_DIR_USER, movie_filename); + auto fd = fd_open(path, O_RDONLY); + if (fd >= 0) { + const auto &filename = path.string(); + (void)fd_close(fd); + std::string query = _("現存するファイルに上>書きしますか? (", "Replace existing file "); + query.append(filename); + query.append(_(")", "? ")); + if (!get_check(query)) { + return; + } - if (!movie_fd) { - msg_print(_("ファイルを開けません!", "Can not open file.")); - return; - } + movie_fd = fd_open(path, O_WRONLY | O_TRUNC); + } else { + movie_fd = fd_make(path); + } - movie_mode = 1; - prepare_chuukei_hooks(); - do_cmd_redraw(player_ptr); - } + if (!movie_fd) { + msg_print(_("ファイルを開けません!", "Can not open file.")); + return; } + + movie_mode = 1; + prepare_chuukei_hooks(); + do_cmd_redraw(player_ptr); } static int handle_movie_timestamp_data(int timestamp) @@ -350,33 +409,40 @@ static int read_movie_file(void) static char recv_buf[RECVBUF_SIZE]; static int remain_bytes = 0; int recv_bytes; - int i; + int i, start; recv_bytes = read(movie_fd, recv_buf + remain_bytes, RECVBUF_SIZE - remain_bytes); - if (recv_bytes <= 0) + if (recv_bytes <= 0) { return -1; + } /* 前回残ったデータ量に今回読んだデータ量を追加 */ remain_bytes += recv_bytes; - for (i = 0; i < remain_bytes; i++) { + for (i = 0, start = 0; i < remain_bytes; i++) { /* データのくぎり('\0')を探す */ if (recv_buf[i] == '\0') { /* 'd'で始まるデータ(タイムスタンプ)の場合は 描画キューに保存する処理を呼ぶ */ - if ((recv_buf[0] == 'd') && (handle_movie_timestamp_data(atoi(recv_buf + 1)) < 0)) + if ((recv_buf[start] == 'd') && (handle_movie_timestamp_data(atoi(recv_buf + start + 1)) < 0)) { return -1; + } /* 受信データを保存 */ - if (insert_ringbuf(recv_buf) < 0) + if (insert_ringbuf(std::string_view(recv_buf + start, i - start)) < 0) { return -1; + } - /* 次のデータ移行をrecv_bufの先頭に移動 */ - memmove(recv_buf, recv_buf + i + 1, remain_bytes - i - 1); - - remain_bytes -= (i + 1); - i = 0; + start = i + 1; + } + } + if (start > 0) { + if (remain_bytes >= start) { + memmove(recv_buf, recv_buf + start, remain_bytes - start); + remain_bytes -= start; + } else { + remain_bytes = 0; } } @@ -388,10 +454,11 @@ static int read_movie_file(void) static void win2unix(int col, char *buf) { char wall; - if (col == 9) + if (col == 9) { wall = '%'; - else + } else { wall = '#'; + } while (*buf) { #ifdef JP @@ -400,10 +467,11 @@ static void win2unix(int col, char *buf) continue; } #endif - if (*buf == 127) + if (*buf == 127) { *buf = wall; - else if (*buf == 31) + } else if (*buf == 31) { *buf = '.'; + } buf++; } } @@ -416,14 +484,17 @@ static bool get_nextbuf(char *buf) while (true) { *ptr = ring.buf[ring.rptr++]; ring.inlen--; - if (ring.rptr == RINGBUF_SIZE) + if (ring.rptr == RINGBUF_SIZE) { ring.rptr = 0; - if (*ptr++ == '\0') + } + if (*ptr++ == '\0') { break; + } } - if (buf[0] == 'd') + if (buf[0] == 'd') { return false; + } return true; } @@ -438,27 +509,32 @@ static void update_term_size(int x, int y, int len) ny = oy; /* 横方向のチェック */ - if (x + len > ox) + if (x + len > ox) { nx = x + len; + } /* 縦方向のチェック */ - if (y + 1 > oy) + if (y + 1 > oy) { ny = y + 1; + } - if (nx != ox || ny != oy) + if (nx != ox || ny != oy) { term_resize(nx, ny); + } } -static bool flush_ringbuf_client(void) +static bool flush_ringbuf_client() { char buf[1024]; /* 書くデータなし */ - if (fresh_queue.next == fresh_queue.tail) + if (fresh_queue.next == fresh_queue.tail) { return false; + } /* まだ書くべき時でない */ - if (fresh_queue.time[fresh_queue.next] > get_current_time() - epoch_time) + if (fresh_queue.time[fresh_queue.next] > get_current_time() - epoch_time) { return false; + } /* 時間情報(区切り)が得られるまで書く */ while (get_nextbuf(buf)) { @@ -477,8 +553,9 @@ static bool flush_ringbuf_client(void) if (id == 's') { col = tmp3; mesg = &buf[4]; - } else + } else { mesg = &buf[5]; + } #ifndef WINDOWS win2unix(col, mesg); #endif @@ -489,10 +566,10 @@ static bool flush_ringbuf_client(void) euc2sjis(mesg); #endif update_term_size(x, y, len); - (void)((*angband_term[0]->text_hook)(x, y, len, (byte)col, mesg)); - memcpy(&Term->scr->c[y][x], mesg, len); + (void)((*angband_terms[0]->text_hook)(x, y, len, (byte)col, mesg)); + memcpy(&game_term->scr->c[y][x], mesg, len); for (i = x; i < x + len; i++) { - Term->scr->a[y][i] = col; + game_term->scr->a[y][i] = col; } break; @@ -502,49 +579,51 @@ static bool flush_ringbuf_client(void) } mesg[i] = '\0'; update_term_size(x, y, len); - (void)((*angband_term[0]->text_hook)(x, y, len, (byte)col, mesg)); - memcpy(&Term->scr->c[y][x], mesg, len); + (void)((*angband_terms[0]->text_hook)(x, y, len, (byte)col, mesg)); + memcpy(&game_term->scr->c[y][x], mesg, len); for (i = x; i < x + len; i++) { - Term->scr->a[y][i] = col; + game_term->scr->a[y][i] = col; } break; case 's': /* 一文字 */ update_term_size(x, y, 1); - (void)((*angband_term[0]->text_hook)(x, y, 1, (byte)col, mesg)); - memcpy(&Term->scr->c[y][x], mesg, 1); - Term->scr->a[y][x] = col; + (void)((*angband_terms[0]->text_hook)(x, y, 1, (byte)col, mesg)); + memcpy(&game_term->scr->c[y][x], mesg, 1); + game_term->scr->a[y][x] = col; break; case 'w': update_term_size(x, y, len); - (void)((*angband_term[0]->wipe_hook)(x, y, len)); + (void)((*angband_terms[0]->wipe_hook)(x, y, len)); break; case 'x': - if (x == TERM_XTRA_CLEAR) + if (x == TERM_XTRA_CLEAR) { term_clear(); - (void)((*angband_term[0]->xtra_hook)(x, 0)); + } + (void)((*angband_terms[0]->xtra_hook)(x, 0)); break; case 'c': update_term_size(x, y, 1); - (void)((*angband_term[0]->curs_hook)(x, y)); + (void)((*angband_terms[0]->curs_hook)(x, y)); break; case 'C': update_term_size(x, y, 1); - (void)((*angband_term[0]->bigcurs_hook)(x, y)); + (void)((*angband_terms[0]->bigcurs_hook)(x, y)); break; } } fresh_queue.next++; - if (fresh_queue.next == FRESH_QUEUE_SIZE) + if (fresh_queue.next == FRESH_QUEUE_SIZE) { fresh_queue.next = 0; + } return true; } -void prepare_browse_movie_without_path_build(concptr filename) +void prepare_browse_movie_without_path_build(std::string_view filename) { movie_fd = fd_open(filename, O_RDONLY); init_buffer(); @@ -573,11 +652,10 @@ void browse_movie(void) } #ifndef WINDOWS -void prepare_browse_movie_with_path_build(concptr filename) +void prepare_browse_movie_with_path_build(std::string_view filename) { - char buf[1024]; - path_build(buf, sizeof(buf), ANGBAND_DIR_USER, filename); - movie_fd = fd_open(buf, O_RDONLY); + const auto &path = path_build(ANGBAND_DIR_USER, filename); + movie_fd = fd_open(path.string(), O_RDONLY); init_buffer(); } #endif