mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	core/movie: Movie refactor, add a completion callback
This commit is contained in:
		
							parent
							
								
									6cb9a45154
								
							
						
					
					
						commit
						0f44f7b481
					
				
					 5 changed files with 77 additions and 47 deletions
				
			
		|  | @ -39,6 +39,7 @@ | ||||||
| #include "core/gdbstub/gdbstub.h" | #include "core/gdbstub/gdbstub.h" | ||||||
| #include "core/hle/service/am/am.h" | #include "core/hle/service/am/am.h" | ||||||
| #include "core/loader/loader.h" | #include "core/loader/loader.h" | ||||||
|  | #include "core/movie.h" | ||||||
| #include "core/settings.h" | #include "core/settings.h" | ||||||
| #include "network/network.h" | #include "network/network.h" | ||||||
| 
 | 
 | ||||||
|  | @ -268,8 +269,6 @@ int main(int argc, char** argv) { | ||||||
|     // Apply the command line arguments
 |     // Apply the command line arguments
 | ||||||
|     Settings::values.gdbstub_port = gdb_port; |     Settings::values.gdbstub_port = gdb_port; | ||||||
|     Settings::values.use_gdbstub = use_gdbstub; |     Settings::values.use_gdbstub = use_gdbstub; | ||||||
|     Settings::values.movie_play = std::move(movie_play); |  | ||||||
|     Settings::values.movie_record = std::move(movie_record); |  | ||||||
|     Settings::Apply(); |     Settings::Apply(); | ||||||
| 
 | 
 | ||||||
|     // Register frontend applets
 |     // 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()) { |     while (emu_window->IsOpen()) { | ||||||
|         system.RunLoop(); |         system.RunLoop(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     Core::Movie::GetInstance().Shutdown(); | ||||||
|  | 
 | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -176,7 +176,6 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { | ||||||
|     Kernel::Init(system_mode); |     Kernel::Init(system_mode); | ||||||
|     Service::Init(service_manager); |     Service::Init(service_manager); | ||||||
|     GDBStub::Init(); |     GDBStub::Init(); | ||||||
|     Movie::GetInstance().Init(); |  | ||||||
| 
 | 
 | ||||||
|     if (!VideoCore::Init(emu_window)) { |     if (!VideoCore::Init(emu_window)) { | ||||||
|         return ResultStatus::ErrorVideoCore; |         return ResultStatus::ErrorVideoCore; | ||||||
|  | @ -214,7 +213,6 @@ void System::Shutdown() { | ||||||
|                          perf_results.frametime * 1000.0); |                          perf_results.frametime * 1000.0); | ||||||
| 
 | 
 | ||||||
|     // Shutdown emulation session
 |     // Shutdown emulation session
 | ||||||
|     Movie::GetInstance().Shutdown(); |  | ||||||
|     GDBStub::Shutdown(); |     GDBStub::Shutdown(); | ||||||
|     VideoCore::Shutdown(); |     VideoCore::Shutdown(); | ||||||
|     Service::Shutdown(); |     Service::Shutdown(); | ||||||
|  |  | ||||||
|  | @ -118,10 +118,10 @@ struct CTMHeader { | ||||||
| static_assert(sizeof(CTMHeader) == 256, "CTMHeader should be 256 bytes"); | static_assert(sizeof(CTMHeader) == 256, "CTMHeader should be 256 bytes"); | ||||||
| #pragma pack(pop) | #pragma pack(pop) | ||||||
| 
 | 
 | ||||||
| bool Movie::IsPlayingInput() { | bool Movie::IsPlayingInput() const { | ||||||
|     return play_mode == PlayMode::Playing; |     return play_mode == PlayMode::Playing; | ||||||
| } | } | ||||||
| bool Movie::IsRecordingInput() { | bool Movie::IsRecordingInput() const { | ||||||
|     return play_mode == PlayMode::Recording; |     return play_mode == PlayMode::Recording; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -129,6 +129,7 @@ void Movie::CheckInputEnd() { | ||||||
|     if (current_byte + sizeof(ControllerState) > recorded_input.size()) { |     if (current_byte + sizeof(ControllerState) > recorded_input.size()) { | ||||||
|         LOG_INFO(Movie, "Playback finished"); |         LOG_INFO(Movie, "Playback finished"); | ||||||
|         play_mode = PlayMode::None; |         play_mode = PlayMode::None; | ||||||
|  |         playback_completion_callback(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -343,33 +344,35 @@ void Movie::Record(const Service::IR::ExtraHIDResponse& extra_hid_response) { | ||||||
|     Record(s); |     Record(s); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool Movie::ValidateHeader(const CTMHeader& header) { | Movie::ValidationResult Movie::ValidateHeader(const CTMHeader& header) const { | ||||||
|     if (header_magic_bytes != header.filetype) { |     if (header_magic_bytes != header.filetype) { | ||||||
|         LOG_ERROR(Movie, "Playback file does not have valid header"); |         LOG_ERROR(Movie, "Playback file does not have valid header"); | ||||||
|         return false; |         return ValidationResult::Invalid; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::string revision = |     std::string revision = | ||||||
|         Common::ArrayToString(header.revision.data(), header.revision.size(), 21, false); |         Common::ArrayToString(header.revision.data(), header.revision.size(), 21, false); | ||||||
|     revision = Common::ToLower(revision); |     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; |     u64 program_id; | ||||||
|     Core::System::GetInstance().GetAppLoader().ReadProgramId(program_id); |     Core::System::GetInstance().GetAppLoader().ReadProgramId(program_id); | ||||||
|     if (program_id != header.program_id) { |     if (program_id != header.program_id) { | ||||||
|         LOG_WARNING(Movie, "This movie was recorded using a ROM with a different 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() { | void Movie::SaveMovie() { | ||||||
|     LOG_INFO(Movie, "Saving movie"); |     LOG_INFO(Movie, "Saving recorded movie to '{}'", record_movie_file); | ||||||
|     FileUtil::IOFile save_record(Settings::values.movie_record, "wb"); |     FileUtil::IOFile save_record(record_movie_file, "wb"); | ||||||
| 
 | 
 | ||||||
|     if (!save_record.IsGood()) { |     if (!save_record.IsGood()) { | ||||||
|         LOG_ERROR(Movie, "Unable to open file to save movie"); |         LOG_ERROR(Movie, "Unable to open file to save movie"); | ||||||
|  | @ -394,31 +397,45 @@ void Movie::SaveMovie() { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Movie::Init() { | void Movie::StartPlayback(const std::string& movie_file, | ||||||
|     if (!Settings::values.movie_play.empty()) { |                           std::function<void()> completion_callback) { | ||||||
|         LOG_INFO(Movie, "Loading Movie for playback"); |     LOG_INFO(Movie, "Loading Movie for playback"); | ||||||
|         FileUtil::IOFile save_record(Settings::values.movie_play, "rb"); |     FileUtil::IOFile save_record(movie_file, "rb"); | ||||||
|         u64 size = save_record.GetSize(); |     const u64 size = save_record.GetSize(); | ||||||
| 
 | 
 | ||||||
|         if (save_record.IsGood() && size > sizeof(CTMHeader)) { |     if (save_record.IsGood() && size > sizeof(CTMHeader)) { | ||||||
|             CTMHeader header; |         CTMHeader header; | ||||||
|             save_record.ReadArray(&header, 1); |         save_record.ReadArray(&header, 1); | ||||||
|             if (ValidateHeader(header)) { |         if (ValidateHeader(header) != ValidationResult::Invalid) { | ||||||
|                 play_mode = PlayMode::Playing; |             play_mode = PlayMode::Playing; | ||||||
|                 recorded_input.resize(size - sizeof(CTMHeader)); |             recorded_input.resize(size - sizeof(CTMHeader)); | ||||||
|                 save_record.ReadArray(recorded_input.data(), recorded_input.size()); |             save_record.ReadArray(recorded_input.data(), recorded_input.size()); | ||||||
|                 current_byte = 0; |             current_byte = 0; | ||||||
|             } |             playback_completion_callback = completion_callback; | ||||||
|         } else { |  | ||||||
|             LOG_ERROR(Movie, "Failed to playback movie: Unable to open '{}'", |  | ||||||
|                       Settings::values.movie_play); |  | ||||||
|         } |         } | ||||||
|  |     } 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()) { |     CTMHeader header; | ||||||
|         LOG_INFO(Movie, "Enabling Movie recording"); |     save_record.ReadArray(&header, 1); | ||||||
|         play_mode = PlayMode::Recording; |     return ValidateHeader(header); | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Movie::Shutdown() { | void Movie::Shutdown() { | ||||||
|  | @ -428,6 +445,7 @@ void Movie::Shutdown() { | ||||||
| 
 | 
 | ||||||
|     play_mode = PlayMode::None; |     play_mode = PlayMode::None; | ||||||
|     recorded_input.resize(0); |     recorded_input.resize(0); | ||||||
|  |     record_movie_file.clear(); | ||||||
|     current_byte = 0; |     current_byte = 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <functional> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
| namespace Service { | namespace Service { | ||||||
|  | @ -26,6 +27,12 @@ enum class PlayMode; | ||||||
| 
 | 
 | ||||||
| class Movie { | class Movie { | ||||||
| public: | public: | ||||||
|  |     enum class ValidationResult { | ||||||
|  |         OK, | ||||||
|  |         RevisionDismatch, | ||||||
|  |         GameDismatch, | ||||||
|  |         Invalid, | ||||||
|  |     }; | ||||||
|     /**
 |     /**
 | ||||||
|      * Gets the instance of the Movie singleton class. |      * Gets the instance of the Movie singleton class. | ||||||
|      * @returns Reference to 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; |         return s_instance; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void Init(); |     void StartPlayback(const std::string& movie_file, | ||||||
|  |                        std::function<void()> completion_callback = {}); | ||||||
|  |     void StartRecording(const std::string& movie_file); | ||||||
|  |     ValidationResult ValidateMovie(const std::string& movie_file) const; | ||||||
| 
 | 
 | ||||||
|     void Shutdown(); |     void Shutdown(); | ||||||
| 
 | 
 | ||||||
|  | @ -74,14 +84,12 @@ public: | ||||||
|      * When playing: Replaces the given input states with the ones stored in the playback file |      * When playing: Replaces the given input states with the ones stored in the playback file | ||||||
|      */ |      */ | ||||||
|     void HandleExtraHidResponse(Service::IR::ExtraHIDResponse& extra_hid_response); |     void HandleExtraHidResponse(Service::IR::ExtraHIDResponse& extra_hid_response); | ||||||
|  |     bool IsPlayingInput() const; | ||||||
|  |     bool IsRecordingInput() const; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     static Movie s_instance; |     static Movie s_instance; | ||||||
| 
 | 
 | ||||||
|     bool IsPlayingInput(); |  | ||||||
| 
 |  | ||||||
|     bool IsRecordingInput(); |  | ||||||
| 
 |  | ||||||
|     void CheckInputEnd(); |     void CheckInputEnd(); | ||||||
| 
 | 
 | ||||||
|     template <typename... Targs> |     template <typename... Targs> | ||||||
|  | @ -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::PadState& pad_state, const s16& c_stick_x, const s16& c_stick_y); | ||||||
|     void Record(const Service::IR::ExtraHIDResponse& extra_hid_response); |     void Record(const Service::IR::ExtraHIDResponse& extra_hid_response); | ||||||
| 
 | 
 | ||||||
|     bool ValidateHeader(const CTMHeader& header); |     ValidationResult ValidateHeader(const CTMHeader& header) const; | ||||||
| 
 | 
 | ||||||
|     void SaveMovie(); |     void SaveMovie(); | ||||||
| 
 | 
 | ||||||
|     PlayMode play_mode; |     PlayMode play_mode; | ||||||
|  |     std::string record_movie_file; | ||||||
|     std::vector<u8> recorded_input; |     std::vector<u8> recorded_input; | ||||||
|  |     std::function<void()> playback_completion_callback; | ||||||
|     size_t current_byte = 0; |     size_t current_byte = 0; | ||||||
| }; | }; | ||||||
| } // namespace Core
 | } // namespace Core
 | ||||||
|  | @ -156,10 +156,6 @@ struct Values { | ||||||
|     std::string log_filter; |     std::string log_filter; | ||||||
|     std::unordered_map<std::string, bool> lle_modules; |     std::unordered_map<std::string, bool> lle_modules; | ||||||
| 
 | 
 | ||||||
|     // Movie
 |  | ||||||
|     std::string movie_play; |  | ||||||
|     std::string movie_record; |  | ||||||
| 
 |  | ||||||
|     // WebService
 |     // WebService
 | ||||||
|     bool enable_telemetry; |     bool enable_telemetry; | ||||||
|     std::string telemetry_endpoint_url; |     std::string telemetry_endpoint_url; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue