From 0f44f7b481fa3b35c7b383e7f7c4f4777d617c86 Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Sat, 7 Jul 2018 22:30:37 +0800 Subject: [PATCH] core/movie: Movie refactor, add a completion callback --- src/citra/citra.cpp | 12 +++++-- src/core/core.cpp | 2 -- src/core/movie.cpp | 84 +++++++++++++++++++++++++++------------------ src/core/movie.h | 22 ++++++++---- src/core/settings.h | 4 --- 5 files changed, 77 insertions(+), 47 deletions(-) diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index 8ecc1b202..30f3a5493 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -39,6 +39,7 @@ #include "core/gdbstub/gdbstub.h" #include "core/hle/service/am/am.h" #include "core/loader/loader.h" +#include "core/movie.h" #include "core/settings.h" #include "network/network.h" @@ -268,8 +269,6 @@ int main(int argc, char** argv) { // Apply the command line arguments Settings::values.gdbstub_port = gdb_port; Settings::values.use_gdbstub = use_gdbstub; - Settings::values.movie_play = std::move(movie_play); - Settings::values.movie_record = std::move(movie_record); Settings::Apply(); // Register frontend applets @@ -327,9 +326,18 @@ int main(int argc, char** argv) { } } + if (!movie_play.empty()) { + Core::Movie::GetInstance().StartPlayback(movie_play); + } + if (!movie_record.empty()) { + Core::Movie::GetInstance().StartRecording(movie_record); + } + while (emu_window->IsOpen()) { system.RunLoop(); } + Core::Movie::GetInstance().Shutdown(); + return 0; } diff --git a/src/core/core.cpp b/src/core/core.cpp index 013ef4c2e..ee4cd2060 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -176,7 +176,6 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { Kernel::Init(system_mode); Service::Init(service_manager); GDBStub::Init(); - Movie::GetInstance().Init(); if (!VideoCore::Init(emu_window)) { return ResultStatus::ErrorVideoCore; @@ -214,7 +213,6 @@ void System::Shutdown() { perf_results.frametime * 1000.0); // Shutdown emulation session - Movie::GetInstance().Shutdown(); GDBStub::Shutdown(); VideoCore::Shutdown(); Service::Shutdown(); diff --git a/src/core/movie.cpp b/src/core/movie.cpp index 16b3da7ca..0372d94da 100644 --- a/src/core/movie.cpp +++ b/src/core/movie.cpp @@ -118,10 +118,10 @@ struct CTMHeader { static_assert(sizeof(CTMHeader) == 256, "CTMHeader should be 256 bytes"); #pragma pack(pop) -bool Movie::IsPlayingInput() { +bool Movie::IsPlayingInput() const { return play_mode == PlayMode::Playing; } -bool Movie::IsRecordingInput() { +bool Movie::IsRecordingInput() const { return play_mode == PlayMode::Recording; } @@ -129,6 +129,7 @@ void Movie::CheckInputEnd() { if (current_byte + sizeof(ControllerState) > recorded_input.size()) { LOG_INFO(Movie, "Playback finished"); play_mode = PlayMode::None; + playback_completion_callback(); } } @@ -343,33 +344,35 @@ void Movie::Record(const Service::IR::ExtraHIDResponse& extra_hid_response) { Record(s); } -bool Movie::ValidateHeader(const CTMHeader& header) { +Movie::ValidationResult Movie::ValidateHeader(const CTMHeader& header) const { if (header_magic_bytes != header.filetype) { LOG_ERROR(Movie, "Playback file does not have valid header"); - return false; + return ValidationResult::Invalid; } std::string revision = Common::ArrayToString(header.revision.data(), header.revision.size(), 21, false); revision = Common::ToLower(revision); - if (revision != Common::g_scm_rev) { - LOG_WARNING(Movie, - "This movie was created on a different version of Citra, playback may desync"); - } - u64 program_id; Core::System::GetInstance().GetAppLoader().ReadProgramId(program_id); if (program_id != header.program_id) { LOG_WARNING(Movie, "This movie was recorded using a ROM with a different program id"); + return ValidationResult::GameDismatch; } - return true; + if (revision != Common::g_scm_rev) { + LOG_WARNING(Movie, + "This movie was created on a different version of Citra, playback may desync"); + return ValidationResult::RevisionDismatch; + } + + return ValidationResult::OK; } void Movie::SaveMovie() { - LOG_INFO(Movie, "Saving movie"); - FileUtil::IOFile save_record(Settings::values.movie_record, "wb"); + LOG_INFO(Movie, "Saving recorded movie to '{}'", record_movie_file); + FileUtil::IOFile save_record(record_movie_file, "wb"); if (!save_record.IsGood()) { LOG_ERROR(Movie, "Unable to open file to save movie"); @@ -394,31 +397,45 @@ void Movie::SaveMovie() { } } -void Movie::Init() { - if (!Settings::values.movie_play.empty()) { - LOG_INFO(Movie, "Loading Movie for playback"); - FileUtil::IOFile save_record(Settings::values.movie_play, "rb"); - u64 size = save_record.GetSize(); +void Movie::StartPlayback(const std::string& movie_file, + std::function completion_callback) { + LOG_INFO(Movie, "Loading Movie for playback"); + FileUtil::IOFile save_record(movie_file, "rb"); + const u64 size = save_record.GetSize(); - if (save_record.IsGood() && size > sizeof(CTMHeader)) { - CTMHeader header; - save_record.ReadArray(&header, 1); - if (ValidateHeader(header)) { - play_mode = PlayMode::Playing; - recorded_input.resize(size - sizeof(CTMHeader)); - save_record.ReadArray(recorded_input.data(), recorded_input.size()); - current_byte = 0; - } - } else { - LOG_ERROR(Movie, "Failed to playback movie: Unable to open '{}'", - Settings::values.movie_play); + if (save_record.IsGood() && size > sizeof(CTMHeader)) { + CTMHeader header; + save_record.ReadArray(&header, 1); + if (ValidateHeader(header) != ValidationResult::Invalid) { + play_mode = PlayMode::Playing; + recorded_input.resize(size - sizeof(CTMHeader)); + save_record.ReadArray(recorded_input.data(), recorded_input.size()); + current_byte = 0; + playback_completion_callback = completion_callback; } + } else { + LOG_ERROR(Movie, "Failed to playback movie: Unable to open '{}'", movie_file); + } +} + +void Movie::StartRecording(const std::string& movie_file) { + LOG_INFO(Movie, "Enabling Movie recording"); + play_mode = PlayMode::Recording; + record_movie_file = movie_file; +} + +Movie::ValidationResult Movie::ValidateMovie(const std::string& movie_file) const { + LOG_INFO(Movie, "Validating Movie file '{}'", movie_file); + FileUtil::IOFile save_record(movie_file, "rb"); + const u64 size = save_record.GetSize(); + + if (!save_record || size <= sizeof(CTMHeader)) { + return ValidationResult::Invalid; } - if (!Settings::values.movie_record.empty()) { - LOG_INFO(Movie, "Enabling Movie recording"); - play_mode = PlayMode::Recording; - } + CTMHeader header; + save_record.ReadArray(&header, 1); + return ValidateHeader(header); } void Movie::Shutdown() { @@ -428,6 +445,7 @@ void Movie::Shutdown() { play_mode = PlayMode::None; recorded_input.resize(0); + record_movie_file.clear(); current_byte = 0; } diff --git a/src/core/movie.h b/src/core/movie.h index 1f1ed6158..4a0d96b81 100644 --- a/src/core/movie.h +++ b/src/core/movie.h @@ -4,6 +4,7 @@ #pragma once +#include #include "common/common_types.h" namespace Service { @@ -26,6 +27,12 @@ enum class PlayMode; class Movie { public: + enum class ValidationResult { + OK, + RevisionDismatch, + GameDismatch, + Invalid, + }; /** * Gets the instance of the Movie singleton class. * @returns Reference to the instance of the Movie singleton class. @@ -34,7 +41,10 @@ public: return s_instance; } - void Init(); + void StartPlayback(const std::string& movie_file, + std::function completion_callback = {}); + void StartRecording(const std::string& movie_file); + ValidationResult ValidateMovie(const std::string& movie_file) const; void Shutdown(); @@ -74,14 +84,12 @@ public: * When playing: Replaces the given input states with the ones stored in the playback file */ void HandleExtraHidResponse(Service::IR::ExtraHIDResponse& extra_hid_response); + bool IsPlayingInput() const; + bool IsRecordingInput() const; private: static Movie s_instance; - bool IsPlayingInput(); - - bool IsRecordingInput(); - void CheckInputEnd(); template @@ -103,12 +111,14 @@ private: void Record(const Service::IR::PadState& pad_state, const s16& c_stick_x, const s16& c_stick_y); void Record(const Service::IR::ExtraHIDResponse& extra_hid_response); - bool ValidateHeader(const CTMHeader& header); + ValidationResult ValidateHeader(const CTMHeader& header) const; void SaveMovie(); PlayMode play_mode; + std::string record_movie_file; std::vector recorded_input; + std::function playback_completion_callback; size_t current_byte = 0; }; } // namespace Core \ No newline at end of file diff --git a/src/core/settings.h b/src/core/settings.h index 762246c33..90b5be6f8 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -156,10 +156,6 @@ struct Values { std::string log_filter; std::unordered_map lle_modules; - // Movie - std::string movie_play; - std::string movie_record; - // WebService bool enable_telemetry; std::string telemetry_endpoint_url;