From fb14bd956a98fe25bf25c6b9c07e03344c207874 Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Tue, 7 Jul 2020 00:15:26 +0800 Subject: [PATCH] citra_qt: Add indicator in status bar Since we do not have an overlay yet, it can be confusing whether movie is being recorded or played. This makes it clear. Status messages (e.g. system archive missing) will be overriden, but that shouldn't be too important when recording movies. Doubled the status bar updating frequency to provide a better experience. It now updates every second. --- src/citra_qt/main.cpp | 21 +++++++++++++++++- src/citra_qt/main.h | 1 + src/core/movie.cpp | 50 +++++++++++++++++++++++++++++-------------- src/core/movie.h | 6 ++++++ 4 files changed, 61 insertions(+), 17 deletions(-) diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index b3242bcef..d9612fce8 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -1055,7 +1055,7 @@ void GMainWindow::BootGame(const QString& filename) { game_list->hide(); game_list_placeholder->hide(); } - status_bar_update_timer.start(2000); + status_bar_update_timer.start(1000); if (UISettings::values.hide_mouse) { mouse_hide_timer.start(); @@ -1165,6 +1165,7 @@ void GMainWindow::ShutdownGame() { // Disable status bar updates status_bar_update_timer.stop(); message_label->setVisible(false); + message_label_used_for_movie = false; emu_speed_label->setVisible(false); game_fps_label->setVisible(false); emu_frametime_label->setVisible(false); @@ -1982,6 +1983,23 @@ void GMainWindow::UpdateStatusBar() { return; } + // Update movie status + const u64 current = Core::Movie::GetInstance().GetCurrentInputIndex(); + const u64 total = Core::Movie::GetInstance().GetTotalInputCount(); + if (Core::Movie::GetInstance().IsRecordingInput()) { + message_label->setText(tr("Recording %1").arg(current)); + message_label->setVisible(true); + message_label_used_for_movie = true; + } else if (Core::Movie::GetInstance().IsPlayingInput()) { + message_label->setText(tr("Playing %1 / %2").arg(current).arg(total)); + message_label->setVisible(true); + message_label_used_for_movie = true; + } else if (message_label_used_for_movie) { // Clear the label if movie was just closed + message_label->setText(QString{}); + message_label->setVisible(false); + message_label_used_for_movie = false; + } + auto results = Core::System::GetInstance().GetAndResetPerfStats(); if (Settings::values.use_frame_limit_alternate) { @@ -2093,6 +2111,7 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det emu_thread->SetRunning(true); message_label->setText(status_message); message_label->setVisible(true); + message_label_used_for_movie = false; } } } diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index eff5ea7b5..99451d308 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -248,6 +248,7 @@ private: QLabel* game_fps_label = nullptr; QLabel* emu_frametime_label = nullptr; QTimer status_bar_update_timer; + bool message_label_used_for_movie = false; MultiplayerState* multiplayer_state = nullptr; std::unique_ptr config; diff --git a/src/core/movie.cpp b/src/core/movie.cpp index 51746e744..1e27b45f2 100644 --- a/src/core/movie.cpp +++ b/src/core/movie.cpp @@ -129,6 +129,22 @@ struct CTMHeader { static_assert(sizeof(CTMHeader) == 256, "CTMHeader should be 256 bytes"); #pragma pack(pop) +static u64 GetInputCount(const std::vector& input) { + u64 input_count = 0; + for (std::size_t pos = 0; pos < input.size(); pos += sizeof(ControllerState)) { + if (input.size() < pos + sizeof(ControllerState)) { + break; + } + + ControllerState state; + std::memcpy(&state, input.data() + pos, sizeof(ControllerState)); + if (state.type == ControllerStateType::PadAndCircle) { + input_count++; + } + } + return input_count; +} + template void Movie::serialize(Archive& ar, const unsigned int file_version) { // Only serialize what's needed to make savestates useful for TAS: @@ -136,6 +152,10 @@ void Movie::serialize(Archive& ar, const unsigned int file_version) { ar& _current_byte; current_byte = static_cast(_current_byte); + if (file_version > 0) { + ar& current_input; + } + std::vector recorded_input_ = recorded_input; ar& recorded_input_; @@ -167,6 +187,7 @@ void Movie::serialize(Archive& ar, const unsigned int file_version) { "This savestate was created at a later point and must be loaded in R+W mode"); } play_mode = PlayMode::Playing; + total_input = GetInputCount(recorded_input); } else { recorded_input = std::move(recorded_input_); play_mode = PlayMode::Recording; @@ -184,6 +205,13 @@ bool Movie::IsRecordingInput() const { return play_mode == PlayMode::Recording; } +u64 Movie::GetCurrentInputIndex() const { + return current_input; +} +u64 Movie::GetTotalInputCount() const { + return total_input; +} + void Movie::CheckInputEnd() { if (current_byte + sizeof(ControllerState) > recorded_input.size()) { LOG_INFO(Movie, "Playback finished"); @@ -198,6 +226,7 @@ void Movie::Play(Service::HID::PadState& pad_state, s16& circle_pad_x, s16& circ ControllerState s; std::memcpy(&s, &recorded_input[current_byte], sizeof(ControllerState)); current_byte += sizeof(ControllerState); + current_input++; if (s.type != ControllerStateType::PadAndCircle) { LOG_ERROR(Movie, @@ -325,6 +354,8 @@ void Movie::Record(const ControllerState& controller_state) { void Movie::Record(const Service::HID::PadState& pad_state, const s16& circle_pad_x, const s16& circle_pad_y) { + current_input++; + ControllerState s; s.type = ControllerStateType::PadAndCircle; @@ -429,22 +460,6 @@ Movie::ValidationResult Movie::ValidateHeader(const CTMHeader& header) const { return ValidationResult::OK; } -static u64 GetInputCount(const std::vector& input) { - u64 input_count = 0; - for (std::size_t pos = 0; pos < input.size(); pos += sizeof(ControllerState)) { - if (input.size() < pos + sizeof(ControllerState)) { - break; - } - - ControllerState state; - std::memcpy(&state, input.data() + pos, sizeof(ControllerState)); - if (state.type == ControllerStateType::PadAndCircle) { - input_count++; - } - } - return input_count; -} - Movie::ValidationResult Movie::ValidateInput(const std::vector& input, u64 expected_count) const { return GetInputCount(input) == expected_count ? ValidationResult::OK @@ -507,11 +522,13 @@ void Movie::StartPlayback(const std::string& movie_file) { record_movie_author = author.data(); rerecord_count = header.rerecord_count; + total_input = header.input_count; recorded_input.resize(size - sizeof(CTMHeader)); save_record.ReadArray(recorded_input.data(), recorded_input.size()); current_byte = 0; + current_input = 0; id = header.id; LOG_INFO(Movie, "Loaded Movie, ID: {:016X}", id); @@ -622,6 +639,7 @@ void Movie::Shutdown() { recorded_input.resize(0); record_movie_file.clear(); current_byte = 0; + current_input = 0; init_time = 0; id = 0; } diff --git a/src/core/movie.h b/src/core/movie.h index dcbe8129b..e62dfe5c2 100644 --- a/src/core/movie.h +++ b/src/core/movie.h @@ -123,6 +123,9 @@ public: bool IsPlayingInput() const; bool IsRecordingInput() const; + u64 GetCurrentInputIndex() const; + u64 GetTotalInputCount() const; + private: static Movie s_instance; @@ -159,6 +162,9 @@ private: std::vector recorded_input; std::size_t current_byte = 0; + u64 current_input = 0; + // Total input count of the current movie being played. Not used for recording. + u64 total_input = 0; u64 id = 0; // ID of the current movie loaded u64 init_time;