From 95cba6be2beda7fbf4fb597b0aa4cbb8912d8265 Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Sat, 13 Jan 2024 13:14:48 +0100 Subject: [PATCH] Core/Movie: Refactor to class, move to System. A bit of global state remains (the `header` in `BeginRecordingInput()`) due to unclear lifetime requirements. --- Source/Core/Core/BootManager.cpp | 7 +- Source/Core/Core/Core.cpp | 8 +- Source/Core/Core/DolphinAnalytics.cpp | 3 +- Source/Core/Core/HW/DVD/DVDInterface.cpp | 4 +- Source/Core/Core/HW/EXI/EXI.cpp | 6 +- Source/Core/Core/HW/EXI/EXI_Channel.cpp | 2 +- Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp | 5 +- .../Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp | 18 +- .../Core/Core/HW/EXI/EXI_DeviceMemoryCard.h | 6 +- Source/Core/Core/HW/SI/SI.cpp | 9 +- Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp | 3 +- Source/Core/Core/HW/SI/SI_DeviceGCAdapter.cpp | 3 +- .../Core/Core/HW/SI/SI_DeviceGCController.cpp | 21 +- .../Core/Core/HW/SI/SI_DeviceGCController.h | 8 +- Source/Core/Core/HW/VideoInterface.cpp | 2 +- Source/Core/Core/HW/Wiimote.cpp | 6 +- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 13 +- Source/Core/Core/IOS/FS/HostBackend/FS.cpp | 10 +- Source/Core/Core/Movie.cpp | 843 +++++++++--------- Source/Core/Core/Movie.h | 182 ++-- Source/Core/Core/NetPlayClient.cpp | 16 +- Source/Core/Core/State.cpp | 29 +- Source/Core/Core/System.cpp | 9 +- Source/Core/Core/System.h | 5 + Source/Core/Core/WiiRoot.cpp | 12 +- .../AchievementSettingsWidget.cpp | 7 +- Source/Core/DolphinQt/GBAWidget.cpp | 2 +- Source/Core/DolphinQt/MainWindow.cpp | 41 +- Source/Core/DolphinQt/MenuBar.cpp | 11 +- Source/Core/DolphinQt/TAS/TASCheckBox.cpp | 6 +- Source/Core/VideoCommon/OnScreenUI.cpp | 24 +- Source/Core/VideoCommon/VideoConfig.cpp | 5 +- 32 files changed, 717 insertions(+), 609 deletions(-) diff --git a/Source/Core/Core/BootManager.cpp b/Source/Core/Core/BootManager.cpp index 76d25aa088..72f525027f 100644 --- a/Source/Core/Core/BootManager.cpp +++ b/Source/Core/Core/BootManager.cpp @@ -66,11 +66,13 @@ bool BootCore(std::unique_ptr boot, const WindowSystemInfo& wsi) return false; // Movie settings - if (Movie::IsPlayingInput() && Movie::IsConfigSaved()) + auto& system = Core::System::GetInstance(); + auto& movie = system.GetMovie(); + if (movie.IsPlayingInput() && movie.IsConfigSaved()) { for (ExpansionInterface::Slot slot : ExpansionInterface::MEMCARD_SLOTS) { - if (Movie::IsUsingMemcard(slot) && Movie::IsStartingFromClearSave() && !StartUp.bWii) + if (movie.IsUsingMemcard(slot) && movie.IsStartingFromClearSave() && !StartUp.bWii) { const auto raw_path = File::GetUserPath(D_GCUSER_IDX) + @@ -142,7 +144,6 @@ bool BootCore(std::unique_ptr boot, const WindowSystemInfo& wsi) if (!boot->riivolution_patches.empty()) Config::SetCurrent(Config::MAIN_FAST_DISC_SPEED, true); - auto& system = Core::System::GetInstance(); system.Initialize(); Core::UpdateWantDeterminism(/*initial*/ true); diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 646fcfec47..e686fca30f 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -564,8 +564,8 @@ static void EmuThread(Core::System& system, std::unique_ptr boot system.GetCustomAssetLoader().Init(); Common::ScopeGuard asset_loader_guard([&system] { system.GetCustomAssetLoader().Shutdown(); }); - Movie::Init(*boot); - Common::ScopeGuard movie_guard{&Movie::Shutdown}; + system.GetMovie().Init(*boot); + Common::ScopeGuard movie_guard([&system] { system.GetMovie().Shutdown(); }); AudioCommon::InitSoundStream(system); Common::ScopeGuard audio_guard([&system] { AudioCommon::ShutdownSoundStream(system); }); @@ -1019,7 +1019,8 @@ void UpdateWantDeterminism(bool initial) // For now, this value is not itself configurable. Instead, individual // settings that depend on it, such as GPU determinism mode. should have // override options for testing, - bool new_want_determinism = Movie::IsMovieActive() || NetPlay::IsNetPlayRunning(); + auto& system = Core::System::GetInstance(); + bool new_want_determinism = system.GetMovie().IsMovieActive() || NetPlay::IsNetPlayRunning(); if (new_want_determinism != s_wants_determinism || initial) { NOTICE_LOG_FMT(COMMON, "Want determinism <- {}", new_want_determinism ? "true" : "false"); @@ -1030,7 +1031,6 @@ void UpdateWantDeterminism(bool initial) if (ios) ios->UpdateWantDeterminism(new_want_determinism); - auto& system = Core::System::GetInstance(); system.GetFifo().UpdateWantDeterminism(new_want_determinism); // We need to clear the cache because some parts of the JIT depend on want_determinism, diff --git a/Source/Core/Core/DolphinAnalytics.cpp b/Source/Core/Core/DolphinAnalytics.cpp index a8bc51aa69..5593a66963 100644 --- a/Source/Core/Core/DolphinAnalytics.cpp +++ b/Source/Core/Core/DolphinAnalytics.cpp @@ -34,6 +34,7 @@ #include "Core/HW/GCPad.h" #include "Core/Movie.h" #include "Core/NetPlayProto.h" +#include "Core/System.h" #include "InputCommon/GCAdapter.h" #include "InputCommon/InputConfig.h" #include "VideoCommon/VideoBackendBase.h" @@ -417,7 +418,7 @@ void DolphinAnalytics::MakePerGameBuilder() // NetPlay / recording. builder.AddData("netplay", NetPlay::IsNetPlayRunning()); - builder.AddData("movie", Movie::IsMovieActive()); + builder.AddData("movie", Core::System::GetInstance().GetMovie().IsMovieActive()); // Controller information // We grab enough to tell what percentage of our users are playing with keyboard/mouse, some kind diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index 2a7765d955..ae2f6e4e61 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -476,7 +476,7 @@ void DVDInterface::ChangeDisc(const std::string& new_path) m_disc_path_to_insert = new_path; m_system.GetCoreTiming().ScheduleEvent(m_system.GetSystemTimers().GetTicksPerSecond(), m_insert_disc); - Movie::SignalDiscChange(new_path); + m_system.GetMovie().SignalDiscChange(new_path); for (size_t i = 0; i < m_auto_disc_change_paths.size(); ++i) { @@ -1087,7 +1087,7 @@ void DVDInterface::ExecuteCommand(ReplyType reply_type) const bool force_eject = eject && !kill; - if (Config::Get(Config::MAIN_AUTO_DISC_CHANGE) && !Movie::IsPlayingInput() && + if (Config::Get(Config::MAIN_AUTO_DISC_CHANGE) && !m_system.GetMovie().IsPlayingInput() && m_system.GetDVDThread().IsInsertedDiscRunning() && !m_auto_disc_change_paths.empty()) { m_system.GetCoreTiming().ScheduleEvent( diff --git a/Source/Core/Core/HW/EXI/EXI.cpp b/Source/Core/Core/HW/EXI/EXI.cpp index d6c09b36fa..fd371fa076 100644 --- a/Source/Core/Core/HW/EXI/EXI.cpp +++ b/Source/Core/Core/HW/EXI/EXI.cpp @@ -36,9 +36,11 @@ ExpansionInterfaceManager::~ExpansionInterfaceManager() = default; void ExpansionInterfaceManager::AddMemoryCard(Slot slot) { EXIDeviceType memorycard_device; - if (Movie::IsPlayingInput() && Movie::IsConfigSaved()) + + auto& movie = m_system.GetMovie(); + if (movie.IsPlayingInput() && movie.IsConfigSaved()) { - if (Movie::IsUsingMemcard(slot)) + if (movie.IsUsingMemcard(slot)) { memorycard_device = Config::Get(Config::GetInfoForEXIDevice(slot)); if (memorycard_device != EXIDeviceType::MemoryCardFolder && diff --git a/Source/Core/Core/HW/EXI/EXI_Channel.cpp b/Source/Core/Core/HW/EXI/EXI_Channel.cpp index ca2d8f49cd..8d47a816e4 100644 --- a/Source/Core/Core/HW/EXI/EXI_Channel.cpp +++ b/Source/Core/Core/HW/EXI/EXI_Channel.cpp @@ -264,7 +264,7 @@ void CEXIChannel::DoState(PointerWrap& p) } if (type == EXIDeviceType::MemoryCardFolder && old_header_data != m_memcard_header_data && - !Movie::IsMovieActive()) + !m_system.GetMovie().IsMovieActive()) { // We have loaded a savestate that has a GCI folder memcard that is different to the virtual // card that is currently active. In order to prevent the game from recognizing this card as a diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp index 4667f9f69e..159df02f64 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp @@ -406,9 +406,10 @@ u32 CEXIIPL::GetEmulatedTime(Core::System& system, u32 epoch) { u64 ltime = 0; - if (Movie::IsMovieActive()) + auto& movie = system.GetMovie(); + if (movie.IsMovieActive()) { - ltime = Movie::GetRecordingStartTime(); + ltime = movie.GetRecordingStartTime(); // let's keep time moving forward, regardless of what it starts at ltime += system.GetCoreTiming().GetTicks() / system.GetSystemTimers().GetTicksPerSecond(); diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp index 03f5610007..cfc76f27db 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp @@ -149,7 +149,8 @@ CEXIMemoryCard::CEXIMemoryCard(Core::System& system, const Slot slot, bool gci_f } std::pair -CEXIMemoryCard::GetGCIFolderPath(Slot card_slot, AllowMovieFolder allow_movie_folder) +CEXIMemoryCard::GetGCIFolderPath(Slot card_slot, AllowMovieFolder allow_movie_folder, + Movie::MovieManager& movie) { std::string path_override = Config::Get(Config::GetInfoForGCIPathOverride(card_slot)); @@ -157,9 +158,8 @@ CEXIMemoryCard::GetGCIFolderPath(Slot card_slot, AllowMovieFolder allow_movie_fo return {std::move(path_override), false}; const bool use_movie_folder = allow_movie_folder == AllowMovieFolder::Yes && - Movie::IsPlayingInput() && Movie::IsConfigSaved() && - Movie::IsUsingMemcard(card_slot) && - Movie::IsStartingFromClearSave(); + movie.IsPlayingInput() && movie.IsConfigSaved() && + movie.IsUsingMemcard(card_slot) && movie.IsStartingFromClearSave(); const DiscIO::Region region = Config::ToGameCubeRegion(SConfig::GetInstance().m_region); if (use_movie_folder) @@ -182,7 +182,8 @@ void CEXIMemoryCard::SetupGciFolder(const Memcard::HeaderData& header_data) current_game_id = Common::swap32(reinterpret_cast(game_id.c_str())); } - const auto [dir_path, migrate] = GetGCIFolderPath(m_card_slot, AllowMovieFolder::Yes); + const auto [dir_path, migrate] = + GetGCIFolderPath(m_card_slot, AllowMovieFolder::Yes, m_system.GetMovie()); const File::FileInfo file_info(dir_path); if (!file_info.Exists()) @@ -219,8 +220,9 @@ void CEXIMemoryCard::SetupGciFolder(const Memcard::HeaderData& header_data) void CEXIMemoryCard::SetupRawMemcard(u16 size_mb) { std::string filename; - if (Movie::IsPlayingInput() && Movie::IsConfigSaved() && Movie::IsUsingMemcard(m_card_slot) && - Movie::IsStartingFromClearSave()) + auto& movie = m_system.GetMovie(); + if (movie.IsPlayingInput() && movie.IsConfigSaved() && movie.IsUsingMemcard(m_card_slot) && + movie.IsStartingFromClearSave()) { filename = File::GetUserPath(D_GCUSER_IDX) + fmt::format("Movie{}.raw", s_card_short_names[m_card_slot]); @@ -501,7 +503,7 @@ void CEXIMemoryCard::DoState(PointerWrap& p) // otherwise, we'll assume the user wants to keep their memcards and saves separate, // unless we're loading (in which case we let the savestate contents decide, in order to stay // aligned with them). - bool storeContents = (Movie::IsMovieActive()); + bool storeContents = m_system.GetMovie().IsMovieActive(); p.Do(storeContents); if (storeContents) diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h index cbb204d06a..2264abb076 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h @@ -26,6 +26,10 @@ namespace Memcard { struct HeaderData; } +namespace Movie +{ +class MovieManager; +} namespace ExpansionInterface { @@ -58,7 +62,7 @@ public: static void Shutdown(); static std::pair - GetGCIFolderPath(Slot card_slot, AllowMovieFolder allow_movie_folder); + GetGCIFolderPath(Slot card_slot, AllowMovieFolder allow_movie_folder, Movie::MovieManager& movie); private: void SetupGciFolder(const Memcard::HeaderData& header_data); diff --git a/Source/Core/Core/HW/SI/SI.cpp b/Source/Core/Core/HW/SI/SI.cpp index eeebf291d5..423f373337 100644 --- a/Source/Core/Core/HW/SI/SI.cpp +++ b/Source/Core/Core/HW/SI/SI.cpp @@ -265,19 +265,20 @@ void SerialInterfaceManager::Init() m_channel[i].in_lo.hex = 0; m_channel[i].has_recent_device_change = false; - if (Movie::IsMovieActive()) + auto& movie = m_system.GetMovie(); + if (movie.IsMovieActive()) { m_desired_device_types[i] = SIDEVICE_NONE; - if (Movie::IsUsingGBA(i)) + if (movie.IsUsingGBA(i)) { m_desired_device_types[i] = SIDEVICE_GC_GBA_EMULATED; } - else if (Movie::IsUsingPad(i)) + else if (movie.IsUsingPad(i)) { SIDevices current = Config::Get(Config::GetInfoForSIDevice(i)); // GC pad-compatible devices can be used for both playing and recording - if (Movie::IsUsingBongo(i)) + if (movie.IsUsingBongo(i)) m_desired_device_types[i] = SIDEVICE_GC_TARUKONGA; else if (SIDevice_IsGCController(current)) m_desired_device_types[i] = current; diff --git a/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp b/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp index 4bb0e69617..10522842de 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp +++ b/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp @@ -125,7 +125,8 @@ bool CSIDevice_GBAEmu::GetData(u32& hi, u32& low) GCPadStatus pad_status{}; if (!NetPlay::IsNetPlayRunning()) pad_status = Pad::GetGBAStatus(m_device_number); - SerialInterface::CSIDevice_GCController::HandleMoviePadStatus(m_device_number, &pad_status); + SerialInterface::CSIDevice_GCController::HandleMoviePadStatus(m_system.GetMovie(), + m_device_number, &pad_status); static constexpr std::array buttons_map = { PadButton::PAD_BUTTON_A, // A diff --git a/Source/Core/Core/HW/SI/SI_DeviceGCAdapter.cpp b/Source/Core/Core/HW/SI/SI_DeviceGCAdapter.cpp index 8a06fa1ff0..88747062f8 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceGCAdapter.cpp +++ b/Source/Core/Core/HW/SI/SI_DeviceGCAdapter.cpp @@ -11,6 +11,7 @@ #include "Core/Core.h" #include "Core/HW/GCPad.h" #include "Core/NetPlayProto.h" +#include "Core/System.h" #include "InputCommon/GCAdapter.h" namespace SerialInterface @@ -38,7 +39,7 @@ GCPadStatus CSIDevice_GCAdapter::GetPadStatus() pad_status = GCAdapter::Input(m_device_number); } - HandleMoviePadStatus(m_device_number, &pad_status); + HandleMoviePadStatus(m_system.GetMovie(), m_device_number, &pad_status); // Our GCAdapter code sets PAD_GET_ORIGIN when a new device has been connected. // Watch for this to calibrate real controllers on connection. diff --git a/Source/Core/Core/HW/SI/SI_DeviceGCController.cpp b/Source/Core/Core/HW/SI/SI_DeviceGCController.cpp index fde127f3f6..f8150a6339 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceGCController.cpp +++ b/Source/Core/Core/HW/SI/SI_DeviceGCController.cpp @@ -120,25 +120,26 @@ int CSIDevice_GCController::RunBuffer(u8* buffer, int request_length) return 0; } -void CSIDevice_GCController::HandleMoviePadStatus(int device_number, GCPadStatus* pad_status) +void CSIDevice_GCController::HandleMoviePadStatus(Movie::MovieManager& movie, int device_number, + GCPadStatus* pad_status) { - Movie::SetPolledDevice(); + movie.SetPolledDevice(); if (NetPlay_GetInput(device_number, pad_status)) { } - else if (Movie::IsPlayingInput()) + else if (movie.IsPlayingInput()) { - Movie::PlayController(pad_status, device_number); - Movie::InputUpdate(); + movie.PlayController(pad_status, device_number); + movie.InputUpdate(); } - else if (Movie::IsRecordingInput()) + else if (movie.IsRecordingInput()) { - Movie::RecordInput(pad_status, device_number); - Movie::InputUpdate(); + movie.RecordInput(pad_status, device_number); + movie.InputUpdate(); } else { - Movie::CheckPadStatus(pad_status, device_number); + movie.CheckPadStatus(pad_status, device_number); } } @@ -153,7 +154,7 @@ GCPadStatus CSIDevice_GCController::GetPadStatus() pad_status = Pad::GetStatus(m_device_number); } - HandleMoviePadStatus(m_device_number, &pad_status); + HandleMoviePadStatus(m_system.GetMovie(), m_device_number, &pad_status); // Our GCAdapter code sets PAD_GET_ORIGIN when a new device has been connected. // Watch for this to calibrate real controllers on connection. diff --git a/Source/Core/Core/HW/SI/SI_DeviceGCController.h b/Source/Core/Core/HW/SI/SI_DeviceGCController.h index 43e3d7aabb..6c8996b53f 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceGCController.h +++ b/Source/Core/Core/HW/SI/SI_DeviceGCController.h @@ -9,6 +9,11 @@ #include "Core/HW/SI/SI_Device.h" #include "InputCommon/GCPadStatus.h" +namespace Movie +{ +class MovieManager; +} + namespace SerialInterface { class CSIDevice_GCController : public ISIDevice @@ -78,7 +83,8 @@ public: // Direct rumble to the right GC Controller static void Rumble(int pad_num, ControlState strength, SIDevices device); - static void HandleMoviePadStatus(int device_number, GCPadStatus* pad_status); + static void HandleMoviePadStatus(Movie::MovieManager& movie, int device_number, + GCPadStatus* pad_status); protected: void SetOrigin(const GCPadStatus& pad_status); diff --git a/Source/Core/Core/HW/VideoInterface.cpp b/Source/Core/Core/HW/VideoInterface.cpp index 1217b789d9..83a30cbbcf 100644 --- a/Source/Core/Core/HW/VideoInterface.cpp +++ b/Source/Core/Core/HW/VideoInterface.cpp @@ -852,7 +852,7 @@ void VideoInterfaceManager::Update(u64 ticks) // in case frame counter display is enabled if (m_half_line_count == 0 || m_half_line_count == GetHalfLinesPerEvenField()) - Movie::FrameUpdate(); + m_system.GetMovie().FrameUpdate(); // If this half-line is at some boundary of the "active video lines" in either field, we either // need to (a) send a request to the GPU thread to actually render the XFB, or (b) increment diff --git a/Source/Core/Core/HW/Wiimote.cpp b/Source/Core/Core/HW/Wiimote.cpp index 13ef2139f6..86f9c6a56c 100644 --- a/Source/Core/Core/HW/Wiimote.cpp +++ b/Source/Core/Core/HW/Wiimote.cpp @@ -19,6 +19,7 @@ #include "Core/IOS/USB/Bluetooth/WiimoteDevice.h" #include "Core/Movie.h" #include "Core/NetPlayClient.h" +#include "Core/System.h" #include "Core/WiiUtils.h" #include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h" @@ -192,8 +193,9 @@ void Initialize(InitializeMode init_mode) WiimoteReal::Initialize(init_mode); // Reload Wiimotes with our settings - if (Movie::IsMovieActive()) - Movie::ChangeWiiPads(); + auto& movie = Core::System::GetInstance().GetMovie(); + if (movie.IsMovieActive()) + movie.ChangeWiiPads(); } void ResetAllWiimotes() diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index 1baccc4e81..e74e807898 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -23,6 +23,7 @@ #include "Core/Core.h" #include "Core/HW/Wiimote.h" #include "Core/Movie.h" +#include "Core/System.h" #include "Core/HW/WiimoteCommon/WiimoteConstants.h" #include "Core/HW/WiimoteCommon/WiimoteHid.h" @@ -552,7 +553,8 @@ void Wiimote::Update(const WiimoteEmu::DesiredWiimoteState& target_state) void Wiimote::SendDataReport(const DesiredWiimoteState& target_state) { - Movie::SetPolledDevice(); + auto& movie = Core::System::GetInstance().GetMovie(); + movie.SetPolledDevice(); if (InputReportID::ReportDisabled == m_reporting_mode) { @@ -569,9 +571,8 @@ void Wiimote::SendDataReport(const DesiredWiimoteState& target_state) DataReportBuilder rpt_builder(m_reporting_mode); - if (Movie::IsPlayingInput() && - Movie::PlayWiimote(m_bt_device_index, rpt_builder, m_active_extension, - GetExtensionEncryptionKey())) + if (movie.IsPlayingInput() && movie.PlayWiimote(m_bt_device_index, rpt_builder, + m_active_extension, GetExtensionEncryptionKey())) { // Update buttons in status struct from movie: rpt_builder.GetCoreData(&m_status.buttons); @@ -640,8 +641,8 @@ void Wiimote::SendDataReport(const DesiredWiimoteState& target_state) } } - Movie::CheckWiimoteStatus(m_bt_device_index, rpt_builder, m_active_extension, - GetExtensionEncryptionKey()); + movie.CheckWiimoteStatus(m_bt_device_index, rpt_builder, m_active_extension, + GetExtensionEncryptionKey()); // Send the report: InterruptDataInputCallback(rpt_builder.GetDataPtr(), rpt_builder.GetDataSize()); diff --git a/Source/Core/Core/IOS/FS/HostBackend/FS.cpp b/Source/Core/Core/IOS/FS/HostBackend/FS.cpp index a916ffba81..503a8c4607 100644 --- a/Source/Core/Core/IOS/FS/HostBackend/FS.cpp +++ b/Source/Core/Core/IOS/FS/HostBackend/FS.cpp @@ -23,6 +23,7 @@ #include "Core/IOS/ES/ES.h" #include "Core/IOS/IOS.h" #include "Core/Movie.h" +#include "Core/System.h" #include "Core/WiiRoot.h" namespace IOS::HLE::FS @@ -391,8 +392,9 @@ void HostFileSystem::DoState(PointerWrap& p) // then a call to p.DoExternal() will be used to skip over reading the contents of the "/" // directory (it skips over the number of bytes specified by size_of_nand_folder_saved) + auto& movie = Core::System::GetInstance().GetMovie(); bool original_save_state_made_during_movie_recording = - Movie::IsMovieActive() && Core::WiiRootIsTemporary(); + movie.IsMovieActive() && Core::WiiRootIsTemporary(); p.Do(original_save_state_made_during_movie_recording); u32 temp_val = 0; @@ -414,17 +416,17 @@ void HostFileSystem::DoState(PointerWrap& p) else // case where we're in read mode. { DoStateRead(p, "/tmp"); - if (!Movie::IsMovieActive() || !original_save_state_made_during_movie_recording || + if (!movie.IsMovieActive() || !original_save_state_made_during_movie_recording || !Core::WiiRootIsTemporary() || (original_save_state_made_during_movie_recording != - (Movie::IsMovieActive() && Core::WiiRootIsTemporary()))) + (movie.IsMovieActive() && Core::WiiRootIsTemporary()))) { (void)p.DoExternal(temp_val); } else { p.Do(temp_val); - if (Movie::IsMovieActive() && Core::WiiRootIsTemporary()) + if (movie.IsMovieActive() && Core::WiiRootIsTemporary()) DoStateRead(p, "/"); } } diff --git a/Source/Core/Core/Movie.cpp b/Source/Core/Core/Movie.cpp index fb96379748..84b6ddc9f6 100644 --- a/Source/Core/Core/Movie.cpp +++ b/Source/Core/Core/Movie.cpp @@ -82,51 +82,6 @@ namespace Movie using namespace WiimoteCommon; using namespace WiimoteEmu; -enum class PlayMode -{ - None = 0, - Recording, - Playing, -}; - -static bool s_bReadOnly = true; -static u32 s_rerecords = 0; -static PlayMode s_playMode = PlayMode::None; - -static std::array s_controllers{}; -static std::array s_wiimotes{}; -static ControllerState s_padState; -static DTMHeader tmpHeader; -static std::vector s_temp_input; -static u64 s_currentByte = 0; -static u64 s_currentFrame = 0, s_totalFrames = 0; // VI -static u64 s_currentLagCount = 0; -static u64 s_totalLagCount = 0; // just stats -static u64 s_currentInputCount = 0, s_totalInputCount = 0; // just stats -static u64 s_totalTickCount = 0, s_tickCountAtLastInput = 0; // just stats -static u64 s_recordingStartTime; // seconds since 1970 that recording started -static bool s_bSaveConfig = false, s_bNetPlay = false; -static bool s_bClearSave = false; -static bool s_bDiscChange = false; -static bool s_bReset = false; -static std::string s_author; -static std::string s_discChange; -static std::array s_MD5; -static u8 s_bongos, s_memcards; -static std::array s_revision; -static u32 s_DSPiromHash = 0; -static u32 s_DSPcoefHash = 0; - -static bool s_bRecordingFromSaveState = false; -static bool s_bPolled = false; - -// s_InputDisplay is used by both CPU and GPU (is mutable). -static std::mutex s_input_display_lock; -static std::string s_InputDisplay[8]; - -static std::string s_current_file_name; - -static void GetSettings(); static bool IsMovieHeader(const std::array& magic) { return magic[0] == 'D' && magic[1] == 'T' && magic[2] == 'M' && magic[3] == 0x1A; @@ -161,45 +116,51 @@ static std::array ConvertGitRevisionToBytes(const std::string& revision) return revision_bytes; } +MovieManager::MovieManager(Core::System& system) : m_system(system) +{ +} + +MovieManager::~MovieManager() = default; + // NOTE: GPU Thread -std::string GetInputDisplay() +std::string MovieManager::GetInputDisplay() { if (!IsMovieActive()) { - s_controllers = {}; - s_wiimotes = {}; + m_controllers = {}; + m_wiimotes = {}; for (int i = 0; i < 4; ++i) { auto& si = Core::System::GetInstance().GetSerialInterface(); if (si.GetDeviceType(i) == SerialInterface::SIDEVICE_GC_GBA_EMULATED) - s_controllers[i] = ControllerType::GBA; + m_controllers[i] = ControllerType::GBA; else if (si.GetDeviceType(i) != SerialInterface::SIDEVICE_NONE) - s_controllers[i] = ControllerType::GC; + m_controllers[i] = ControllerType::GC; else - s_controllers[i] = ControllerType::None; - s_wiimotes[i] = Config::Get(Config::GetInfoForWiimoteSource(i)) != WiimoteSource::None; + m_controllers[i] = ControllerType::None; + m_wiimotes[i] = Config::Get(Config::GetInfoForWiimoteSource(i)) != WiimoteSource::None; } } std::string input_display; { - std::lock_guard guard(s_input_display_lock); + std::lock_guard guard(m_input_display_lock); for (int i = 0; i < 4; ++i) { if (IsUsingPad(i)) - input_display += s_InputDisplay[i] + '\n'; + input_display += m_input_display[i] + '\n'; } for (int i = 0; i < 4; ++i) { if (IsUsingWiimote(i)) - input_display += s_InputDisplay[i + 4] + '\n'; + input_display += m_input_display[i + 4] + '\n'; } } return input_display; } // NOTE: GPU Thread -std::string GetRTCDisplay() +std::string MovieManager::GetRTCDisplay() { using ExpansionInterface::CEXIIPL; @@ -213,53 +174,50 @@ std::string GetRTCDisplay() } // NOTE: GPU Thread -std::string GetRerecords() +std::string MovieManager::GetRerecords() { if (IsMovieActive()) - return fmt::format("Rerecords: {}", s_rerecords); + return fmt::format("Rerecords: {}", m_rerecords); return "Rerecords: N/A"; } -void FrameUpdate() +void MovieManager::FrameUpdate() { - s_currentFrame++; - if (!s_bPolled) - s_currentLagCount++; + m_current_frame++; + if (!m_polled) + m_current_lag_count++; if (IsRecordingInput()) { - s_totalFrames = s_currentFrame; - s_totalLagCount = s_currentLagCount; + m_total_frames = m_current_frame; + m_total_lag_count = m_current_lag_count; } - s_bPolled = false; + m_polled = false; } -static void CheckMD5(); -static void GetMD5(); - // called when game is booting up, even if no movie is active, // but potentially after BeginRecordingInput or PlayInput has been called. // NOTE: EmuThread -void Init(const BootParameters& boot) +void MovieManager::Init(const BootParameters& boot) { if (std::holds_alternative(boot.parameters)) - s_current_file_name = std::get(boot.parameters).path; + m_current_file_name = std::get(boot.parameters).path; else - s_current_file_name.clear(); + m_current_file_name.clear(); - s_bPolled = false; - s_bSaveConfig = false; + m_polled = false; + m_save_config = false; if (IsPlayingInput()) { ReadHeader(); - std::thread md5thread(CheckMD5); + std::thread md5thread(&MovieManager::CheckMD5, this); md5thread.detach(); - if (strncmp(tmpHeader.gameID.data(), SConfig::GetInstance().GetGameID().c_str(), 6)) + if (strncmp(m_temp_header.gameID.data(), SConfig::GetInstance().GetGameID().c_str(), 6)) { PanicAlertFmtT("The recorded game ({0}) is not the same as the selected game ({1})", - tmpHeader.GetGameID(), SConfig::GetInstance().GetGameID()); + m_temp_header.GetGameID(), SConfig::GetInstance().GetGameID()); EndPlayInput(false); } } @@ -267,135 +225,135 @@ void Init(const BootParameters& boot) if (IsRecordingInput()) { GetSettings(); - std::thread md5thread(GetMD5); + std::thread md5thread(&MovieManager::GetMD5, this); md5thread.detach(); - s_tickCountAtLastInput = 0; + m_tick_count_at_last_input = 0; } - memset(&s_padState, 0, sizeof(s_padState)); + memset(&m_pad_state, 0, sizeof(m_pad_state)); - for (auto& disp : s_InputDisplay) + for (auto& disp : m_input_display) disp.clear(); if (!IsMovieActive()) { - s_bRecordingFromSaveState = false; - s_rerecords = 0; - s_currentByte = 0; - s_currentFrame = 0; - s_currentLagCount = 0; - s_currentInputCount = 0; + m_recording_from_save_state = false; + m_rerecords = 0; + m_current_byte = 0; + m_current_frame = 0; + m_current_lag_count = 0; + m_current_input_count = 0; } } // NOTE: CPU Thread -void InputUpdate() +void MovieManager::InputUpdate() { - s_currentInputCount++; + m_current_input_count++; if (IsRecordingInput()) { auto& system = Core::System::GetInstance(); auto& core_timing = system.GetCoreTiming(); - s_totalInputCount = s_currentInputCount; - s_totalTickCount += core_timing.GetTicks() - s_tickCountAtLastInput; - s_tickCountAtLastInput = core_timing.GetTicks(); + m_total_input_count = m_current_input_count; + m_total_tick_count += core_timing.GetTicks() - m_tick_count_at_last_input; + m_tick_count_at_last_input = core_timing.GetTicks(); } } // NOTE: CPU Thread -void SetPolledDevice() +void MovieManager::SetPolledDevice() { - s_bPolled = true; + m_polled = true; } // NOTE: Host Thread -void SetReadOnly(bool bEnabled) +void MovieManager::SetReadOnly(bool bEnabled) { - if (s_bReadOnly != bEnabled) + if (m_read_only != bEnabled) Core::DisplayMessage(bEnabled ? "Read-only mode." : "Read+Write mode.", 1000); - s_bReadOnly = bEnabled; + m_read_only = bEnabled; } -bool IsRecordingInput() +bool MovieManager::IsRecordingInput() const { - return (s_playMode == PlayMode::Recording); + return (m_play_mode == PlayMode::Recording); } -bool IsRecordingInputFromSaveState() +bool MovieManager::IsRecordingInputFromSaveState() const { - return s_bRecordingFromSaveState; + return m_recording_from_save_state; } -bool IsJustStartingRecordingInputFromSaveState() +bool MovieManager::IsJustStartingRecordingInputFromSaveState() const { - return IsRecordingInputFromSaveState() && s_currentFrame == 0; + return IsRecordingInputFromSaveState() && m_current_frame == 0; } -bool IsJustStartingPlayingInputFromSaveState() +bool MovieManager::IsJustStartingPlayingInputFromSaveState() const { - return IsRecordingInputFromSaveState() && s_currentFrame == 1 && IsPlayingInput(); + return IsRecordingInputFromSaveState() && m_current_frame == 1 && IsPlayingInput(); } -bool IsPlayingInput() +bool MovieManager::IsPlayingInput() const { - return (s_playMode == PlayMode::Playing); + return (m_play_mode == PlayMode::Playing); } -bool IsMovieActive() +bool MovieManager::IsMovieActive() const { - return s_playMode != PlayMode::None; + return m_play_mode != PlayMode::None; } -bool IsReadOnly() +bool MovieManager::IsReadOnly() const { - return s_bReadOnly; + return m_read_only; } -u64 GetRecordingStartTime() +u64 MovieManager::GetRecordingStartTime() const { - return s_recordingStartTime; + return m_recording_start_time; } -u64 GetCurrentFrame() +u64 MovieManager::GetCurrentFrame() const { - return s_currentFrame; + return m_current_frame; } -u64 GetTotalFrames() +u64 MovieManager::GetTotalFrames() const { - return s_totalFrames; + return m_total_frames; } -u64 GetCurrentInputCount() +u64 MovieManager::GetCurrentInputCount() const { - return s_currentInputCount; + return m_current_input_count; } -u64 GetTotalInputCount() +u64 MovieManager::GetTotalInputCount() const { - return s_totalInputCount; + return m_total_input_count; } -u64 GetCurrentLagCount() +u64 MovieManager::GetCurrentLagCount() const { - return s_currentLagCount; + return m_current_lag_count; } -u64 GetTotalLagCount() +u64 MovieManager::GetTotalLagCount() const { - return s_totalLagCount; + return m_total_lag_count; } -void SetClearSave(bool enabled) +void MovieManager::SetClearSave(bool enabled) { - s_bClearSave = enabled; + m_clear_save = enabled; } -void SignalDiscChange(const std::string& new_path) +void MovieManager::SignalDiscChange(const std::string& new_path) { - if (Movie::IsRecordingInput()) + if (IsRecordingInput()) { size_t size_of_path_without_filename = new_path.find_last_of("/\\") + 1; std::string filename = new_path.substr(size_of_path_without_filename); @@ -406,66 +364,66 @@ void SignalDiscChange(const std::string& new_path) "The filename of the disc image must not be longer than 40 characters.", filename); } - s_discChange = filename; - s_bDiscChange = true; + m_disc_change_filename = filename; + m_has_disc_change = true; } } -void SetReset(bool reset) +void MovieManager::SetReset(bool reset) { - s_bReset = reset; + m_reset = reset; } -bool IsUsingPad(int controller) +bool MovieManager::IsUsingPad(int controller) const { - return s_controllers[controller] != ControllerType::None; + return m_controllers[controller] != ControllerType::None; } -bool IsUsingBongo(int controller) +bool MovieManager::IsUsingBongo(int controller) const { - return ((s_bongos & (1 << controller)) != 0); + return ((m_bongos & (1 << controller)) != 0); } -bool IsUsingGBA(int controller) +bool MovieManager::IsUsingGBA(int controller) const { - return s_controllers[controller] == ControllerType::GBA; + return m_controllers[controller] == ControllerType::GBA; } -bool IsUsingWiimote(int wiimote) +bool MovieManager::IsUsingWiimote(int wiimote) const { - return s_wiimotes[wiimote]; + return m_wiimotes[wiimote]; } -bool IsConfigSaved() +bool MovieManager::IsConfigSaved() const { - return s_bSaveConfig; + return m_save_config; } -bool IsStartingFromClearSave() +bool MovieManager::IsStartingFromClearSave() const { - return s_bClearSave; + return m_clear_save; } -bool IsUsingMemcard(ExpansionInterface::Slot slot) +bool MovieManager::IsUsingMemcard(ExpansionInterface::Slot slot) const { switch (slot) { case ExpansionInterface::Slot::A: - return (s_memcards & 1) != 0; + return (m_memcards & 1) != 0; case ExpansionInterface::Slot::B: - return (s_memcards & 2) != 0; + return (m_memcards & 2) != 0; default: return false; } } -bool IsNetPlayRecording() +bool MovieManager::IsNetPlayRecording() const { - return s_bNetPlay; + return m_net_play; } // NOTE: Host Thread -void ChangePads() +void MovieManager::ChangePads() { if (!Core::IsRunning()) return; @@ -483,7 +441,7 @@ void ChangePads() controllers[i] = ControllerType::None; } - if (s_controllers == controllers) + if (m_controllers == controllers) return; auto& si = Core::System::GetInstance().GetSerialInterface(); @@ -513,7 +471,7 @@ void ChangePads() } // NOTE: Host / Emu Threads -void ChangeWiiPads(bool instantly) +void MovieManager::ChangeWiiPads(bool instantly) { WiimoteEnabledArray wiimotes{}; @@ -523,7 +481,7 @@ void ChangeWiiPads(bool instantly) } // This is important for Wiimotes, because they can desync easily if they get re-activated - if (instantly && s_wiimotes == wiimotes) + if (instantly && m_wiimotes == wiimotes) return; const auto bt = WiiUtils::GetBluetoothEmuDevice(); @@ -539,43 +497,43 @@ void ChangeWiiPads(bool instantly) } // NOTE: Host Thread -bool BeginRecordingInput(const ControllerTypeArray& controllers, - const WiimoteEnabledArray& wiimotes) +bool MovieManager::BeginRecordingInput(const ControllerTypeArray& controllers, + const WiimoteEnabledArray& wiimotes) { - if (s_playMode != PlayMode::None || + if (m_play_mode != PlayMode::None || (controllers == ControllerTypeArray{} && wiimotes == WiimoteEnabledArray{})) return false; - const auto start_recording = [controllers, wiimotes] { - s_controllers = controllers; - s_wiimotes = wiimotes; - s_currentFrame = s_totalFrames = 0; - s_currentLagCount = s_totalLagCount = 0; - s_currentInputCount = s_totalInputCount = 0; - s_totalTickCount = s_tickCountAtLastInput = 0; - s_bongos = 0; - s_memcards = 0; + const auto start_recording = [this, controllers, wiimotes] { + m_controllers = controllers; + m_wiimotes = wiimotes; + m_current_frame = m_total_frames = 0; + m_current_lag_count = m_total_lag_count = 0; + m_current_input_count = m_total_input_count = 0; + m_total_tick_count = m_tick_count_at_last_input = 0; + m_bongos = 0; + m_memcards = 0; if (NetPlay::IsNetPlayRunning()) { - s_bNetPlay = true; - s_recordingStartTime = ExpansionInterface::CEXIIPL::NetPlay_GetEmulatedTime(); + m_net_play = true; + m_recording_start_time = ExpansionInterface::CEXIIPL::NetPlay_GetEmulatedTime(); } else if (Config::Get(Config::MAIN_CUSTOM_RTC_ENABLE)) { - s_recordingStartTime = Config::Get(Config::MAIN_CUSTOM_RTC_VALUE); + m_recording_start_time = Config::Get(Config::MAIN_CUSTOM_RTC_VALUE); } else { - s_recordingStartTime = Common::Timer::GetLocalTimeSinceJan1970(); + m_recording_start_time = Common::Timer::GetLocalTimeSinceJan1970(); } - s_rerecords = 0; + m_rerecords = 0; for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i) { const SerialInterface::SIDevices si_device = Config::Get(Config::GetInfoForSIDevice(i)); if (si_device == SerialInterface::SIDEVICE_GC_TARUKONGA) - s_bongos |= (1 << i); + m_bongos |= (1 << i); } if (Core::IsRunningAndStarted()) @@ -585,9 +543,9 @@ bool BeginRecordingInput(const ControllerTypeArray& controllers, File::Delete(save_path); State::SaveAs(save_path); - s_bRecordingFromSaveState = true; + m_recording_from_save_state = true; - std::thread md5thread(GetMD5); + std::thread md5thread(&MovieManager::GetMD5, this); md5thread.detach(); GetSettings(); } @@ -599,11 +557,11 @@ bool BeginRecordingInput(const ControllerTypeArray& controllers, Wiimote::ResetAllWiimotes(); } - s_playMode = PlayMode::Recording; - s_author = Config::Get(Config::MAIN_MOVIE_MOVIE_AUTHOR); - s_temp_input.clear(); + m_play_mode = PlayMode::Recording; + m_author = Config::Get(Config::MAIN_MOVIE_MOVIE_AUTHOR); + m_temp_input.clear(); - s_currentByte = 0; + m_current_byte = 0; // This is a bit of a hack, SYSCONF movie code expects the movie layer active for both recording // and playback. That layer is really only designed for playback, not recording. Also, we can't @@ -663,7 +621,7 @@ static std::string Analog1DToString(u32 v, const std::string& prefix, u32 range } // NOTE: CPU Thread -static void SetInputDisplayString(ControllerState padState, int controllerID) +static std::string GenerateInputDisplayString(ControllerState padState, int controllerID) { std::string display_str = fmt::format("P{}:", controllerID + 1); @@ -711,16 +669,13 @@ static void SetInputDisplayString(ControllerState padState, int controllerID) display_str += " DISCONNECTED"; } - std::lock_guard guard(s_input_display_lock); - s_InputDisplay[controllerID] = std::move(display_str); + return display_str; } // NOTE: CPU Thread -static void SetWiiInputDisplayString(int remoteID, const DataReportBuilder& rpt, - ExtensionNumber ext, const EncryptionKey& key) +static std::string GenerateWiiInputDisplayString(int remoteID, const DataReportBuilder& rpt, + ExtensionNumber ext, const EncryptionKey& key) { - int controllerID = remoteID + 4; - std::string display_str = fmt::format("R{}:", remoteID + 1); if (rpt.HasCore()) @@ -842,133 +797,143 @@ static void SetWiiInputDisplayString(int remoteID, const DataReportBuilder& rpt, display_str += Analog2DToString(right_stick.x, right_stick.y, " R-ANA", 31); } - std::lock_guard guard(s_input_display_lock); - s_InputDisplay[controllerID] = std::move(display_str); + return display_str; } // NOTE: CPU Thread -void CheckPadStatus(const GCPadStatus* PadStatus, int controllerID) +void MovieManager::CheckPadStatus(const GCPadStatus* PadStatus, int controllerID) { - s_padState.A = ((PadStatus->button & PAD_BUTTON_A) != 0); - s_padState.B = ((PadStatus->button & PAD_BUTTON_B) != 0); - s_padState.X = ((PadStatus->button & PAD_BUTTON_X) != 0); - s_padState.Y = ((PadStatus->button & PAD_BUTTON_Y) != 0); - s_padState.Z = ((PadStatus->button & PAD_TRIGGER_Z) != 0); - s_padState.Start = ((PadStatus->button & PAD_BUTTON_START) != 0); + m_pad_state.A = ((PadStatus->button & PAD_BUTTON_A) != 0); + m_pad_state.B = ((PadStatus->button & PAD_BUTTON_B) != 0); + m_pad_state.X = ((PadStatus->button & PAD_BUTTON_X) != 0); + m_pad_state.Y = ((PadStatus->button & PAD_BUTTON_Y) != 0); + m_pad_state.Z = ((PadStatus->button & PAD_TRIGGER_Z) != 0); + m_pad_state.Start = ((PadStatus->button & PAD_BUTTON_START) != 0); - s_padState.DPadUp = ((PadStatus->button & PAD_BUTTON_UP) != 0); - s_padState.DPadDown = ((PadStatus->button & PAD_BUTTON_DOWN) != 0); - s_padState.DPadLeft = ((PadStatus->button & PAD_BUTTON_LEFT) != 0); - s_padState.DPadRight = ((PadStatus->button & PAD_BUTTON_RIGHT) != 0); + m_pad_state.DPadUp = ((PadStatus->button & PAD_BUTTON_UP) != 0); + m_pad_state.DPadDown = ((PadStatus->button & PAD_BUTTON_DOWN) != 0); + m_pad_state.DPadLeft = ((PadStatus->button & PAD_BUTTON_LEFT) != 0); + m_pad_state.DPadRight = ((PadStatus->button & PAD_BUTTON_RIGHT) != 0); - s_padState.L = ((PadStatus->button & PAD_TRIGGER_L) != 0); - s_padState.R = ((PadStatus->button & PAD_TRIGGER_R) != 0); - s_padState.TriggerL = PadStatus->triggerLeft; - s_padState.TriggerR = PadStatus->triggerRight; + m_pad_state.L = ((PadStatus->button & PAD_TRIGGER_L) != 0); + m_pad_state.R = ((PadStatus->button & PAD_TRIGGER_R) != 0); + m_pad_state.TriggerL = PadStatus->triggerLeft; + m_pad_state.TriggerR = PadStatus->triggerRight; - s_padState.AnalogStickX = PadStatus->stickX; - s_padState.AnalogStickY = PadStatus->stickY; + m_pad_state.AnalogStickX = PadStatus->stickX; + m_pad_state.AnalogStickY = PadStatus->stickY; - s_padState.CStickX = PadStatus->substickX; - s_padState.CStickY = PadStatus->substickY; + m_pad_state.CStickX = PadStatus->substickX; + m_pad_state.CStickY = PadStatus->substickY; - s_padState.is_connected = PadStatus->isConnected; + m_pad_state.is_connected = PadStatus->isConnected; - s_padState.get_origin = (PadStatus->button & PAD_GET_ORIGIN) != 0; + m_pad_state.get_origin = (PadStatus->button & PAD_GET_ORIGIN) != 0; - s_padState.disc = s_bDiscChange; - s_bDiscChange = false; - s_padState.reset = s_bReset; - s_bReset = false; + m_pad_state.disc = m_has_disc_change; + m_has_disc_change = false; + m_pad_state.reset = m_reset; + m_reset = false; - SetInputDisplayString(s_padState, controllerID); + { + std::string display_str = GenerateInputDisplayString(m_pad_state, controllerID); + + std::lock_guard guard(m_input_display_lock); + m_input_display[controllerID] = std::move(display_str); + } } // NOTE: CPU Thread -void RecordInput(const GCPadStatus* PadStatus, int controllerID) +void MovieManager::RecordInput(const GCPadStatus* PadStatus, int controllerID) { if (!IsRecordingInput() || !IsUsingPad(controllerID)) return; CheckPadStatus(PadStatus, controllerID); - s_temp_input.resize(s_currentByte + sizeof(ControllerState)); - memcpy(&s_temp_input[s_currentByte], &s_padState, sizeof(ControllerState)); - s_currentByte += sizeof(ControllerState); + m_temp_input.resize(m_current_byte + sizeof(ControllerState)); + memcpy(&m_temp_input[m_current_byte], &m_pad_state, sizeof(ControllerState)); + m_current_byte += sizeof(ControllerState); } // NOTE: CPU Thread -void CheckWiimoteStatus(int wiimote, const DataReportBuilder& rpt, ExtensionNumber ext, - const EncryptionKey& key) +void MovieManager::CheckWiimoteStatus(int wiimote, const DataReportBuilder& rpt, + ExtensionNumber ext, const EncryptionKey& key) { - SetWiiInputDisplayString(wiimote, rpt, ext, key); + { + std::string display_str = GenerateWiiInputDisplayString(wiimote, rpt, ext, key); + + std::lock_guard guard(m_input_display_lock); + m_input_display[wiimote + 4] = std::move(display_str); + } if (IsRecordingInput()) RecordWiimote(wiimote, rpt.GetDataPtr(), rpt.GetDataSize()); } -void RecordWiimote(int wiimote, const u8* data, u8 size) +void MovieManager::RecordWiimote(int wiimote, const u8* data, u8 size) { if (!IsRecordingInput() || !IsUsingWiimote(wiimote)) return; InputUpdate(); - s_temp_input.resize(s_currentByte + size + 1); - s_temp_input[s_currentByte++] = size; - memcpy(&s_temp_input[s_currentByte], data, size); - s_currentByte += size; + m_temp_input.resize(m_current_byte + size + 1); + m_temp_input[m_current_byte++] = size; + memcpy(&m_temp_input[m_current_byte], data, size); + m_current_byte += size; } // NOTE: EmuThread / Host Thread -void ReadHeader() +void MovieManager::ReadHeader() { for (int i = 0; i < 4; ++i) { - if (tmpHeader.GBAControllers & (1 << i)) - s_controllers[i] = ControllerType::GBA; - else if (tmpHeader.controllers & (1 << i)) - s_controllers[i] = ControllerType::GC; + if (m_temp_header.GBAControllers & (1 << i)) + m_controllers[i] = ControllerType::GBA; + else if (m_temp_header.controllers & (1 << i)) + m_controllers[i] = ControllerType::GC; else - s_controllers[i] = ControllerType::None; - s_wiimotes[i] = (tmpHeader.controllers & (1 << (i + 4))) != 0; + m_controllers[i] = ControllerType::None; + m_wiimotes[i] = (m_temp_header.controllers & (1 << (i + 4))) != 0; } - s_recordingStartTime = tmpHeader.recordingStartTime; - if (s_rerecords < tmpHeader.numRerecords) - s_rerecords = tmpHeader.numRerecords; + m_recording_start_time = m_temp_header.recordingStartTime; + if (m_rerecords < m_temp_header.numRerecords) + m_rerecords = m_temp_header.numRerecords; - if (tmpHeader.bSaveConfig) + if (m_temp_header.bSaveConfig) { - s_bSaveConfig = true; - Config::AddLayer(ConfigLoaders::GenerateMovieConfigLoader(&tmpHeader)); - s_bClearSave = tmpHeader.bClearSave; - s_memcards = tmpHeader.memcards; - s_bongos = tmpHeader.bongos; - s_bNetPlay = tmpHeader.bNetPlay; - s_revision = tmpHeader.revision; + m_save_config = true; + Config::AddLayer(ConfigLoaders::GenerateMovieConfigLoader(&m_temp_header)); + m_clear_save = m_temp_header.bClearSave; + m_memcards = m_temp_header.memcards; + m_bongos = m_temp_header.bongos; + m_net_play = m_temp_header.bNetPlay; + m_revision = m_temp_header.revision; } else { GetSettings(); } - s_discChange = {tmpHeader.discChange.begin(), tmpHeader.discChange.end()}; - s_author = {tmpHeader.author.begin(), tmpHeader.author.end()}; - s_MD5 = tmpHeader.md5; - s_DSPiromHash = tmpHeader.DSPiromHash; - s_DSPcoefHash = tmpHeader.DSPcoefHash; + m_disc_change_filename = {m_temp_header.discChange.begin(), m_temp_header.discChange.end()}; + m_author = {m_temp_header.author.begin(), m_temp_header.author.end()}; + m_md5 = m_temp_header.md5; + m_dsp_irom_hash = m_temp_header.DSPiromHash; + m_dsp_coef_hash = m_temp_header.DSPcoefHash; } // NOTE: Host Thread -bool PlayInput(const std::string& movie_path, std::optional* savestate_path) +bool MovieManager::PlayInput(const std::string& movie_path, + std::optional* savestate_path) { - if (s_playMode != PlayMode::None) + if (m_play_mode != PlayMode::None) return false; File::IOFile recording_file(movie_path, "rb"); - if (!recording_file.ReadArray(&tmpHeader, 1)) + if (!recording_file.ReadArray(&m_temp_header, 1)) return false; - if (!IsMovieHeader(tmpHeader.filetype)) + if (!IsMovieHeader(m_temp_header.filetype)) { PanicAlertFmtT("Invalid recording file"); return false; @@ -981,28 +946,28 @@ bool PlayInput(const std::string& movie_path, std::optional* savest return false; #endif // USE_RETRO_ACHIEVEMENTS - s_totalFrames = tmpHeader.frameCount; - s_totalLagCount = tmpHeader.lagCount; - s_totalInputCount = tmpHeader.inputCount; - s_totalTickCount = tmpHeader.tickCount; - s_currentFrame = 0; - s_currentLagCount = 0; - s_currentInputCount = 0; + m_total_frames = m_temp_header.frameCount; + m_total_lag_count = m_temp_header.lagCount; + m_total_input_count = m_temp_header.inputCount; + m_total_tick_count = m_temp_header.tickCount; + m_current_frame = 0; + m_current_lag_count = 0; + m_current_input_count = 0; - s_playMode = PlayMode::Playing; + m_play_mode = PlayMode::Playing; // Wiimotes cause desync issues if they're not reset before launching the game Wiimote::ResetAllWiimotes(); Core::UpdateWantDeterminism(); - s_temp_input.resize(recording_file.GetSize() - 256); - recording_file.ReadBytes(s_temp_input.data(), s_temp_input.size()); - s_currentByte = 0; + m_temp_input.resize(recording_file.GetSize() - 256); + recording_file.ReadBytes(m_temp_input.data(), m_temp_input.size()); + m_current_byte = 0; recording_file.Close(); // Load savestate (and skip to frame data) - if (tmpHeader.bFromSaveState && savestate_path) + if (m_temp_header.bFromSaveState && savestate_path) { const std::string savestate_path_temp = movie_path + ".sav"; if (File::Exists(savestate_path_temp)) @@ -1016,34 +981,34 @@ bool PlayInput(const std::string& movie_path, std::optional* savest movie_path, savestate_path_temp); } - s_bRecordingFromSaveState = true; + m_recording_from_save_state = true; #ifdef USE_RETRO_ACHIEVEMENTS // On the chance someone tries to re-enable before the TAS can start Config::SetBase(Config::RA_HARDCORE_ENABLED, false); #endif // USE_RETRO_ACHIEVEMENTS - Movie::LoadInput(movie_path); + LoadInput(movie_path); } return true; } -void DoState(PointerWrap& p) +void MovieManager::DoState(PointerWrap& p) { // many of these could be useful to save even when no movie is active, // and the data is tiny, so let's just save it regardless of movie state. - p.Do(s_currentFrame); - p.Do(s_currentByte); - p.Do(s_currentLagCount); - p.Do(s_currentInputCount); - p.Do(s_bPolled); - p.Do(s_tickCountAtLastInput); - // other variables (such as s_totalBytes and s_totalFrames) are set in LoadInput + p.Do(m_current_frame); + p.Do(m_current_byte); + p.Do(m_current_lag_count); + p.Do(m_current_input_count); + p.Do(m_polled); + p.Do(m_tick_count_at_last_input); + // other variables (such as s_totalBytes and m_total_frames) are set in LoadInput } // NOTE: Host Thread -void LoadInput(const std::string& movie_path) +void MovieManager::LoadInput(const std::string& movie_path) { File::IOFile t_record; if (!t_record.Open(movie_path, "r+b")) @@ -1053,21 +1018,21 @@ void LoadInput(const std::string& movie_path) return; } - t_record.ReadArray(&tmpHeader, 1); + t_record.ReadArray(&m_temp_header, 1); - if (!IsMovieHeader(tmpHeader.filetype)) + if (!IsMovieHeader(m_temp_header.filetype)) { PanicAlertFmtT("Savestate movie {0} is corrupted, movie recording stopping...", movie_path); EndPlayInput(false); return; } ReadHeader(); - if (!s_bReadOnly) + if (!m_read_only) { - s_rerecords++; - tmpHeader.numRerecords = s_rerecords; + m_rerecords++; + m_temp_header.numRerecords = m_rerecords; t_record.Seek(0, File::SeekOrigin::Begin); - t_record.WriteArray(&tmpHeader, 1); + t_record.WriteArray(&m_temp_header, 1); } ChangePads(); @@ -1078,46 +1043,47 @@ void LoadInput(const std::string& movie_path) bool afterEnd = false; // This can only happen if the user manually deletes data from the dtm. - if (s_currentByte > totalSavedBytes) + if (m_current_byte > totalSavedBytes) { PanicAlertFmtT( "Warning: You loaded a save whose movie ends before the current frame in the save " "(byte {0} < {1}) (frame {2} < {3}). You should load another save before continuing.", - totalSavedBytes + 256, s_currentByte + 256, tmpHeader.frameCount, s_currentFrame); + totalSavedBytes + 256, m_current_byte + 256, m_temp_header.frameCount, m_current_frame); afterEnd = true; } - if (!s_bReadOnly || s_temp_input.empty()) + if (!m_read_only || m_temp_input.empty()) { - s_totalFrames = tmpHeader.frameCount; - s_totalLagCount = tmpHeader.lagCount; - s_totalInputCount = tmpHeader.inputCount; - s_totalTickCount = s_tickCountAtLastInput = tmpHeader.tickCount; + m_total_frames = m_temp_header.frameCount; + m_total_lag_count = m_temp_header.lagCount; + m_total_input_count = m_temp_header.inputCount; + m_total_tick_count = m_tick_count_at_last_input = m_temp_header.tickCount; - s_temp_input.resize(static_cast(totalSavedBytes)); - t_record.ReadBytes(s_temp_input.data(), s_temp_input.size()); + m_temp_input.resize(static_cast(totalSavedBytes)); + t_record.ReadBytes(m_temp_input.data(), m_temp_input.size()); } - else if (s_currentByte > 0) + else if (m_current_byte > 0) { - if (s_currentByte > totalSavedBytes) + if (m_current_byte > totalSavedBytes) { } - else if (s_currentByte > s_temp_input.size()) + else if (m_current_byte > m_temp_input.size()) { afterEnd = true; PanicAlertFmtT( "Warning: You loaded a save that's after the end of the current movie. (byte {0} " "> {1}) (input {2} > {3}). You should load another save before continuing, or load " "this state with read-only mode off.", - s_currentByte + 256, s_temp_input.size() + 256, s_currentInputCount, s_totalInputCount); + m_current_byte + 256, m_temp_input.size() + 256, m_current_input_count, + m_total_input_count); } - else if (s_currentByte > 0 && !s_temp_input.empty()) + else if (m_current_byte > 0 && !m_temp_input.empty()) { // verify identical from movie start to the save's current frame - std::vector movInput(s_currentByte); + std::vector movInput(m_current_byte); t_record.ReadArray(movInput.data(), movInput.size()); - const auto result = std::mismatch(movInput.begin(), movInput.end(), s_temp_input.begin()); + const auto result = std::mismatch(movInput.begin(), movInput.end(), m_temp_input.begin()); if (result.first != movInput.end()) { @@ -1136,13 +1102,13 @@ void LoadInput(const std::string& movie_path) "read-only mode off. Otherwise you'll probably get a desync.", byte_offset, byte_offset); - std::copy(movInput.begin(), movInput.end(), s_temp_input.begin()); + std::copy(movInput.begin(), movInput.end(), m_temp_input.begin()); } else { const ptrdiff_t frame = mismatch_index / sizeof(ControllerState); ControllerState curPadState; - memcpy(&curPadState, &s_temp_input[frame * sizeof(ControllerState)], + memcpy(&curPadState, &m_temp_input[frame * sizeof(ControllerState)], sizeof(ControllerState)); ControllerState movPadState; memcpy(&movPadState, &movInput[frame * sizeof(ControllerState)], sizeof(ControllerState)); @@ -1161,41 +1127,41 @@ void LoadInput(const std::string& movie_path) "Start={24}, A={25}, B={26}, X={27}, Y={28}, Z={29}, DUp={30}, DDown={31}, " "DLeft={32}, DRight={33}, L={34}, R={35}, LT={36}, RT={37}, AnalogX={38}, " "AnalogY={39}, CX={40}, CY={41}, Connected={42}", - frame, s_totalFrames, tmpHeader.frameCount, frame, curPadState.Start, curPadState.A, - curPadState.B, curPadState.X, curPadState.Y, curPadState.Z, curPadState.DPadUp, - curPadState.DPadDown, curPadState.DPadLeft, curPadState.DPadRight, curPadState.L, - curPadState.R, curPadState.TriggerL, curPadState.TriggerR, curPadState.AnalogStickX, - curPadState.AnalogStickY, curPadState.CStickX, curPadState.CStickY, - curPadState.is_connected, frame, movPadState.Start, movPadState.A, movPadState.B, - movPadState.X, movPadState.Y, movPadState.Z, movPadState.DPadUp, movPadState.DPadDown, - movPadState.DPadLeft, movPadState.DPadRight, movPadState.L, movPadState.R, - movPadState.TriggerL, movPadState.TriggerR, movPadState.AnalogStickX, - movPadState.AnalogStickY, movPadState.CStickX, movPadState.CStickY, - curPadState.is_connected); + frame, m_total_frames, m_temp_header.frameCount, frame, curPadState.Start, + curPadState.A, curPadState.B, curPadState.X, curPadState.Y, curPadState.Z, + curPadState.DPadUp, curPadState.DPadDown, curPadState.DPadLeft, curPadState.DPadRight, + curPadState.L, curPadState.R, curPadState.TriggerL, curPadState.TriggerR, + curPadState.AnalogStickX, curPadState.AnalogStickY, curPadState.CStickX, + curPadState.CStickY, curPadState.is_connected, frame, movPadState.Start, + movPadState.A, movPadState.B, movPadState.X, movPadState.Y, movPadState.Z, + movPadState.DPadUp, movPadState.DPadDown, movPadState.DPadLeft, movPadState.DPadRight, + movPadState.L, movPadState.R, movPadState.TriggerL, movPadState.TriggerR, + movPadState.AnalogStickX, movPadState.AnalogStickY, movPadState.CStickX, + movPadState.CStickY, curPadState.is_connected); } } } } t_record.Close(); - s_bSaveConfig = tmpHeader.bSaveConfig; + m_save_config = m_temp_header.bSaveConfig; if (!afterEnd) { - if (s_bReadOnly) + if (m_read_only) { - if (s_playMode != PlayMode::Playing) + if (m_play_mode != PlayMode::Playing) { - s_playMode = PlayMode::Playing; + m_play_mode = PlayMode::Playing; Core::UpdateWantDeterminism(); Core::DisplayMessage("Switched to playback", 2000); } } else { - if (s_playMode != PlayMode::Recording) + if (m_play_mode != PlayMode::Recording) { - s_playMode = PlayMode::Recording; + m_play_mode = PlayMode::Recording; Core::UpdateWantDeterminism(); Core::DisplayMessage("Switched to recording", 2000); } @@ -1208,169 +1174,174 @@ void LoadInput(const std::string& movie_path) } // NOTE: CPU Thread -static void CheckInputEnd() +void MovieManager::CheckInputEnd() { - if (s_currentByte >= s_temp_input.size() || - (Core::System::GetInstance().GetCoreTiming().GetTicks() > s_totalTickCount && + if (m_current_byte >= m_temp_input.size() || + (Core::System::GetInstance().GetCoreTiming().GetTicks() > m_total_tick_count && !IsRecordingInputFromSaveState())) { - EndPlayInput(!s_bReadOnly); + EndPlayInput(!m_read_only); } } // NOTE: CPU Thread -void PlayController(GCPadStatus* PadStatus, int controllerID) +void MovieManager::PlayController(GCPadStatus* PadStatus, int controllerID) { // Correct playback is entirely dependent on the emulator polling the controllers // in the same order done during recording - if (!IsPlayingInput() || !IsUsingPad(controllerID) || s_temp_input.empty()) + if (!IsPlayingInput() || !IsUsingPad(controllerID) || m_temp_input.empty()) return; - if (s_currentByte + sizeof(ControllerState) > s_temp_input.size()) + if (m_current_byte + sizeof(ControllerState) > m_temp_input.size()) { - PanicAlertFmtT("Premature movie end in PlayController. {0} + {1} > {2}", s_currentByte, - sizeof(ControllerState), s_temp_input.size()); - EndPlayInput(!s_bReadOnly); + PanicAlertFmtT("Premature movie end in PlayController. {0} + {1} > {2}", m_current_byte, + sizeof(ControllerState), m_temp_input.size()); + EndPlayInput(!m_read_only); return; } - memcpy(&s_padState, &s_temp_input[s_currentByte], sizeof(ControllerState)); - s_currentByte += sizeof(ControllerState); + memcpy(&m_pad_state, &m_temp_input[m_current_byte], sizeof(ControllerState)); + m_current_byte += sizeof(ControllerState); - PadStatus->isConnected = s_padState.is_connected; + PadStatus->isConnected = m_pad_state.is_connected; - PadStatus->triggerLeft = s_padState.TriggerL; - PadStatus->triggerRight = s_padState.TriggerR; + PadStatus->triggerLeft = m_pad_state.TriggerL; + PadStatus->triggerRight = m_pad_state.TriggerR; - PadStatus->stickX = s_padState.AnalogStickX; - PadStatus->stickY = s_padState.AnalogStickY; + PadStatus->stickX = m_pad_state.AnalogStickX; + PadStatus->stickY = m_pad_state.AnalogStickY; - PadStatus->substickX = s_padState.CStickX; - PadStatus->substickY = s_padState.CStickY; + PadStatus->substickX = m_pad_state.CStickX; + PadStatus->substickY = m_pad_state.CStickY; PadStatus->button = 0; PadStatus->button |= PAD_USE_ORIGIN; - if (s_padState.A) + if (m_pad_state.A) { PadStatus->button |= PAD_BUTTON_A; PadStatus->analogA = 0xFF; } - if (s_padState.B) + if (m_pad_state.B) { PadStatus->button |= PAD_BUTTON_B; PadStatus->analogB = 0xFF; } - if (s_padState.X) + if (m_pad_state.X) PadStatus->button |= PAD_BUTTON_X; - if (s_padState.Y) + if (m_pad_state.Y) PadStatus->button |= PAD_BUTTON_Y; - if (s_padState.Z) + if (m_pad_state.Z) PadStatus->button |= PAD_TRIGGER_Z; - if (s_padState.Start) + if (m_pad_state.Start) PadStatus->button |= PAD_BUTTON_START; - if (s_padState.DPadUp) + if (m_pad_state.DPadUp) PadStatus->button |= PAD_BUTTON_UP; - if (s_padState.DPadDown) + if (m_pad_state.DPadDown) PadStatus->button |= PAD_BUTTON_DOWN; - if (s_padState.DPadLeft) + if (m_pad_state.DPadLeft) PadStatus->button |= PAD_BUTTON_LEFT; - if (s_padState.DPadRight) + if (m_pad_state.DPadRight) PadStatus->button |= PAD_BUTTON_RIGHT; - if (s_padState.L) + if (m_pad_state.L) PadStatus->button |= PAD_TRIGGER_L; - if (s_padState.R) + if (m_pad_state.R) PadStatus->button |= PAD_TRIGGER_R; - if (s_padState.get_origin) + if (m_pad_state.get_origin) PadStatus->button |= PAD_GET_ORIGIN; - if (s_padState.disc) + if (m_pad_state.disc) { - Core::RunAsCPUThread([] { - auto& system = Core::System::GetInstance(); - if (!system.GetDVDInterface().AutoChangeDisc()) + Core::RunAsCPUThread([this] { + if (!m_system.GetDVDInterface().AutoChangeDisc()) { - system.GetCPU().Break(); - PanicAlertFmtT("Change the disc to {0}", s_discChange); + m_system.GetCPU().Break(); + PanicAlertFmtT("Change the disc to {0}", m_disc_change_filename); } }); } - if (s_padState.reset) + if (m_pad_state.reset) { auto& system = Core::System::GetInstance(); system.GetProcessorInterface().ResetButton_Tap(); } - SetInputDisplayString(s_padState, controllerID); + { + std::string display_str = GenerateInputDisplayString(m_pad_state, controllerID); + + std::lock_guard guard(m_input_display_lock); + m_input_display[controllerID] = std::move(display_str); + } + CheckInputEnd(); } // NOTE: CPU Thread -bool PlayWiimote(int wiimote, WiimoteCommon::DataReportBuilder& rpt, ExtensionNumber ext, - const EncryptionKey& key) +bool MovieManager::PlayWiimote(int wiimote, WiimoteCommon::DataReportBuilder& rpt, + ExtensionNumber ext, const EncryptionKey& key) { - if (!IsPlayingInput() || !IsUsingWiimote(wiimote) || s_temp_input.empty()) + if (!IsPlayingInput() || !IsUsingWiimote(wiimote) || m_temp_input.empty()) return false; - if (s_currentByte > s_temp_input.size()) + if (m_current_byte > m_temp_input.size()) { - PanicAlertFmtT("Premature movie end in PlayWiimote. {0} > {1}", s_currentByte, - s_temp_input.size()); - EndPlayInput(!s_bReadOnly); + PanicAlertFmtT("Premature movie end in PlayWiimote. {0} > {1}", m_current_byte, + m_temp_input.size()); + EndPlayInput(!m_read_only); return false; } const u8 size = rpt.GetDataSize(); - const u8 sizeInMovie = s_temp_input[s_currentByte]; + const u8 sizeInMovie = m_temp_input[m_current_byte]; if (size != sizeInMovie) { PanicAlertFmtT( "Fatal desync. Aborting playback. (Error in PlayWiimote: {0} != {1}, byte {2}.){3}", - sizeInMovie, size, s_currentByte, - (s_controllers == ControllerTypeArray{}) ? + sizeInMovie, size, m_current_byte, + (m_controllers == ControllerTypeArray{}) ? " Try re-creating the recording with all GameCube controllers " "disabled (in Configure > GameCube > Device Settings)." : ""); - EndPlayInput(!s_bReadOnly); + EndPlayInput(!m_read_only); return false; } - s_currentByte++; + m_current_byte++; - if (s_currentByte + size > s_temp_input.size()) + if (m_current_byte + size > m_temp_input.size()) { - PanicAlertFmtT("Premature movie end in PlayWiimote. {0} + {1} > {2}", s_currentByte, size, - s_temp_input.size()); - EndPlayInput(!s_bReadOnly); + PanicAlertFmtT("Premature movie end in PlayWiimote. {0} + {1} > {2}", m_current_byte, size, + m_temp_input.size()); + EndPlayInput(!m_read_only); return false; } - memcpy(rpt.GetDataPtr(), &s_temp_input[s_currentByte], size); - s_currentByte += size; + memcpy(rpt.GetDataPtr(), &m_temp_input[m_current_byte], size); + m_current_byte += size; - s_currentInputCount++; + m_current_input_count++; CheckInputEnd(); return true; } // NOTE: Host / EmuThread / CPU Thread -void EndPlayInput(bool cont) +void MovieManager::EndPlayInput(bool cont) { if (cont) { - // If !IsMovieActive(), changing s_playMode requires calling UpdateWantDeterminism + // If !IsMovieActive(), changing m_play_mode requires calling UpdateWantDeterminism ASSERT(IsMovieActive()); - s_playMode = PlayMode::Recording; + m_play_mode = PlayMode::Recording; Core::DisplayMessage("Reached movie end. Resuming recording.", 2000); } - else if (s_playMode != PlayMode::None) + else if (m_play_mode != PlayMode::None) { // We can be called by EmuThread during boot (CPU::State::PowerDown) auto& system = Core::System::GetInstance(); @@ -1378,15 +1349,15 @@ void EndPlayInput(bool cont) bool was_running = Core::IsRunningAndStarted() && !cpu.IsStepping(); if (was_running && Config::Get(Config::MAIN_MOVIE_PAUSE_MOVIE)) cpu.Break(); - s_rerecords = 0; - s_currentByte = 0; - s_playMode = PlayMode::None; + m_rerecords = 0; + m_current_byte = 0; + m_play_mode = PlayMode::None; Core::DisplayMessage("Movie End.", 2000); - s_bRecordingFromSaveState = false; + m_recording_from_save_state = false; Config::RemoveLayer(Config::LayerType::Movie); // we don't clear these things because otherwise we can't resume playback if we load a movie // state later - // s_totalFrames = s_totalBytes = 0; + // m_total_frames = s_totalBytes = 0; // delete tmpInput; // tmpInput = nullptr; @@ -1395,7 +1366,7 @@ void EndPlayInput(bool cont) } // NOTE: Save State + Host Thread -void SaveRecording(const std::string& filename) +void MovieManager::SaveRecording(const std::string& filename) { File::IOFile save_record(filename, "wb"); // Create the real header now and write it @@ -1420,26 +1391,26 @@ void SaveRecording(const std::string& filename) header.controllers |= 1 << (i + 4); } - header.bFromSaveState = s_bRecordingFromSaveState; - header.frameCount = s_totalFrames; - header.lagCount = s_totalLagCount; - header.inputCount = s_totalInputCount; - header.numRerecords = s_rerecords; - header.recordingStartTime = s_recordingStartTime; + header.bFromSaveState = m_recording_from_save_state; + header.frameCount = m_total_frames; + header.lagCount = m_total_lag_count; + header.inputCount = m_total_input_count; + header.numRerecords = m_rerecords; + header.recordingStartTime = m_recording_start_time; header.bSaveConfig = true; ConfigLoaders::SaveToDTM(&header); - header.memcards = s_memcards; - header.bClearSave = s_bClearSave; - header.bNetPlay = s_bNetPlay; - strncpy(header.discChange.data(), s_discChange.c_str(), header.discChange.size()); - strncpy(header.author.data(), s_author.c_str(), header.author.size()); - header.md5 = s_MD5; - header.bongos = s_bongos; - header.revision = s_revision; - header.DSPiromHash = s_DSPiromHash; - header.DSPcoefHash = s_DSPcoefHash; - header.tickCount = s_totalTickCount; + header.memcards = m_memcards; + header.bClearSave = m_clear_save; + header.bNetPlay = m_net_play; + strncpy(header.discChange.data(), m_disc_change_filename.c_str(), header.discChange.size()); + strncpy(header.author.data(), m_author.c_str(), header.author.size()); + header.md5 = m_md5; + header.bongos = m_bongos; + header.revision = m_revision; + header.DSPiromHash = m_dsp_irom_hash; + header.DSPcoefHash = m_dsp_coef_hash; + header.tickCount = m_total_tick_count; // TODO header.uniqueID = 0; @@ -1447,9 +1418,9 @@ void SaveRecording(const std::string& filename) save_record.WriteArray(&header, 1); - bool success = save_record.WriteBytes(s_temp_input.data(), s_temp_input.size()); + bool success = save_record.WriteBytes(m_temp_input.data(), m_temp_input.size()); - if (success && s_bRecordingFromSaveState) + if (success && m_recording_from_save_state) { std::string stateFilename = filename + ".sav"; success = File::CopyRegularFile(File::GetUserPath(D_STATESAVES_IDX) + "dtm.sav", stateFilename); @@ -1462,17 +1433,17 @@ void SaveRecording(const std::string& filename) } // NOTE: GPU Thread -void SetGraphicsConfig() +void MovieManager::SetGraphicsConfig() { - g_Config.bEFBAccessEnable = tmpHeader.bEFBAccessEnable; - g_Config.bSkipEFBCopyToRam = tmpHeader.bSkipEFBCopyToRam; - g_Config.bEFBEmulateFormatChanges = tmpHeader.bEFBEmulateFormatChanges; - g_Config.bImmediateXFB = tmpHeader.bImmediateXFB; - g_Config.bSkipXFBCopyToRam = tmpHeader.bSkipXFBCopyToRam; + g_Config.bEFBAccessEnable = m_temp_header.bEFBAccessEnable; + g_Config.bSkipEFBCopyToRam = m_temp_header.bSkipEFBCopyToRam; + g_Config.bEFBEmulateFormatChanges = m_temp_header.bEFBEmulateFormatChanges; + g_Config.bImmediateXFB = m_temp_header.bImmediateXFB; + g_Config.bSkipXFBCopyToRam = m_temp_header.bSkipXFBCopyToRam; } // NOTE: EmuThread / Host Thread -void GetSettings() +void MovieManager::GetSettings() { using ExpansionInterface::EXIDeviceType; const EXIDeviceType slot_a_type = Config::Get(Config::MAIN_SLOT_A); @@ -1482,12 +1453,12 @@ void GetSettings() const bool slot_b_has_raw_memcard = slot_b_type == EXIDeviceType::MemoryCard; const bool slot_b_has_gci_folder = slot_b_type == EXIDeviceType::MemoryCardFolder; - s_bSaveConfig = true; - s_bNetPlay = NetPlay::IsNetPlayRunning(); + m_save_config = true; + m_net_play = NetPlay::IsNetPlayRunning(); if (SConfig::GetInstance().bWii) { u64 title_id = SConfig::GetInstance().GetTitleID(); - s_bClearSave = !File::Exists( + m_clear_save = !File::Exists( Common::GetTitleDataPath(title_id, Common::FromWhichRoot::Session) + "/banner.bin"); } else @@ -1495,22 +1466,22 @@ void GetSettings() const auto raw_memcard_exists = [](ExpansionInterface::Slot card_slot) { return File::Exists(Config::GetMemcardPath(card_slot, SConfig::GetInstance().m_region)); }; - const auto gci_folder_has_saves = [](ExpansionInterface::Slot card_slot) { + const auto gci_folder_has_saves = [this](ExpansionInterface::Slot card_slot) { const auto [path, migrate] = ExpansionInterface::CEXIMemoryCard::GetGCIFolderPath( - card_slot, ExpansionInterface::AllowMovieFolder::No); + card_slot, ExpansionInterface::AllowMovieFolder::No, *this); const u64 number_of_saves = File::ScanDirectoryTree(path, false).size; return number_of_saves > 0; }; - s_bClearSave = !(slot_a_has_raw_memcard && raw_memcard_exists(ExpansionInterface::Slot::A)) && + m_clear_save = !(slot_a_has_raw_memcard && raw_memcard_exists(ExpansionInterface::Slot::A)) && !(slot_b_has_raw_memcard && raw_memcard_exists(ExpansionInterface::Slot::B)) && !(slot_a_has_gci_folder && gci_folder_has_saves(ExpansionInterface::Slot::A)) && !(slot_b_has_gci_folder && gci_folder_has_saves(ExpansionInterface::Slot::B)); } - s_memcards |= (slot_a_has_raw_memcard || slot_a_has_gci_folder) << 0; - s_memcards |= (slot_b_has_raw_memcard || slot_b_has_gci_folder) << 1; + m_memcards |= (slot_a_has_raw_memcard || slot_a_has_gci_folder) << 0; + m_memcards |= (slot_b_has_raw_memcard || slot_b_has_gci_folder) << 1; - s_revision = ConvertGitRevisionToBytes(Common::GetScmRevGitStr()); + m_revision = ConvertGitRevisionToBytes(Common::GetScmRevGitStr()); if (!Config::Get(Config::MAIN_DSP_HLE)) { @@ -1536,29 +1507,27 @@ void GetSettings() file_coef.Close(); for (u16& entry : coef) entry = Common::swap16(entry); - s_DSPiromHash = + m_dsp_irom_hash = Common::HashAdler32(reinterpret_cast(irom.data()), DSP::DSP_IROM_BYTE_SIZE); - s_DSPcoefHash = + m_dsp_coef_hash = Common::HashAdler32(reinterpret_cast(coef.data()), DSP::DSP_COEF_BYTE_SIZE); } else { - s_DSPiromHash = 0; - s_DSPcoefHash = 0; + m_dsp_irom_hash = 0; + m_dsp_coef_hash = 0; } } -static const mbedtls_md_info_t* s_md5_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5); - // NOTE: Entrypoint for own thread -static void CheckMD5() +void MovieManager::CheckMD5() { - if (s_current_file_name.empty()) + if (m_current_file_name.empty()) return; for (int i = 0, n = 0; i < 16; ++i) { - if (tmpHeader.md5[i] != 0) + if (m_temp_header.md5[i] != 0) continue; n++; if (n == 16) @@ -1567,29 +1536,31 @@ static void CheckMD5() Core::DisplayMessage("Verifying checksum...", 2000); std::array game_md5; - mbedtls_md_file(s_md5_info, s_current_file_name.c_str(), game_md5.data()); + mbedtls_md_file(mbedtls_md_info_from_type(MBEDTLS_MD_MD5), m_current_file_name.c_str(), + game_md5.data()); - if (game_md5 == s_MD5) + if (game_md5 == m_md5) Core::DisplayMessage("Checksum of current game matches the recorded game.", 2000); else Core::DisplayMessage("Checksum of current game does not match the recorded game!", 3000); } // NOTE: Entrypoint for own thread -static void GetMD5() +void MovieManager::GetMD5() { - if (s_current_file_name.empty()) + if (m_current_file_name.empty()) return; Core::DisplayMessage("Calculating checksum of game file...", 2000); - mbedtls_md_file(s_md5_info, s_current_file_name.c_str(), s_MD5.data()); + mbedtls_md_file(mbedtls_md_info_from_type(MBEDTLS_MD_MD5), m_current_file_name.c_str(), + m_md5.data()); Core::DisplayMessage("Finished calculating checksum.", 2000); } // NOTE: EmuThread -void Shutdown() +void MovieManager::Shutdown() { - s_currentInputCount = s_totalInputCount = s_totalFrames = s_tickCountAtLastInput = 0; - s_temp_input.clear(); + m_current_input_count = m_total_input_count = m_total_frames = m_tick_count_at_last_input = 0; + m_temp_input.clear(); } } // namespace Movie diff --git a/Source/Core/Core/Movie.h b/Source/Core/Core/Movie.h index e0264d42ab..74b541cc2f 100644 --- a/Source/Core/Core/Movie.h +++ b/Source/Core/Core/Movie.h @@ -5,9 +5,11 @@ #include #include +#include #include #include #include +#include #include "Common/CommonTypes.h" @@ -16,6 +18,11 @@ struct BootParameters; struct GCPadStatus; class PointerWrap; +namespace Core +{ +class System; +} + namespace ExpansionInterface { enum class Slot : int; @@ -137,68 +144,137 @@ static_assert(sizeof(DTMHeader) == 256, "DTMHeader should be 256 bytes"); #pragma pack(pop) -void FrameUpdate(); -void InputUpdate(); -void Init(const BootParameters& boot); +enum class PlayMode +{ + None = 0, + Recording, + Playing, +}; -void SetPolledDevice(); +class MovieManager +{ +public: + explicit MovieManager(Core::System& system); + MovieManager(const MovieManager& other) = delete; + MovieManager(MovieManager&& other) = delete; + MovieManager& operator=(const MovieManager& other) = delete; + MovieManager& operator=(MovieManager&& other) = delete; + ~MovieManager(); -bool IsRecordingInput(); -bool IsRecordingInputFromSaveState(); -bool IsJustStartingRecordingInputFromSaveState(); -bool IsJustStartingPlayingInputFromSaveState(); -bool IsPlayingInput(); -bool IsMovieActive(); -bool IsReadOnly(); -u64 GetRecordingStartTime(); + void FrameUpdate(); + void InputUpdate(); + void Init(const BootParameters& boot); -u64 GetCurrentFrame(); -u64 GetTotalFrames(); -u64 GetCurrentInputCount(); -u64 GetTotalInputCount(); -u64 GetCurrentLagCount(); -u64 GetTotalLagCount(); + void SetPolledDevice(); -void SetClearSave(bool enabled); -void SignalDiscChange(const std::string& new_path); -void SetReset(bool reset); + bool IsRecordingInput() const; + bool IsRecordingInputFromSaveState() const; + bool IsJustStartingRecordingInputFromSaveState() const; + bool IsJustStartingPlayingInputFromSaveState() const; + bool IsPlayingInput() const; + bool IsMovieActive() const; + bool IsReadOnly() const; + u64 GetRecordingStartTime() const; -bool IsConfigSaved(); -bool IsStartingFromClearSave(); -bool IsUsingMemcard(ExpansionInterface::Slot slot); -void SetGraphicsConfig(); -bool IsNetPlayRecording(); + u64 GetCurrentFrame() const; + u64 GetTotalFrames() const; + u64 GetCurrentInputCount() const; + u64 GetTotalInputCount() const; + u64 GetCurrentLagCount() const; + u64 GetTotalLagCount() const; -bool IsUsingPad(int controller); -bool IsUsingWiimote(int wiimote); -bool IsUsingBongo(int controller); -bool IsUsingGBA(int controller); -void ChangePads(); -void ChangeWiiPads(bool instantly = false); + void SetClearSave(bool enabled); + void SignalDiscChange(const std::string& new_path); + void SetReset(bool reset); -void SetReadOnly(bool bEnabled); + bool IsConfigSaved() const; + bool IsStartingFromClearSave() const; + bool IsUsingMemcard(ExpansionInterface::Slot slot) const; + void SetGraphicsConfig(); + bool IsNetPlayRecording() const; -bool BeginRecordingInput(const ControllerTypeArray& controllers, - const WiimoteEnabledArray& wiimotes); -void RecordInput(const GCPadStatus* PadStatus, int controllerID); -void RecordWiimote(int wiimote, const u8* data, u8 size); + bool IsUsingPad(int controller) const; + bool IsUsingWiimote(int wiimote) const; + bool IsUsingBongo(int controller) const; + bool IsUsingGBA(int controller) const; + void ChangePads(); + void ChangeWiiPads(bool instantly = false); -bool PlayInput(const std::string& movie_path, std::optional* savestate_path); -void LoadInput(const std::string& movie_path); -void ReadHeader(); -void PlayController(GCPadStatus* PadStatus, int controllerID); -bool PlayWiimote(int wiimote, WiimoteCommon::DataReportBuilder& rpt, - WiimoteEmu::ExtensionNumber ext, const WiimoteEmu::EncryptionKey& key); -void EndPlayInput(bool cont); -void SaveRecording(const std::string& filename); -void DoState(PointerWrap& p); -void Shutdown(); -void CheckPadStatus(const GCPadStatus* PadStatus, int controllerID); -void CheckWiimoteStatus(int wiimote, const WiimoteCommon::DataReportBuilder& rpt, - WiimoteEmu::ExtensionNumber ext, const WiimoteEmu::EncryptionKey& key); + void SetReadOnly(bool bEnabled); -std::string GetInputDisplay(); -std::string GetRTCDisplay(); -std::string GetRerecords(); + bool BeginRecordingInput(const ControllerTypeArray& controllers, + const WiimoteEnabledArray& wiimotes); + void RecordInput(const GCPadStatus* PadStatus, int controllerID); + void RecordWiimote(int wiimote, const u8* data, u8 size); + + bool PlayInput(const std::string& movie_path, std::optional* savestate_path); + void LoadInput(const std::string& movie_path); + void ReadHeader(); + void PlayController(GCPadStatus* PadStatus, int controllerID); + bool PlayWiimote(int wiimote, WiimoteCommon::DataReportBuilder& rpt, + WiimoteEmu::ExtensionNumber ext, const WiimoteEmu::EncryptionKey& key); + void EndPlayInput(bool cont); + void SaveRecording(const std::string& filename); + void DoState(PointerWrap& p); + void Shutdown(); + void CheckPadStatus(const GCPadStatus* PadStatus, int controllerID); + void CheckWiimoteStatus(int wiimote, const WiimoteCommon::DataReportBuilder& rpt, + WiimoteEmu::ExtensionNumber ext, const WiimoteEmu::EncryptionKey& key); + + std::string GetInputDisplay(); + std::string GetRTCDisplay(); + std::string GetRerecords(); + +private: + void GetSettings(); + void CheckInputEnd(); + + void CheckMD5(); + void GetMD5(); + + bool m_read_only = true; + u32 m_rerecords = 0; + PlayMode m_play_mode = PlayMode::None; + + std::array m_controllers{}; + std::array m_wiimotes{}; + ControllerState m_pad_state{}; + DTMHeader m_temp_header{}; + std::vector m_temp_input; + u64 m_current_byte = 0; + u64 m_current_frame = 0; + u64 m_total_frames = 0; // VI + u64 m_current_lag_count = 0; + u64 m_total_lag_count = 0; + u64 m_current_input_count = 0; + u64 m_total_input_count = 0; + u64 m_total_tick_count = 0; + u64 m_tick_count_at_last_input = 0; + u64 m_recording_start_time = 0; // seconds since 1970 that recording started + bool m_save_config = false; + bool m_net_play = false; + bool m_clear_save = false; + bool m_has_disc_change = false; + bool m_reset = false; + std::string m_author; + std::string m_disc_change_filename; + std::array m_md5{}; + u8 m_bongos = 0; + u8 m_memcards = 0; + std::array m_revision{}; + u32 m_dsp_irom_hash = 0; + u32 m_dsp_coef_hash = 0; + + bool m_recording_from_save_state = false; + bool m_polled = false; + + std::string m_current_file_name; + + // m_input_display is used by both CPU and GPU (is mutable). + std::mutex m_input_display_lock; + std::array m_input_display; + + Core::System& m_system; +}; } // namespace Movie diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index 0f6d3a38ba..c40b122a70 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -1763,8 +1763,9 @@ bool NetPlayClient::StartGame(const std::string& path) if (m_dialog->IsRecording()) { - if (Movie::IsReadOnly()) - Movie::SetReadOnly(false); + auto& movie = Core::System::GetInstance().GetMovie(); + if (movie.IsReadOnly()) + movie.SetReadOnly(false); Movie::ControllerTypeArray controllers{}; Movie::WiimoteEnabledArray wiimotes{}; @@ -1778,7 +1779,7 @@ bool NetPlayClient::StartGame(const std::string& path) controllers[i] = Movie::ControllerType::None; wiimotes[i] = m_wiimote_map[i] > 0; } - Movie::BeginRecordingInput(controllers, wiimotes); + movie.BeginRecordingInput(controllers, wiimotes); } for (unsigned int i = 0; i < 4; ++i) @@ -2114,14 +2115,15 @@ bool NetPlayClient::GetNetPads(const int pad_nb, const bool batching, GCPadStatu m_pad_buffer[pad_nb].Pop(*pad_status); - if (Movie::IsRecordingInput()) + auto& movie = Core::System::GetInstance().GetMovie(); + if (movie.IsRecordingInput()) { - Movie::RecordInput(pad_status, pad_nb); - Movie::InputUpdate(); + movie.RecordInput(pad_status, pad_nb); + movie.InputUpdate(); } else { - Movie::CheckPadStatus(pad_status, pad_nb); + movie.CheckPadStatus(pad_status, pad_nb); } return true; diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 4147136588..09334242e8 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -171,7 +171,7 @@ static void DoState(PointerWrap& p) // Movie must be done before the video backend, because the window is redrawn in the video backend // state load, and the frame number must be up-to-date. - Movie::DoState(p); + system.GetMovie().DoState(p); p.DoMarker("Movie"); // Begin with video backend, so that it gets a chance to clear its caches and writeback modified @@ -443,9 +443,10 @@ static void CompressAndDumpState(CompressAndDumpState_args& save_args) } } - if ((Movie::IsMovieActive()) && !Movie::IsJustStartingRecordingInputFromSaveState()) - Movie::SaveRecording(dtmname); - else if (!Movie::IsMovieActive()) + auto& movie = Core::System::GetInstance().GetMovie(); + if ((movie.IsMovieActive()) && !movie.IsJustStartingRecordingInputFromSaveState()) + movie.SaveRecording(dtmname); + else if (!movie.IsMovieActive()) File::Delete(dtmname); // Move written state to final location. @@ -867,13 +868,14 @@ void LoadAs(const std::string& filename) Core::RunOnCPUThread( [&] { // Save temp buffer for undo load state - if (!Movie::IsJustStartingRecordingInputFromSaveState()) + auto& movie = Core::System::GetInstance().GetMovie(); + if (!movie.IsJustStartingRecordingInputFromSaveState()) { std::lock_guard lk2(s_undo_load_buffer_mutex); SaveToBuffer(s_undo_load_buffer); const std::string dtmpath = File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm"; - if (Movie::IsMovieActive()) - Movie::SaveRecording(dtmpath); + if (movie.IsMovieActive()) + movie.SaveRecording(dtmpath); else if (File::Exists(dtmpath)) File::Delete(dtmpath); } @@ -904,10 +906,10 @@ void LoadAs(const std::string& filename) Core::DisplayMessage( fmt::format("Loaded State from {}", tempfilename.filename().string()), 2000); if (File::Exists(filename + ".dtm")) - Movie::LoadInput(filename + ".dtm"); - else if (!Movie::IsJustStartingRecordingInputFromSaveState() && - !Movie::IsJustStartingPlayingInputFromSaveState()) - Movie::EndPlayInput(false); + movie.LoadInput(filename + ".dtm"); + else if (!movie.IsJustStartingRecordingInputFromSaveState() && + !movie.IsJustStartingPlayingInputFromSaveState()) + movie.EndPlayInput(false); } else { @@ -1015,13 +1017,14 @@ void UndoLoadState() std::lock_guard lk(s_undo_load_buffer_mutex); if (!s_undo_load_buffer.empty()) { - if (Movie::IsMovieActive()) + auto& movie = Core::System::GetInstance().GetMovie(); + if (movie.IsMovieActive()) { const std::string dtmpath = File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm"; if (File::Exists(dtmpath)) { LoadFromBuffer(s_undo_load_buffer); - Movie::LoadInput(dtmpath); + movie.LoadInput(dtmpath); } else { diff --git a/Source/Core/Core/System.cpp b/Source/Core/Core/System.cpp index fa325d909b..8dbffa3abb 100644 --- a/Source/Core/Core/System.cpp +++ b/Source/Core/Core/System.cpp @@ -26,6 +26,7 @@ #include "Core/HW/SystemTimers.h" #include "Core/HW/VideoInterface.h" #include "Core/HW/WII_IPC.h" +#include "Core/Movie.h" #include "Core/PowerPC/Interpreter/Interpreter.h" #include "Core/PowerPC/JitInterface.h" #include "Core/PowerPC/PowerPC.h" @@ -52,7 +53,7 @@ struct System::Impl m_mmu(system, m_memory, m_power_pc), m_processor_interface(system), m_serial_interface(system), m_system_timers(system), m_video_interface(system), m_interpreter(system, m_power_pc.GetPPCState(), m_mmu), m_jit_interface(system), - m_fifo_player(system), m_fifo_recorder(system) + m_fifo_player(system), m_fifo_recorder(system), m_movie(system) { } @@ -93,6 +94,7 @@ struct System::Impl VideoCommon::CustomAssetLoader m_custom_asset_loader; FifoPlayer m_fifo_player; FifoRecorder m_fifo_recorder; + Movie::MovieManager m_movie; }; System::System() : m_impl{std::make_unique(*this)} @@ -248,6 +250,11 @@ PowerPC::MMU& System::GetMMU() const return m_impl->m_mmu; } +Movie::MovieManager& System::GetMovie() const +{ + return m_impl->m_movie; +} + PixelEngine::PixelEngineManager& System::GetPixelEngine() const { return m_impl->m_pixel_engine; diff --git a/Source/Core/Core/System.h b/Source/Core/Core/System.h index c835464987..742c63bc23 100644 --- a/Source/Core/Core/System.h +++ b/Source/Core/Core/System.h @@ -74,6 +74,10 @@ namespace MemoryInterface { class MemoryInterfaceManager; }; +namespace Movie +{ +class MovieManager; +} namespace PixelEngine { class PixelEngineManager; @@ -161,6 +165,7 @@ public: Memory::MemoryManager& GetMemory() const; MemoryInterface::MemoryInterfaceManager& GetMemoryInterface() const; PowerPC::MMU& GetMMU() const; + Movie::MovieManager& GetMovie() const; PixelEngine::PixelEngineManager& GetPixelEngine() const; PixelShaderManager& GetPixelShaderManager() const; PowerPC::PowerPCManager& GetPowerPC() const; diff --git a/Source/Core/Core/WiiRoot.cpp b/Source/Core/Core/WiiRoot.cpp index 94024d63ea..a6486d3fa5 100644 --- a/Source/Core/Core/WiiRoot.cpp +++ b/Source/Core/Core/WiiRoot.cpp @@ -28,6 +28,7 @@ #include "Core/Movie.h" #include "Core/NetPlayClient.h" #include "Core/SysConf.h" +#include "Core/System.h" namespace Core { @@ -127,24 +128,25 @@ static bool CopyNandFile(FS::FileSystem* source_fs, const std::string& source_fi static void InitializeDeterministicWiiSaves(FS::FileSystem* session_fs, const BootSessionData& boot_session_data) { + auto& movie = Core::System::GetInstance().GetMovie(); const u64 title_id = SConfig::GetInstance().GetTitleID(); const auto configured_fs = FS::MakeFileSystem(FS::Location::Configured); - if (Movie::IsRecordingInput()) + if (movie.IsRecordingInput()) { if (NetPlay::IsNetPlayRunning() && !SConfig::GetInstance().bCopyWiiSaveNetplay) { - Movie::SetClearSave(true); + movie.SetClearSave(true); } else { // TODO: Check for the actual save data const std::string path = Common::GetTitleDataPath(title_id) + "/banner.bin"; - Movie::SetClearSave(!configured_fs->GetMetadata(IOS::PID_KERNEL, IOS::PID_KERNEL, path)); + movie.SetClearSave(!configured_fs->GetMetadata(IOS::PID_KERNEL, IOS::PID_KERNEL, path)); } } if ((NetPlay::IsNetPlayRunning() && SConfig::GetInstance().bCopyWiiSaveNetplay) || - (Movie::IsMovieActive() && !Movie::IsStartingFromClearSave())) + (movie.IsMovieActive() && !movie.IsStartingFromClearSave())) { auto* sync_fs = boot_session_data.GetWiiSyncFS(); auto& sync_titles = boot_session_data.GetWiiSyncTitles(); @@ -154,7 +156,7 @@ static void InitializeDeterministicWiiSaves(FS::FileSystem* session_fs, sync_fs ? "sync_fs" : "configured_fs"); // Copy the current user's save to the Blank NAND - if (Movie::IsMovieActive() && !NetPlay::IsNetPlayRunning()) + if (movie.IsMovieActive() && !NetPlay::IsNetPlayRunning()) { INFO_LOG_FMT(CORE, "Wii Save Init: Copying {0:016x}.", title_id); CopySave(source_fs, session_fs, title_id); diff --git a/Source/Core/DolphinQt/Achievements/AchievementSettingsWidget.cpp b/Source/Core/DolphinQt/Achievements/AchievementSettingsWidget.cpp index 216273c23a..dfd415e912 100644 --- a/Source/Core/DolphinQt/Achievements/AchievementSettingsWidget.cpp +++ b/Source/Core/DolphinQt/Achievements/AchievementSettingsWidget.cpp @@ -15,6 +15,7 @@ #include "Core/Config/MainSettings.h" #include "Core/Core.h" #include "Core/Movie.h" +#include "Core/System.h" #include "DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h" #include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h" @@ -197,9 +198,9 @@ void AchievementSettingsWidget::LoadSettings() SignalBlocking(m_common_hardcore_enabled_input) ->setChecked(Config::Get(Config::RA_HARDCORE_ENABLED)); SignalBlocking(m_common_hardcore_enabled_input) - ->setEnabled(enabled && - (hardcore_enabled || - (Core::GetState() == Core::State::Uninitialized && !Movie::IsPlayingInput()))); + ->setEnabled(enabled && (hardcore_enabled || + (Core::GetState() == Core::State::Uninitialized && + !Core::System::GetInstance().GetMovie().IsPlayingInput()))); SignalBlocking(m_common_progress_enabled_input) ->setChecked(Config::Get(Config::RA_PROGRESS_ENABLED)); diff --git a/Source/Core/DolphinQt/GBAWidget.cpp b/Source/Core/DolphinQt/GBAWidget.cpp index b355efddd3..15aef84a15 100644 --- a/Source/Core/DolphinQt/GBAWidget.cpp +++ b/Source/Core/DolphinQt/GBAWidget.cpp @@ -365,7 +365,7 @@ void GBAWidget::SaveSettings() bool GBAWidget::CanControlCore() { - return !Movie::IsMovieActive() && !NetPlay::IsNetPlayRunning(); + return !Core::System::GetInstance().GetMovie().IsMovieActive() && !NetPlay::IsNetPlayRunning(); } bool GBAWidget::CanResetCore() diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index 74e6198c84..b644c9df07 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -277,7 +277,7 @@ MainWindow::MainWindow(std::unique_ptr boot_parameters, if (!movie_path.empty()) { std::optional savestate_path; - if (Movie::PlayInput(movie_path, &savestate_path)) + if (Core::System::GetInstance().GetMovie().PlayInput(movie_path, &savestate_path)) { m_pending_boot->boot_session_data.SetSavestateData(std::move(savestate_path), DeleteSavestateAfterBoot::No); @@ -646,8 +646,9 @@ void MainWindow::ConnectHotkeys() connect(m_hotkey_scheduler, &HotkeyScheduler::ConnectWiiRemote, this, &MainWindow::OnConnectWiiRemote); connect(m_hotkey_scheduler, &HotkeyScheduler::ToggleReadOnlyMode, [this] { - bool read_only = !Movie::IsReadOnly(); - Movie::SetReadOnly(read_only); + auto& movie = Core::System::GetInstance().GetMovie(); + bool read_only = !movie.IsReadOnly(); + movie.SetReadOnly(read_only); emit ReadOnlyModeChanged(read_only); }); @@ -1011,9 +1012,10 @@ void MainWindow::ForceStop() void MainWindow::Reset() { - if (Movie::IsRecordingInput()) - Movie::SetReset(true); auto& system = Core::System::GetInstance(); + auto& movie = system.GetMovie(); + if (movie.IsRecordingInput()) + movie.SetReset(true); system.GetProcessorInterface().ResetButton_Tap(); } @@ -1853,15 +1855,16 @@ void MainWindow::OnPlayRecording() if (dtm_file.isEmpty()) return; - if (!Movie::IsReadOnly()) + auto& movie = Core::System::GetInstance().GetMovie(); + if (!movie.IsReadOnly()) { // let's make the read-only flag consistent at the start of a movie. - Movie::SetReadOnly(true); + movie.SetReadOnly(true); emit ReadOnlyModeChanged(true); } std::optional savestate_path; - if (Movie::PlayInput(dtm_file.toStdString(), &savestate_path)) + if (movie.PlayInput(dtm_file.toStdString(), &savestate_path)) { emit RecordingStatusChanged(true); @@ -1871,14 +1874,17 @@ void MainWindow::OnPlayRecording() void MainWindow::OnStartRecording() { - if ((!Core::IsRunningAndStarted() && Core::IsRunning()) || Movie::IsRecordingInput() || - Movie::IsPlayingInput()) + auto& movie = Core::System::GetInstance().GetMovie(); + if ((!Core::IsRunningAndStarted() && Core::IsRunning()) || movie.IsRecordingInput() || + movie.IsPlayingInput()) + { return; + } - if (Movie::IsReadOnly()) + if (movie.IsReadOnly()) { // The user just chose to record a movie, so that should take precedence - Movie::SetReadOnly(false); + movie.SetReadOnly(false); emit ReadOnlyModeChanged(true); } @@ -1897,7 +1903,7 @@ void MainWindow::OnStartRecording() wiimotes[i] = Config::Get(Config::GetInfoForWiimoteSource(i)) != WiimoteSource::None; } - if (Movie::BeginRecordingInput(controllers, wiimotes)) + if (movie.BeginRecordingInput(controllers, wiimotes)) { emit RecordingStatusChanged(true); @@ -1908,10 +1914,11 @@ void MainWindow::OnStartRecording() void MainWindow::OnStopRecording() { - if (Movie::IsRecordingInput()) + auto& movie = Core::System::GetInstance().GetMovie(); + if (movie.IsRecordingInput()) OnExportRecording(); - if (Movie::IsMovieActive()) - Movie::EndPlayInput(false); + if (movie.IsMovieActive()) + movie.EndPlayInput(false); emit RecordingStatusChanged(false); } @@ -1921,7 +1928,7 @@ void MainWindow::OnExportRecording() QString dtm_file = DolphinFileDialog::getSaveFileName( this, tr("Save Recording File As"), QString(), tr("Dolphin TAS Movies (*.dtm)")); if (!dtm_file.isEmpty()) - Movie::SaveRecording(dtm_file.toStdString()); + Core::System::GetInstance().GetMovie().SaveRecording(dtm_file.toStdString()); }); } diff --git a/Source/Core/DolphinQt/MenuBar.cpp b/Source/Core/DolphinQt/MenuBar.cpp index 1fa1a7d562..fbfc83ef46 100644 --- a/Source/Core/DolphinQt/MenuBar.cpp +++ b/Source/Core/DolphinQt/MenuBar.cpp @@ -146,7 +146,8 @@ void MenuBar::OnEmulationStateChanged(Core::State state) #else // USE_RETRO_ACHIEVEMENTS m_recording_play->setEnabled(m_game_selected && !running); #endif // USE_RETRO_ACHIEVEMENTS - m_recording_start->setEnabled((m_game_selected || running) && !Movie::IsPlayingInput()); + m_recording_start->setEnabled((m_game_selected || running) && + !Core::System::GetInstance().GetMovie().IsPlayingInput()); // JIT m_jit_interpreter_core->setEnabled(running); @@ -775,8 +776,9 @@ void MenuBar::AddMovieMenu() m_recording_read_only = movie_menu->addAction(tr("&Read-Only Mode")); m_recording_read_only->setCheckable(true); - m_recording_read_only->setChecked(Movie::IsReadOnly()); - connect(m_recording_read_only, &QAction::toggled, [](bool value) { Movie::SetReadOnly(value); }); + m_recording_read_only->setChecked(Core::System::GetInstance().GetMovie().IsReadOnly()); + connect(m_recording_read_only, &QAction::toggled, + [](bool value) { Core::System::GetInstance().GetMovie().SetReadOnly(value); }); movie_menu->addAction(tr("TAS Input"), this, [this] { emit ShowTASInput(); }); @@ -1231,7 +1233,8 @@ void MenuBar::OnSelectionChanged(std::shared_ptr game_ m_game_selected = !!game_file; m_recording_play->setEnabled(m_game_selected && !Core::IsRunning()); - m_recording_start->setEnabled((m_game_selected || Core::IsRunning()) && !Movie::IsPlayingInput()); + m_recording_start->setEnabled((m_game_selected || Core::IsRunning()) && + !Core::System::GetInstance().GetMovie().IsPlayingInput()); } void MenuBar::OnRecordingStatusChanged(bool recording) diff --git a/Source/Core/DolphinQt/TAS/TASCheckBox.cpp b/Source/Core/DolphinQt/TAS/TASCheckBox.cpp index bd3865e689..3e7f998339 100644 --- a/Source/Core/DolphinQt/TAS/TASCheckBox.cpp +++ b/Source/Core/DolphinQt/TAS/TASCheckBox.cpp @@ -6,6 +6,7 @@ #include #include "Core/Movie.h" +#include "Core/System.h" #include "DolphinQt/QtUtils/QueueOnObject.h" #include "DolphinQt/TAS/TASInputWindow.h" @@ -23,7 +24,8 @@ bool TASCheckBox::GetValue() const if (check_state == Qt::PartiallyChecked) { - const u64 frames_elapsed = Movie::GetCurrentFrame() - m_frame_turbo_started; + const u64 frames_elapsed = + Core::System::GetInstance().GetMovie().GetCurrentFrame() - m_frame_turbo_started; return static_cast(frames_elapsed % m_turbo_total_frames) < m_turbo_press_frames; } @@ -50,7 +52,7 @@ void TASCheckBox::mousePressEvent(QMouseEvent* event) return; } - m_frame_turbo_started = Movie::GetCurrentFrame(); + m_frame_turbo_started = Core::System::GetInstance().GetMovie().GetCurrentFrame(); m_turbo_press_frames = m_parent->GetTurboPressFrames(); m_turbo_total_frames = m_turbo_press_frames + m_parent->GetTurboReleaseFrames(); setCheckState(Qt::PartiallyChecked); diff --git a/Source/Core/VideoCommon/OnScreenUI.cpp b/Source/Core/VideoCommon/OnScreenUI.cpp index fdb5d49af7..dce1c2146f 100644 --- a/Source/Core/VideoCommon/OnScreenUI.cpp +++ b/Source/Core/VideoCommon/OnScreenUI.cpp @@ -11,6 +11,7 @@ #include "Core/Config/MainSettings.h" #include "Core/Config/NetplaySettings.h" #include "Core/Movie.h" +#include "Core/System.h" #include "VideoCommon/AbstractGfx.h" #include "VideoCommon/AbstractPipeline.h" @@ -284,26 +285,27 @@ void OnScreenUI::DrawDebugText() ImGui::GetIO().DisplaySize); if (ImGui::Begin("Movie", nullptr, ImGuiWindowFlags_NoFocusOnAppearing)) { - if (Movie::IsPlayingInput()) + auto& movie = Core::System::GetInstance().GetMovie(); + if (movie.IsPlayingInput()) { - ImGui::Text("Frame: %" PRIu64 " / %" PRIu64, Movie::GetCurrentFrame(), - Movie::GetTotalFrames()); - ImGui::Text("Input: %" PRIu64 " / %" PRIu64, Movie::GetCurrentInputCount(), - Movie::GetTotalInputCount()); + ImGui::Text("Frame: %" PRIu64 " / %" PRIu64, movie.GetCurrentFrame(), + movie.GetTotalFrames()); + ImGui::Text("Input: %" PRIu64 " / %" PRIu64, movie.GetCurrentInputCount(), + movie.GetTotalInputCount()); } else if (Config::Get(Config::MAIN_SHOW_FRAME_COUNT)) { - ImGui::Text("Frame: %" PRIu64, Movie::GetCurrentFrame()); - ImGui::Text("Input: %" PRIu64, Movie::GetCurrentInputCount()); + ImGui::Text("Frame: %" PRIu64, movie.GetCurrentFrame()); + ImGui::Text("Input: %" PRIu64, movie.GetCurrentInputCount()); } if (Config::Get(Config::MAIN_SHOW_LAG)) - ImGui::Text("Lag: %" PRIu64 "\n", Movie::GetCurrentLagCount()); + ImGui::Text("Lag: %" PRIu64 "\n", movie.GetCurrentLagCount()); if (Config::Get(Config::MAIN_MOVIE_SHOW_INPUT_DISPLAY)) - ImGui::TextUnformatted(Movie::GetInputDisplay().c_str()); + ImGui::TextUnformatted(movie.GetInputDisplay().c_str()); if (Config::Get(Config::MAIN_MOVIE_SHOW_RTC)) - ImGui::TextUnformatted(Movie::GetRTCDisplay().c_str()); + ImGui::TextUnformatted(movie.GetRTCDisplay().c_str()); if (Config::Get(Config::MAIN_MOVIE_SHOW_RERECORD)) - ImGui::TextUnformatted(Movie::GetRerecords().c_str()); + ImGui::TextUnformatted(movie.GetRerecords().c_str()); } ImGui::End(); } diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index daba4be9e1..51ab90fb7e 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -47,8 +47,9 @@ static bool IsVSyncActive(bool enabled) void UpdateActiveConfig() { - if (Movie::IsPlayingInput() && Movie::IsConfigSaved()) - Movie::SetGraphicsConfig(); + auto& movie = Core::System::GetInstance().GetMovie(); + if (movie.IsPlayingInput() && movie.IsConfigSaved()) + movie.SetGraphicsConfig(); g_ActiveConfig = g_Config; g_ActiveConfig.bVSyncActive = IsVSyncActive(g_ActiveConfig.bVSync); }