From 73c3c0baeee06c6f2fe9b73a4e0b78071313fb24 Mon Sep 17 00:00:00 2001 From: LibretroAdmin Date: Sat, 7 Nov 2026 15:13:44 +0100 Subject: [PATCH] Remove Movie (TAS input recording/playback) Nothing in the core starts a recording or a playback, so a movie is never active. Each call site keeps its no-movie path: no determinism toggle, the real clock for the RTC, the configured memory cards, no pad/Wiimote input mirroring into display strings on every poll. The state keeps the movie counters' bytes, written as zeroes, so existing savestates still load. --- Makefile.common | 2 - Source/Core/Core/BootManager.cpp | 22 - .../Core/ConfigLoaders/MovieConfigLoader.cpp | 166 -- .../Core/ConfigLoaders/MovieConfigLoader.h | 34 - Source/Core/Core/Core.cpp | 10 +- Source/Core/Core/HW/DVD/AMMediaboard.cpp | 1 - Source/Core/Core/HW/DVD/DVDInterface.cpp | 4 +- Source/Core/Core/HW/EXI/EXI.cpp | 29 +- Source/Core/Core/HW/EXI/EXI_Channel.cpp | 4 +- Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp | 19 +- .../Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp | 39 +- .../Core/Core/HW/EXI/EXI_DeviceMemoryCard.h | 13 +- Source/Core/Core/HW/SI/SI.cpp | 1 - .../Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp | 1 - Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp | 2 - Source/Core/Core/HW/SI/SI_DeviceGCAdapter.cpp | 2 - .../Core/Core/HW/SI/SI_DeviceGCController.cpp | 23 - .../Core/Core/HW/SI/SI_DeviceGCController.h | 7 - Source/Core/Core/HW/VideoInterface.cpp | 7 - Source/Core/Core/HW/Wiimote.cpp | 6 - Source/Core/Core/IOS/FS/HostBackend/FS.cpp | 6 +- Source/Core/Core/IOS/USB/Bluetooth/BTEmu.cpp | 11 - Source/Core/Core/Movie.cpp | 1478 ----------------- Source/Core/Core/Movie.h | 289 ---- Source/Core/Core/State.cpp | 55 +- Source/Core/Core/System.cpp | 8 +- Source/Core/Core/System.h | 5 - Source/Core/Core/WiiRoot.cpp | 43 - Source/Core/VideoCommon/VideoConfig.cpp | 1 - 29 files changed, 31 insertions(+), 2257 deletions(-) delete mode 100644 Source/Core/Core/ConfigLoaders/MovieConfigLoader.cpp delete mode 100644 Source/Core/Core/ConfigLoaders/MovieConfigLoader.h delete mode 100644 Source/Core/Core/Movie.cpp delete mode 100644 Source/Core/Core/Movie.h diff --git a/Makefile.common b/Makefile.common index bc1ff38a3f..71ee0432ef 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1299,7 +1299,6 @@ DOLPHIN_SOURCES_CXX += \ $(CORE_DIR)/Source/Core/Core/ConfigLoaders/BaseConfigLoader.cpp \ $(CORE_DIR)/Source/Core/Core/ConfigLoaders/GameConfigLoader.cpp \ $(CORE_DIR)/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp \ - $(CORE_DIR)/Source/Core/Core/ConfigLoaders/MovieConfigLoader.cpp \ $(CORE_DIR)/Source/Core/Core/ConfigManager.cpp \ $(CORE_DIR)/Source/Core/Core/Core.cpp \ $(CORE_DIR)/Source/Core/Core/CoreTiming.cpp \ @@ -1522,7 +1521,6 @@ DOLPHIN_SOURCES_CXX += \ $(CORE_DIR)/Source/Core/Core/IOS/WFS/WFSSRV.cpp \ $(CORE_DIR)/Source/Core/Core/LibusbUtils.cpp \ $(CORE_DIR)/Source/Core/Core/MemTools.cpp \ - $(CORE_DIR)/Source/Core/Core/Movie.cpp \ $(CORE_DIR)/Source/Core/Core/NetworkCaptureLogger.cpp \ $(CORE_DIR)/Source/Core/Core/PatchEngine.cpp \ $(CORE_DIR)/Source/Core/Core/PowerPC/BreakPoints.cpp \ diff --git a/Source/Core/Core/BootManager.cpp b/Source/Core/Core/BootManager.cpp index a1575e57fb..81ff5be709 100644 --- a/Source/Core/Core/BootManager.cpp +++ b/Source/Core/Core/BootManager.cpp @@ -38,7 +38,6 @@ #include "Core/HW/SI/SI.h" #include "Core/HW/SI/SI_Device.h" #include "Core/HW/Sram.h" -#include "Core/Movie.h" #include "Core/System.h" #include "Core/WiiRoot.h" @@ -58,27 +57,6 @@ bool BootCore(Core::System& system, std::unique_ptr boot, if (!StartUp.SetPathsAndGameMetadata(system, *boot)) return false; - // Movie settings - auto& movie = system.GetMovie(); - if (movie.IsPlayingInput() && movie.IsConfigSaved()) - { - for (ExpansionInterface::Slot slot : ExpansionInterface::MEMCARD_SLOTS) - { - if (movie.IsUsingMemcard(slot) && movie.IsStartingFromClearSave() && !system.IsWii()) - { - const auto raw_path = - File::GetUserPath(D_GCUSER_IDX) + - fmt::format("Movie{}.raw", slot == ExpansionInterface::Slot::A ? 'A' : 'B'); - if (File::Exists(raw_path)) - File::Delete(raw_path); - - const auto movie_path = File::GetUserPath(D_GCUSER_IDX) + "Movie"; - if (File::Exists(movie_path)) - File::DeleteDirRecursively(movie_path); - } - } - } - // Override out-of-region languages/countries to prevent games from crashing or behaving oddly if (!Config::Get(Config::MAIN_OVERRIDE_REGION_SETTINGS)) { diff --git a/Source/Core/Core/ConfigLoaders/MovieConfigLoader.cpp b/Source/Core/Core/ConfigLoaders/MovieConfigLoader.cpp deleted file mode 100644 index 54ea60357a..0000000000 --- a/Source/Core/Core/ConfigLoaders/MovieConfigLoader.cpp +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "Core/ConfigLoaders/MovieConfigLoader.h" - -#include -#include -#include - -#include "Common/CommonTypes.h" -#include "Common/Config/Config.h" - -#include "Core/Config/GraphicsSettings.h" -#include "Core/Config/MainSettings.h" -#include "Core/Config/SYSCONFSettings.h" -#include "Core/Config/SessionSettings.h" -#include "Core/Config/WiimoteSettings.h" -#include "Core/HW/SI/SI.h" -#include "Core/HW/SI/SI_Device.h" -#include "Core/HW/Wiimote.h" -#include "Core/Movie.h" - -namespace PowerPC -{ -enum class CPUCore; -} - -namespace ConfigLoaders -{ -static void LoadFromDTM(Config::Layer* config_layer, Movie::DTMHeader* dtm) -{ - config_layer->Set(Config::MAIN_CPU_THREAD, dtm->bDualCore); - config_layer->Set(Config::MAIN_DSP_HLE, dtm->bDSPHLE); - config_layer->Set(Config::MAIN_FAST_DISC_SPEED, dtm->bFastDiscSpeed); - config_layer->Set(Config::MAIN_CPU_CORE, static_cast(dtm->CPUCore)); - config_layer->Set(Config::MAIN_SYNC_GPU, dtm->bSyncGPU); - config_layer->Set(Config::MAIN_GFX_BACKEND, dtm->videoBackend.data()); - - config_layer->Set(Config::SYSCONF_PROGRESSIVE_SCAN, dtm->bProgressive); - config_layer->Set(Config::SYSCONF_PAL60, dtm->bPAL60); - if (dtm->bWii) - config_layer->Set(Config::SYSCONF_LANGUAGE, static_cast(dtm->language)); - else - config_layer->Set(Config::MAIN_GC_LANGUAGE, static_cast(dtm->language)); - config_layer->Set(Config::SYSCONF_WIDESCREEN, dtm->bWidescreen); - config_layer->Set(Config::SYSCONF_COUNTRY, dtm->countryCode); - - config_layer->Set(Config::GFX_HACK_EFB_ACCESS_ENABLE, dtm->bEFBAccessEnable); - config_layer->Set(Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM, dtm->bSkipEFBCopyToRam); - config_layer->Set(Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES, dtm->bEFBEmulateFormatChanges); - config_layer->Set(Config::GFX_HACK_IMMEDIATE_XFB, dtm->bImmediateXFB); - config_layer->Set(Config::GFX_HACK_SKIP_XFB_COPY_TO_RAM, dtm->bSkipXFBCopyToRam); - - config_layer->Set(Config::SESSION_USE_FMA, dtm->bUseFMA); - - config_layer->Set(Config::MAIN_JIT_FOLLOW_BRANCH, dtm->bFollowBranch); - - for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i) - { - SerialInterface::SIDevices device = SerialInterface::SIDEVICE_NONE; - const auto config_info = Config::GetInfoForSIDevice(i); - if (dtm->GBAControllers & (1 << i)) - { - device = SerialInterface::SIDEVICE_GC_GBA_EMULATED; - } - else if (dtm->controllers & (1 << i)) - { - const SerialInterface::SIDevices si_device = Config::Get(config_info); - if (SerialInterface::SIDevice_IsGCController(si_device)) - { - device = si_device; - } - else - { - device = (dtm->bongos & (1 << i)) != 0 ? SerialInterface::SIDEVICE_GC_TARUKONGA : - SerialInterface::SIDEVICE_GC_CONTROLLER; - } - } - - config_layer->Set(config_info, device); - } - - for (int i = 0; i < MAX_WIIMOTES; ++i) - { - const bool is_using_wiimote = (dtm->controllers & (1 << (i + 4))) != 0; - config_layer->Set(Config::GetInfoForWiimoteSource(i), - is_using_wiimote ? WiimoteSource::Emulated : WiimoteSource::None); - } -} - -void SaveToDTM(Movie::DTMHeader* dtm) -{ - dtm->bDualCore = Config::Get(Config::MAIN_CPU_THREAD); - dtm->bDSPHLE = Config::Get(Config::MAIN_DSP_HLE); - dtm->bFastDiscSpeed = Config::Get(Config::MAIN_FAST_DISC_SPEED); - dtm->CPUCore = static_cast(Config::Get(Config::MAIN_CPU_CORE)); - dtm->bSyncGPU = Config::Get(Config::MAIN_SYNC_GPU); - const std::string video_backend = Config::Get(Config::MAIN_GFX_BACKEND); - - dtm->bProgressive = Config::Get(Config::SYSCONF_PROGRESSIVE_SCAN); - dtm->bPAL60 = Config::Get(Config::SYSCONF_PAL60); - if (dtm->bWii) - dtm->language = Config::Get(Config::SYSCONF_LANGUAGE); - else - dtm->language = Config::Get(Config::MAIN_GC_LANGUAGE); - dtm->bWidescreen = Config::Get(Config::SYSCONF_WIDESCREEN); - dtm->countryCode = Config::Get(Config::SYSCONF_COUNTRY); - - dtm->bEFBAccessEnable = Config::Get(Config::GFX_HACK_EFB_ACCESS_ENABLE); - dtm->bSkipEFBCopyToRam = Config::Get(Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM); - dtm->bEFBEmulateFormatChanges = Config::Get(Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES); - dtm->bImmediateXFB = Config::Get(Config::GFX_HACK_IMMEDIATE_XFB); - dtm->bSkipXFBCopyToRam = Config::Get(Config::GFX_HACK_SKIP_XFB_COPY_TO_RAM); - - dtm->bUseFMA = Config::Get(Config::SESSION_USE_FMA); - - dtm->bFollowBranch = Config::Get(Config::MAIN_JIT_FOLLOW_BRANCH); - - dtm->controllers = 0; - dtm->bongos = 0; - dtm->GBAControllers = 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_NONE) - dtm->controllers |= 1 << i; - - if (si_device == SerialInterface::SIDEVICE_GC_GBA_EMULATED) - dtm->GBAControllers |= 1 << i; - else if (si_device == SerialInterface::SIDEVICE_GC_TARUKONGA) - dtm->bongos |= 1 << i; - } - if (dtm->bWii) - { - for (int i = 0; i < MAX_WIIMOTES; ++i) - { - if (Config::Get(Config::GetInfoForWiimoteSource(i)) != WiimoteSource::None) - dtm->controllers |= 1 << (i + 4); - } - } - - // Settings which only existed in old Dolphin versions - dtm->bSkipIdle = true; - dtm->bEFBCopyEnable = true; - dtm->bEFBCopyCacheEnable = false; - - strncpy(dtm->videoBackend.data(), video_backend.c_str(), dtm->videoBackend.size()); -} - -// TODO: Future project, let this support all the configuration options. -// This will require a large break to the DTM format -void MovieConfigLayerLoader::Load(Config::Layer* config_layer) -{ - LoadFromDTM(config_layer, m_header); -} - -void MovieConfigLayerLoader::Save(Config::Layer* config_layer) -{ -} - -// Loader generation -std::unique_ptr GenerateMovieConfigLoader(Movie::DTMHeader* header) -{ - return std::make_unique(header); -} -} // namespace ConfigLoaders diff --git a/Source/Core/Core/ConfigLoaders/MovieConfigLoader.h b/Source/Core/Core/ConfigLoaders/MovieConfigLoader.h deleted file mode 100644 index fbfb780843..0000000000 --- a/Source/Core/Core/ConfigLoaders/MovieConfigLoader.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -#include "Common/Config/Config.h" - -namespace Movie -{ -struct DTMHeader; -} - -namespace ConfigLoaders -{ -class MovieConfigLayerLoader final : public Config::ConfigLayerLoader -{ -public: - explicit MovieConfigLayerLoader(Movie::DTMHeader* header) - : ConfigLayerLoader(Config::LayerType::Movie), m_header(header) - { - } - - void Load(Config::Layer* config_layer) override; - void Save(Config::Layer* config_layer) override; - -private: - Movie::DTMHeader* m_header; -}; - -void SaveToDTM(Movie::DTMHeader* header); -std::unique_ptr GenerateMovieConfigLoader(Movie::DTMHeader* header); -} // namespace ConfigLoaders diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 191e3b5017..1b39fd4308 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -61,7 +61,6 @@ #include "Core/Host.h" #include "Core/IOS/IOS.h" #include "Core/MemTools.h" -#include "Core/Movie.h" #include "Core/PatchEngine.h" #include "Core/PowerPC/GDBStub.h" #include "Core/PowerPC/JitInterface.h" @@ -510,9 +509,6 @@ void EmuThread(Core::System& system, std::unique_ptr boot, FreeLook::LoadInputConfig(); - system.GetMovie().Init(*boot); - Common::ScopeGuard movie_guard([&system] { system.GetMovie().Shutdown(); }); - HW::Init(system, nullptr); Common::ScopeGuard hw_guard{[&system] { @@ -611,9 +607,6 @@ void EmuThread(Core::System& system, std::unique_ptr boot, s_emu_thread_scope_guards.emplace_back(Common::MoveOnlyFunction( [g = std::move(flag_guard)]() mutable { g.Exit(); })); - s_emu_thread_scope_guards.emplace_back(Common::MoveOnlyFunction( - [g = std::move(movie_guard)]() mutable { g.Exit(); })); - s_emu_thread_scope_guards.emplace_back(Common::MoveOnlyFunction( [g = std::move(hw_guard)]() mutable { g.Exit(); })); @@ -921,7 +914,8 @@ void UpdateWantDeterminism(Core::System& system, 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 = system.GetMovie().IsMovieActive(); + // Only a movie wanted determinism, and none is ever recorded or played. + const bool new_want_determinism = false; if (new_want_determinism != s_wants_determinism || initial) { NOTICE_LOG_FMT(COMMON, "Want determinism <- {}", new_want_determinism ? "true" : "false"); diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.cpp b/Source/Core/Core/HW/DVD/AMMediaboard.cpp index a87adbefb0..0bd5f3453a 100644 --- a/Source/Core/Core/HW/DVD/AMMediaboard.cpp +++ b/Source/Core/Core/HW/DVD/AMMediaboard.cpp @@ -27,7 +27,6 @@ #include "Core/HW/EXI/EXI_DeviceBaseboard.h" #include "Core/HW/Memmap.h" #include "Core/IOS/Network/Socket.h" -#include "Core/Movie.h" #include "Core/System.h" #include "DiscIO/CachedBlob.h" diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index 7813903fb9..48d9f2f808 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -36,7 +36,6 @@ #include "Core/HW/SystemTimers.h" #include "Core/IOS/DI/DI.h" #include "Core/IOS/IOS.h" -#include "Core/Movie.h" #include "Core/System.h" #include "DiscIO/Blob.h" @@ -499,7 +498,6 @@ void DVDInterface::ChangeDisc(const Core::CPUThreadGuard& guard, const std::stri m_disc_path_to_insert = new_path; m_system.GetCoreTiming().ScheduleEvent(m_system.GetSystemTimers().GetTicksPerSecond(), m_insert_disc); - m_system.GetMovie().SignalDiscChange(new_path); for (size_t i = 0; i < m_auto_disc_change_paths.size(); ++i) { @@ -1125,7 +1123,7 @@ void DVDInterface::ExecuteCommand(ReplyType reply_type) const bool force_eject = eject && !kill; - if (Config::Get(Config::MAIN_AUTO_DISC_CHANGE) && !m_system.GetMovie().IsPlayingInput() && + if (Config::Get(Config::MAIN_AUTO_DISC_CHANGE) && 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 144fdd57b9..0a6d9c99f1 100644 --- a/Source/Core/Core/HW/EXI/EXI.cpp +++ b/Source/Core/Core/HW/EXI/EXI.cpp @@ -20,7 +20,6 @@ #include "Core/HW/ProcessorInterface.h" #include "Core/HW/Sram.h" #include "Core/HW/SystemTimers.h" -#include "Core/Movie.h" #include "Core/System.h" #include "DiscIO/Enums.h" @@ -35,33 +34,7 @@ ExpansionInterfaceManager::~ExpansionInterfaceManager() = default; void ExpansionInterfaceManager::AddMemoryCard(Slot slot) { - EXIDeviceType memorycard_device; - - auto& movie = m_system.GetMovie(); - if (movie.IsPlayingInput() && movie.IsConfigSaved()) - { - if (movie.IsUsingMemcard(slot)) - { - memorycard_device = Config::Get(Config::GetInfoForEXIDevice(slot)); - if (memorycard_device != EXIDeviceType::MemoryCardFolder && - memorycard_device != EXIDeviceType::MemoryCard) - { - PanicAlertFmtT( - "The movie indicates that a memory card should be inserted into {0:n}, but one is not " - "currently inserted (instead, {1} is inserted). For the movie to sync properly, " - "please change the selected device to Memory Card or GCI Folder.", - slot, Common::GetStringT(fmt::format("{:n}", memorycard_device).c_str())); - } - } - else - { - memorycard_device = EXIDeviceType::None; - } - } - else - { - memorycard_device = Config::Get(Config::GetInfoForEXIDevice(slot)); - } + const EXIDeviceType memorycard_device = Config::Get(Config::GetInfoForEXIDevice(slot)); m_channels[SlotToEXIChannel(slot)]->AddDevice(memorycard_device, SlotToEXIDevice(slot)); } diff --git a/Source/Core/Core/HW/EXI/EXI_Channel.cpp b/Source/Core/Core/HW/EXI/EXI_Channel.cpp index 8d47a816e4..1844992925 100644 --- a/Source/Core/Core/HW/EXI/EXI_Channel.cpp +++ b/Source/Core/Core/HW/EXI/EXI_Channel.cpp @@ -14,7 +14,6 @@ #include "Core/HW/EXI/EXI.h" #include "Core/HW/EXI/EXI_Device.h" #include "Core/HW/MMIO.h" -#include "Core/Movie.h" #include "Core/System.h" namespace ExpansionInterface @@ -263,8 +262,7 @@ void CEXIChannel::DoState(PointerWrap& p) AddDevice(std::move(save_device), device_index, false); } - if (type == EXIDeviceType::MemoryCardFolder && old_header_data != m_memcard_header_data && - !m_system.GetMovie().IsMovieActive()) + if (type == EXIDeviceType::MemoryCardFolder && old_header_data != m_memcard_header_data) { // 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 77fd5385f5..ee01363913 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp @@ -25,7 +25,6 @@ #include "Core/CoreTiming.h" #include "Core/HW/Sram.h" #include "Core/HW/SystemTimers.h" -#include "Core/Movie.h" #include "Core/System.h" #include "DiscIO/Enums.h" @@ -399,22 +398,8 @@ void CEXIIPL::TransferByte(u8& data) u32 CEXIIPL::GetEmulatedTime(Core::System& system, u32 epoch) { - u64 ltime = 0; - - auto& movie = system.GetMovie(); - if (movie.IsMovieActive()) - { - ltime = movie.GetRecordingStartTime(); - - // let's keep time moving forward, regardless of what it starts at - ltime += system.GetCoreTiming().GetTicks() / system.GetSystemTimers().GetTicksPerSecond(); - } - else - { - ASSERT(!Core::WantsDeterminism()); - ltime = Common::Timer::GetLocalTimeSinceJan1970() - - system.GetSystemTimers().GetLocalTimeRTCOffset(); - } + const u64 ltime = Common::Timer::GetLocalTimeSinceJan1970() - + system.GetSystemTimers().GetLocalTimeRTCOffset(); return static_cast(ltime) - epoch; } diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp index 0d9f1fa81e..9f170870c3 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp @@ -32,7 +32,6 @@ #include "Core/HW/Memmap.h" #include "Core/HW/Sram.h" #include "Core/HW/SystemTimers.h" -#include "Core/Movie.h" #include "Core/System.h" #include "DiscIO/Enums.h" @@ -148,26 +147,14 @@ CEXIMemoryCard::CEXIMemoryCard(Core::System& system, const Slot slot, bool gci_f } std::pair -CEXIMemoryCard::GetGCIFolderPath(Slot card_slot, AllowMovieFolder allow_movie_folder, - Movie::MovieManager& movie) +CEXIMemoryCard::GetGCIFolderPath(Slot card_slot) { std::string path_override = Config::Get(Config::GetInfoForGCIPathOverride(card_slot)); if (!path_override.empty()) 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(); - const DiscIO::Region region = Config::ToGameCubeRegion(SConfig::GetInstance().m_region); - if (use_movie_folder) - { - return {fmt::format("{}{}/Movie/Card {}", File::GetUserPath(D_GCUSER_IDX), - Config::GetDirectoryForRegion(region), s_card_short_names[card_slot]), - false}; - } - return {Config::GetGCIFolderPath(card_slot, region), true}; } @@ -182,7 +169,7 @@ void CEXIMemoryCard::SetupGciFolder(const Memcard::HeaderData& header_data) } const auto [dir_path, migrate] = - GetGCIFolderPath(m_card_slot, AllowMovieFolder::Yes, m_system.GetMovie()); + GetGCIFolderPath(m_card_slot); const File::FileInfo file_info(dir_path); if (!file_info.Exists()) @@ -218,18 +205,8 @@ void CEXIMemoryCard::SetupGciFolder(const Memcard::HeaderData& header_data) void CEXIMemoryCard::SetupRawMemcard(u16 size_mb) { - std::string filename; - 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]); - } - else - { - filename = Config::GetMemcardPath(m_card_slot, SConfig::GetInstance().m_region, size_mb); - } + const std::string filename = + Config::GetMemcardPath(m_card_slot, SConfig::GetInstance().m_region, size_mb); m_memory_card = std::make_unique(filename, m_card_slot, size_mb); } @@ -501,11 +478,9 @@ void CEXIMemoryCard::TransferByte(u8& byte) void CEXIMemoryCard::DoState(PointerWrap& p) { - // for movie sync, we need to save/load memory card contents (and other data) in savestates. - // 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 = m_system.GetMovie().IsMovieActive(); + // Card contents are kept out of states (only movie sync stored them); a loaded state that + // has them still decides, to stay aligned with it. + bool storeContents = false; 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 db9c013671..8744a757b8 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h @@ -26,21 +26,11 @@ namespace Memcard { struct HeaderData; } -namespace Movie -{ -class MovieManager; -} namespace ExpansionInterface { enum class Slot : int; -enum class AllowMovieFolder -{ - Yes, - No, -}; - class CEXIMemoryCard : public IEXIDevice { public: @@ -61,8 +51,7 @@ public: static void Init(CoreTiming::CoreTimingManager& core_timing); static void Shutdown(); - static std::pair - GetGCIFolderPath(Slot card_slot, AllowMovieFolder allow_movie_folder, Movie::MovieManager& movie); + static std::pair GetGCIFolderPath(Slot card_slot); 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 3c6aa16489..0c2c6c61d7 100644 --- a/Source/Core/Core/HW/SI/SI.cpp +++ b/Source/Core/Core/HW/SI/SI.cpp @@ -25,7 +25,6 @@ #include "Core/HW/ProcessorInterface.h" #include "Core/HW/SI/SI_DeviceGBA.h" #include "Core/HW/SystemTimers.h" -#include "Core/Movie.h" #include "Core/System.h" #include "InputCommon/ControllerInterface/ControllerInterface.h" diff --git a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp index e2fbe7b8c2..048f548d67 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp +++ b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp @@ -30,7 +30,6 @@ #include "Core/HW/Triforce/JVSIO.h" #include "Core/HW/Triforce/MarioKartGP.h" #include "Core/HW/Triforce/Touchscreen.h" -#include "Core/Movie.h" #include "Core/System.h" namespace diff --git a/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp b/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp index 7ceb33564f..fce7cf7fb8 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp +++ b/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp @@ -122,8 +122,6 @@ int CSIDevice_GBAEmu::TransferInterval() DataResponse CSIDevice_GBAEmu::GetData(u32& hi, u32& low) { GCPadStatus pad_status = Pad::GetGBAStatus(m_device_number); - 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 4ee7a5ef68..9d501f3ec9 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceGCAdapter.cpp +++ b/Source/Core/Core/HW/SI/SI_DeviceGCAdapter.cpp @@ -29,8 +29,6 @@ GCPadStatus CSIDevice_GCAdapter::GetPadStatus() { GCPadStatus pad_status = GCAdapter::Input(m_device_number); - 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. if (pad_status.button & PAD_GET_ORIGIN) diff --git a/Source/Core/Core/HW/SI/SI_DeviceGCController.cpp b/Source/Core/Core/HW/SI/SI_DeviceGCController.cpp index 2289b97942..311efe8dd1 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceGCController.cpp +++ b/Source/Core/Core/HW/SI/SI_DeviceGCController.cpp @@ -14,7 +14,6 @@ #include "Core/HW/SI/SI.h" #include "Core/HW/SI/SI_Device.h" #include "Core/HW/SystemTimers.h" -#include "Core/Movie.h" #include "Core/System.h" #include "InputCommon/GCPadStatus.h" @@ -114,32 +113,10 @@ int CSIDevice_GCController::RunBuffer(u8* buffer, int request_length) return 0; } -void CSIDevice_GCController::HandleMoviePadStatus(Movie::MovieManager& movie, int device_number, - GCPadStatus* pad_status) -{ - movie.SetPolledDevice(); - if (movie.IsPlayingInput()) - { - movie.PlayController(pad_status, device_number); - movie.InputUpdate(); - } - else if (movie.IsRecordingInput()) - { - movie.RecordInput(pad_status, device_number); - movie.InputUpdate(); - } - else - { - movie.CheckPadStatus(pad_status, device_number); - } -} - GCPadStatus CSIDevice_GCController::GetPadStatus() { GCPadStatus pad_status = Pad::GetStatus(m_device_number); - 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. if (pad_status.button & PAD_GET_ORIGIN) diff --git a/Source/Core/Core/HW/SI/SI_DeviceGCController.h b/Source/Core/Core/HW/SI/SI_DeviceGCController.h index c8d8c12de6..2da7265647 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceGCController.h +++ b/Source/Core/Core/HW/SI/SI_DeviceGCController.h @@ -9,10 +9,6 @@ #include "Core/HW/SI/SI_Device.h" #include "InputCommon/GCPadStatus.h" -namespace Movie -{ -class MovieManager; -} namespace SerialInterface { @@ -80,9 +76,6 @@ public: // Direct rumble to the right GC Controller static void Rumble(int pad_num, ControlState strength, SIDevices device); - 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 7c90277ecc..6e2fec9ad8 100644 --- a/Source/Core/Core/HW/VideoInterface.cpp +++ b/Source/Core/Core/HW/VideoInterface.cpp @@ -26,7 +26,6 @@ #include "Core/HW/ProcessorInterface.h" #include "Core/HW/SI/SI.h" #include "Core/HW/SystemTimers.h" -#include "Core/Movie.h" #include "Core/System.h" #include "DiscIO/Enums.h" @@ -898,12 +897,6 @@ void VideoInterfaceManager::Update(u64 ticks) const bool is_at_field_boundary = m_half_line_count == odd_field_begin || m_half_line_count == even_field_begin; - // Movie's frame counter should be updated before actually rendering the frame, - // in case frame counter display is enabled - - if (is_at_field_boundary) - 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 // the number of frames we've actually drawn diff --git a/Source/Core/Core/HW/Wiimote.cpp b/Source/Core/Core/HW/Wiimote.cpp index c021134db2..5483c7bbdf 100644 --- a/Source/Core/Core/HW/Wiimote.cpp +++ b/Source/Core/Core/HW/Wiimote.cpp @@ -19,7 +19,6 @@ #include "Core/IOS/IOS.h" #include "Core/IOS/USB/Bluetooth/BTEmu.h" #include "Core/IOS/USB/Bluetooth/WiimoteDevice.h" -#include "Core/Movie.h" #include "Core/System.h" #include "Core/WiiUtils.h" @@ -194,11 +193,6 @@ void Initialize(InitializeMode init_mode) WiimoteCommon::RefreshConfig(); WiimoteReal::Initialize(init_mode); - - // Reload Wiimotes with our settings - auto& movie = Core::System::GetInstance().GetMovie(); - if (movie.IsMovieActive()) - movie.ChangeWiiPads(); } void ResetAllWiimotes() diff --git a/Source/Core/Core/IOS/FS/HostBackend/FS.cpp b/Source/Core/Core/IOS/FS/HostBackend/FS.cpp index 3fefefaf0a..deb489d7e6 100644 --- a/Source/Core/Core/IOS/FS/HostBackend/FS.cpp +++ b/Source/Core/Core/IOS/FS/HostBackend/FS.cpp @@ -23,7 +23,6 @@ #include "Common/Swap.h" #include "Core/IOS/ES/ES.h" #include "Core/IOS/IOS.h" -#include "Core/Movie.h" #include "Core/System.h" #include "Core/WiiRoot.h" @@ -322,9 +321,8 @@ 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) - auto& movie = Core::System::GetInstance().GetMovie(); - - const bool is_full_nand_wanted = movie.IsMovieActive() && Core::WiiRootIsTemporary(); + // No movie is ever recorded, so the full NAND is never wanted. + const bool is_full_nand_wanted = false; bool is_full_nand_in_state = is_full_nand_wanted; p.Do(is_full_nand_in_state); diff --git a/Source/Core/Core/IOS/USB/Bluetooth/BTEmu.cpp b/Source/Core/Core/IOS/USB/Bluetooth/BTEmu.cpp index 325d57ffa2..c51731700f 100644 --- a/Source/Core/Core/IOS/USB/Bluetooth/BTEmu.cpp +++ b/Source/Core/Core/IOS/USB/Bluetooth/BTEmu.cpp @@ -20,7 +20,6 @@ #include "Core/HW/WiimoteEmu/DesiredWiimoteState.h" #include "Core/IOS/Device.h" #include "Core/IOS/IOS.h" -#include "Core/Movie.h" #include "Core/SysConf.h" #include "Core/System.h" #include "InputCommon/ControllerInterface/ControllerInterface.h" @@ -349,16 +348,6 @@ void BluetoothEmuDevice::Update() for (size_t i = 0; i < m_wiimotes.size(); ++i) next_call[i] = m_wiimotes[i]->PrepareInput(&wiimote_states[i]); - auto& movie = Core::System::GetInstance().GetMovie(); - for (int i = 0; i != MAX_WIIMOTES; ++i) - { - if (next_call[i] == WiimoteDevice::NextUpdateInputCall::None) - continue; - - movie.PlayWiimote(i, &wiimote_states[i]); - movie.CheckWiimoteStatus(i, wiimote_states[i]); - } - for (size_t i = 0; i < m_wiimotes.size(); ++i) m_wiimotes[i]->UpdateInput(next_call[i], wiimote_states[i]); diff --git a/Source/Core/Core/Movie.cpp b/Source/Core/Core/Movie.cpp deleted file mode 100644 index b9e0ee8d13..0000000000 --- a/Source/Core/Core/Movie.cpp +++ /dev/null @@ -1,1478 +0,0 @@ -// Copyright 2009 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "Core/Movie.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "Common/Assert.h" -#include "Common/ChunkFile.h" -#include "Common/CommonPaths.h" -#include "Common/Config/Config.h" -#include "Common/FileUtil.h" -#include "Common/Hash.h" -#include "Common/IOFile.h" -#include "Common/MsgHandler.h" -#include "Common/NandPaths.h" -#include "Common/StringUtil.h" -#include "Common/Thread.h" -#include "Common/Timer.h" -#include "Common/VariantUtil.h" -#include "Common/Version.h" - -#include "Core/AchievementManager.h" -#include "Core/Boot/Boot.h" -#include "Core/Config/MainSettings.h" -#include "Core/Config/WiimoteSettings.h" -#include "Core/ConfigLoaders/MovieConfigLoader.h" -#include "Core/ConfigManager.h" -#include "Core/Core.h" -#include "Core/CoreTiming.h" -#include "Core/DSP/DSPCore.h" -#include "Core/HW/CPU.h" -#include "Core/HW/DVD/DVDInterface.h" -#include "Core/HW/EXI/EXI.h" -#include "Core/HW/EXI/EXI_DeviceIPL.h" -#include "Core/HW/EXI/EXI_DeviceMemoryCard.h" -#include "Core/HW/ProcessorInterface.h" -#include "Core/HW/SI/SI.h" -#include "Core/HW/SI/SI_Device.h" -#include "Core/HW/Wiimote.h" -#include "Core/HW/WiimoteCommon/WiimoteReport.h" -#include "Core/HW/WiimoteEmu/Extension/Classic.h" -#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h" -#include "Core/HW/WiimoteEmu/ExtensionPort.h" -#include "Core/IOS/USB/Bluetooth/BTEmu.h" -#include "Core/IOS/USB/Bluetooth/WiimoteDevice.h" -#include "Core/State.h" -#include "Core/System.h" -#include "Core/WiiUtils.h" - -#include "InputCommon/GCPadStatus.h" - -#include "VideoCommon/VideoBackendBase.h" - -namespace Movie -{ -using namespace WiimoteCommon; -using namespace WiimoteEmu; - -static bool IsMovieHeader(const std::array& magic) -{ - return magic[0] == 'D' && magic[1] == 'T' && magic[2] == 'M' && magic[3] == 0x1A; -} - -static std::array ConvertGitRevisionToBytes(const std::string& revision) -{ - std::array revision_bytes{}; - - if (revision.size() % 2 == 0 && std::ranges::all_of(revision, Common::IsXDigit)) - { - // The revision string normally contains a git commit hash, - // which is 40 hexadecimal digits long. In DTM files, each pair of - // hexadecimal digits is stored as one byte, for a total of 20 bytes. - size_t bytes_to_write = std::min(revision.size() / 2, revision_bytes.size()); - unsigned int temp; - for (size_t i = 0; i < bytes_to_write; ++i) - { - sscanf(&revision[2 * i], "%02x", &temp); - revision_bytes[i] = temp; - } - } - else - { - // If the revision string for some reason doesn't only contain hexadecimal digit - // pairs, we instead copy the string with no conversion. This probably doesn't match - // the intended design of the DTM format, but it's the most sensible fallback. - size_t bytes_to_write = std::min(revision.size(), revision_bytes.size()); - std::copy_n(std::begin(revision), bytes_to_write, std::begin(revision_bytes)); - } - - return revision_bytes; -} - -MovieManager::MovieManager(Core::System& system) : m_system(system) -{ -} - -MovieManager::~MovieManager() -{ - for (auto& slot : m_input_display) - delete static_cast(retro_atomic_exchange_ptr(&slot, nullptr)); - quiesce_deinit(&m_input_display_quiesce); -} - -void MovieManager::FreeDisplayString(quiesce_node_t* node) -{ - delete reinterpret_cast(node); -} - -void MovieManager::SetInputDisplay(int index, std::string text) -{ - auto* next = new DisplayString{{}, std::move(text)}; - if (void* old = retro_atomic_exchange_ptr(&m_input_display[index], next)) - quiesce_retire(&m_input_display_quiesce, &static_cast(old)->node, - FreeDisplayString); -} - -// NOTE: GPU Thread -std::string MovieManager::GetInputDisplay() -{ - if (!IsMovieActive()) - { - m_controllers = {}; - m_wiimotes = {}; - - const auto& si = m_system.GetSerialInterface(); - for (int i = 0; i < 4; ++i) - { - if (si.GetDeviceType(i) == SerialInterface::SIDEVICE_GC_GBA_EMULATED) - m_controllers[i] = ControllerType::GBA; - else if (si.GetDeviceType(i) != SerialInterface::SIDEVICE_NONE) - m_controllers[i] = ControllerType::GC; - else - m_controllers[i] = ControllerType::None; - m_wiimotes[i] = Config::Get(Config::GetInfoForWiimoteSource(i)) != WiimoteSource::None; - } - } - - std::string input_display; - { - quiesce_enter(&m_input_display_quiesce); - const auto text = [this](int index) -> const std::string& { - static const std::string s_empty; - const auto* display = - static_cast(retro_atomic_load_acquire_ptr(&m_input_display[index])); - return display ? display->text : s_empty; - }; - for (int i = 0; i < 4; ++i) - { - if (IsUsingPad(i)) - input_display += text(i) + '\n'; - } - for (int i = 0; i < 4; ++i) - { - if (IsUsingWiimote(i)) - input_display += text(i + 4) + '\n'; - } - quiesce_exit(&m_input_display_quiesce); - } - return input_display; -} - -// NOTE: GPU Thread -std::string MovieManager::GetRTCDisplay() const -{ - using ExpansionInterface::CEXIIPL; - - const time_t current_time = CEXIIPL::GetEmulatedTime(m_system, CEXIIPL::UNIX_EPOCH); - const tm gm_time = fmt::gmtime(current_time); - - // Use current locale for formatting time, as fmt is locale-agnostic by default. - return fmt::format(std::locale{""}, "Date/Time: {:%c}", gm_time); -} - -// NOTE: GPU Thread -std::string MovieManager::GetRerecords() const -{ - if (IsMovieActive()) - return fmt::format("Rerecords: {}", m_rerecords); - - return "Rerecords: N/A"; -} - -void MovieManager::FrameUpdate() -{ - m_current_frame++; - if (!m_polled) - m_current_lag_count++; - - if (IsRecordingInput()) - { - m_total_frames = m_current_frame; - m_total_lag_count = m_current_lag_count; - } - - m_polled = false; -} - -// called when game is booting up, even if no movie is active, -// but potentially after BeginRecordingInput or PlayInput has been called. -// NOTE: EmuThread -void MovieManager::Init(const BootParameters& boot) -{ - if (std::holds_alternative(boot.parameters)) - m_current_file_name = std::get(boot.parameters).path; - else - m_current_file_name.clear(); - - m_polled = false; - m_save_config = false; - if (IsPlayingInput()) - { - ReadHeader(); - Common::StartDetachedThread(Common::ThreadEntry<&MovieManager::CheckMD5>, this); - 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})", - m_temp_header.GetGameID(), SConfig::GetInstance().GetGameID()); - EndPlayInput(false); - } - } - - if (IsRecordingInput()) - { - GetSettings(); - Common::StartDetachedThread(Common::ThreadEntry<&MovieManager::GetMD5>, this); - m_tick_count_at_last_input = 0; - } - - memset(&m_pad_state, 0, sizeof(m_pad_state)); - - for (int i = 0; i < static_cast(m_input_display.size()); ++i) - SetInputDisplay(i, {}); - - if (!IsMovieActive()) - { - 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 MovieManager::InputUpdate() -{ - m_current_input_count++; - - if (!IsRecordingInput()) - return; - - const auto& core_timing = m_system.GetCoreTiming(); - 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 MovieManager::SetPolledDevice() -{ - m_polled = true; -} - -// NOTE: Host Thread -void MovieManager::SetReadOnly(bool bEnabled) -{ - if (m_read_only != bEnabled) - Core::DisplayMessage(bEnabled ? "Read-only mode." : "Read+Write mode.", 1000); - - m_read_only = bEnabled; -} - -bool MovieManager::IsRecordingInput() const -{ - return (m_play_mode == PlayMode::Recording); -} - -bool MovieManager::IsRecordingInputFromSaveState() const -{ - return m_recording_from_save_state; -} - -bool MovieManager::IsJustStartingRecordingInputFromSaveState() const -{ - return IsRecordingInputFromSaveState() && m_current_frame == 0; -} - -bool MovieManager::IsJustStartingPlayingInputFromSaveState() const -{ - return IsRecordingInputFromSaveState() && m_current_frame == 1 && IsPlayingInput(); -} - -bool MovieManager::IsPlayingInput() const -{ - return (m_play_mode == PlayMode::Playing); -} - -bool MovieManager::IsMovieActive() const -{ - return m_play_mode != PlayMode::None; -} - -bool MovieManager::IsReadOnly() const -{ - return m_read_only; -} - -u64 MovieManager::GetRecordingStartTime() const -{ - return m_recording_start_time; -} - -u64 MovieManager::GetCurrentFrame() const -{ - return m_current_frame; -} - -u64 MovieManager::GetTotalFrames() const -{ - return m_total_frames; -} - -u64 MovieManager::GetCurrentInputCount() const -{ - return m_current_input_count; -} - -u64 MovieManager::GetTotalInputCount() const -{ - return m_total_input_count; -} - -u64 MovieManager::GetCurrentLagCount() const -{ - return m_current_lag_count; -} - -u64 MovieManager::GetTotalLagCount() const -{ - return m_total_lag_count; -} - -void MovieManager::SetClearSave(bool enabled) -{ - m_clear_save = enabled; -} - -void MovieManager::SignalDiscChange(const std::string& new_path) -{ - 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); - constexpr size_t maximum_length = sizeof(DTMHeader::discChange); - if (filename.length() > maximum_length) - { - PanicAlertFmtT("The disc change to \"{0}\" could not be saved in the .dtm file.\n" - "The filename of the disc image must not be longer than 40 characters.", - filename); - } - m_disc_change_filename = filename; - m_has_disc_change = true; - } -} - -void MovieManager::SetReset(bool reset) -{ - m_reset = reset; -} - -bool MovieManager::IsUsingPad(int controller) const -{ - return m_controllers[controller] != ControllerType::None; -} - -bool MovieManager::IsUsingBongo(int controller) const -{ - return ((m_bongos & (1 << controller)) != 0); -} - -bool MovieManager::IsUsingGBA(int controller) const -{ - return m_controllers[controller] == ControllerType::GBA; -} - -bool MovieManager::IsUsingWiimote(int wiimote) const -{ - return m_wiimotes[wiimote]; -} - -bool MovieManager::IsConfigSaved() const -{ - return m_save_config; -} - -bool MovieManager::IsStartingFromClearSave() const -{ - return m_clear_save; -} - -bool MovieManager::IsUsingMemcard(ExpansionInterface::Slot slot) const -{ - switch (slot) - { - case ExpansionInterface::Slot::A: - return (m_memcards & 1) != 0; - case ExpansionInterface::Slot::B: - return (m_memcards & 2) != 0; - default: - return false; - } -} - -bool MovieManager::IsNetPlayRecording() const -{ - return m_net_play; -} - -// NOTE: Host Thread -void MovieManager::ChangePads() -{ - if (!Core::IsRunning(m_system)) - return; - - ControllerTypeArray controllers{}; - - 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_GBA_EMULATED) - controllers[i] = ControllerType::GBA; - else if (SerialInterface::SIDevice_IsGCController(si_device)) - controllers[i] = ControllerType::GC; - else - controllers[i] = ControllerType::None; - } -} - -// NOTE: Host / Emu Threads -void MovieManager::ChangeWiiPads() -{ - WiimoteEnabledArray wiimotes{}; - - for (int i = 0; i < MAX_WIIMOTES; ++i) - { - wiimotes[i] = Config::Get(Config::GetInfoForWiimoteSource(i)) != WiimoteSource::None; - } -} - -// NOTE: Host Thread -bool MovieManager::BeginRecordingInput(const ControllerTypeArray& controllers, - const WiimoteEnabledArray& wiimotes) -{ - if (m_play_mode != PlayMode::None || - (controllers == ControllerTypeArray{} && wiimotes == WiimoteEnabledArray{})) - return false; - - Core::DisplayMessage("Starting movie recording", 2000); - - Core::CPUThreadGuard cpu_thread_guard{m_system}; - - 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 (Config::Get(Config::MAIN_CUSTOM_RTC_ENABLE)) - { - m_recording_start_time = Config::Get(Config::MAIN_CUSTOM_RTC_VALUE); - } - else - { - m_recording_start_time = Common::Timer::GetLocalTimeSinceJan1970(); - } - - 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) - m_bongos |= (1 << i); - } - - if (Core::IsRunning(m_system)) - { - const std::string save_path = File::GetUserPath(D_STATESAVES_IDX) + "dtm.sav"; - if (File::Exists(save_path)) - File::Delete(save_path); - - State::SaveAs(m_system, save_path); - m_recording_from_save_state = true; - - Common::StartDetachedThread(Common::ThreadEntry<&MovieManager::GetMD5>, this); - GetSettings(); - } - - // Wiimotes cause desync issues if they're not reset before launching the game - if (!Core::IsRunning(m_system)) - { - // This will also reset the Wiimotes for GameCube games, but that shouldn't do anything - ::Wiimote::ResetAllWiimotes(); - } - - m_play_mode = PlayMode::Recording; - m_author = Config::Get(Config::MAIN_MOVIE_MOVIE_AUTHOR); - m_temp_input.clear(); - - 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 - // know if we're using a Wii at this point. So, we'll assume a Wii is used here. In practice, - // this shouldn't affect anything for GC (as its only unique setting is language, which will be - // taken from base settings as expected) - static DTMHeader header = {.bWii = true}; - ConfigLoaders::SaveToDTM(&header); - Config::AddLayer(ConfigLoaders::GenerateMovieConfigLoader(&header)); - - if (Core::IsRunning(m_system)) - Core::UpdateWantDeterminism(m_system); - - return true; -} - -static std::string Analog2DToString(u32 x, u32 y, const std::string& prefix, u32 range = 255) -{ - const u32 center = range / 2 + 1; - - if ((x <= 1 || x == center || x >= range) && (y <= 1 || y == center || y >= range)) - { - if (x != center || y != center) - { - if (x != center && y != center) - { - return fmt::format("{}:{},{}", prefix, x < center ? "LEFT" : "RIGHT", - y < center ? "DOWN" : "UP"); - } - - if (x != center) - { - return fmt::format("{}:{}", prefix, x < center ? "LEFT" : "RIGHT"); - } - - return fmt::format("{}:{}", prefix, y < center ? "DOWN" : "UP"); - } - - return ""; - } - - return fmt::format("{}:{},{}", prefix, x, y); -} - -static std::string Analog1DToString(u32 v, const std::string& prefix, u32 range = 255) -{ - if (v == 0) - return ""; - - if (v == range) - return prefix; - - return fmt::format("{}:{}", prefix, v); -} - -// NOTE: CPU Thread -static std::string GenerateInputDisplayString(ControllerState padState, int controllerID) -{ - std::string display_str = fmt::format("P{}:", controllerID + 1); - - if (padState.is_connected) - { - if (padState.A) - display_str += " A"; - if (padState.B) - display_str += " B"; - if (padState.X) - display_str += " X"; - if (padState.Y) - display_str += " Y"; - if (padState.Z) - display_str += " Z"; - if (padState.Start) - display_str += " START"; - - if (padState.DPadUp) - display_str += " UP"; - if (padState.DPadDown) - display_str += " DOWN"; - if (padState.DPadLeft) - display_str += " LEFT"; - if (padState.DPadRight) - display_str += " RIGHT"; - if (padState.reset) - display_str += " RESET"; - - if (padState.TriggerL == 255 || padState.L) - display_str += " L"; - else - display_str += Analog1DToString(padState.TriggerL, " L"); - - if (padState.TriggerR == 255 || padState.R) - display_str += " R"; - else - display_str += Analog1DToString(padState.TriggerR, " R"); - - display_str += Analog2DToString(padState.AnalogStickX, padState.AnalogStickY, " ANA"); - display_str += Analog2DToString(padState.CStickX, padState.CStickY, " C"); - } - else - { - display_str += " DISCONNECTED"; - } - - return display_str; -} - -// NOTE: CPU Thread -static std::string GenerateWiiInputDisplayString(int index, const DesiredWiimoteState& state) -{ - std::string display_str = fmt::format("R{}:", index + 1); - - const auto& buttons = state.buttons; - if (buttons.hex & WiimoteCommon::ButtonData::BUTTON_MASK) - { - if (buttons.left) - display_str += " LEFT"; - if (buttons.right) - display_str += " RIGHT"; - if (buttons.down) - display_str += " DOWN"; - if (buttons.up) - display_str += " UP"; - if (buttons.a) - display_str += " A"; - if (buttons.b) - display_str += " B"; - if (buttons.plus) - display_str += " +"; - if (buttons.minus) - display_str += " -"; - if (buttons.one) - display_str += " 1"; - if (buttons.two) - display_str += " 2"; - if (buttons.home) - display_str += " HOME"; - } - - if (state.acceleration != state.DEFAULT_ACCELERATION) - { - const AccelData& accel_data = state.acceleration; - display_str += - fmt::format(" ACC:{},{},{}", accel_data.value.x, accel_data.value.y, accel_data.value.z); - } - - if (state.camera_points != state.DEFAULT_CAMERA) - { - display_str += " IR:"; - for (auto& point : state.camera_points) - { - if (point.size == 0xff) - display_str += "_,"; - else - display_str += fmt::format("{},{},", point.position.x, point.position.y); - } - display_str.pop_back(); - } - - if (state.extension.data.index() != ExtensionNumber::NONE) - { - const auto ext_visitor = overloaded{ - [&](const Nunchuk::DesiredState& nunchuk) { - const auto bt = nunchuk.GetButtons(); - if (bt & Nunchuk::BUTTON_C) - display_str += " C"; - if (bt & Nunchuk::BUTTON_Z) - display_str += " Z"; - display_str += fmt::format(" N-ACC:{},{},{}", nunchuk.GetAccelX(), nunchuk.GetAccelY(), - nunchuk.GetAccelZ()); - display_str += Analog2DToString(nunchuk.jx, nunchuk.jy, " ANA"); - }, - [&](const Classic::DesiredState& cc) { - const auto bt = cc.GetButtons(); - constexpr std::pair named_buttons[] = { - {Classic::PAD_LEFT, "LEFT"}, {Classic::PAD_RIGHT, "RIGHT"}, - {Classic::PAD_DOWN, "DOWN"}, {Classic::PAD_UP, "UP"}, - {Classic::BUTTON_A, "A"}, {Classic::BUTTON_B, "B"}, - {Classic::BUTTON_X, "X"}, {Classic::BUTTON_Y, "Y"}, - {Classic::BUTTON_ZL, "ZL"}, {Classic::BUTTON_ZR, "ZR"}, - {Classic::BUTTON_PLUS, "+"}, {Classic::BUTTON_MINUS, "-"}, - {Classic::BUTTON_HOME, "HOME"}, - }; - for (auto& [value, name] : named_buttons) - { - if (bt & value) - { - display_str += ' '; - display_str += name; - } - } - constexpr auto trigger_max = (1 << Classic::TRIGGER_BITS) - 1; - display_str += Analog1DToString(cc.GetLeftTrigger().value, " L", trigger_max); - display_str += Analog1DToString(cc.GetRightTrigger().value, " R", trigger_max); - - constexpr auto lstick_max = (1 << Classic::LEFT_STICK_BITS) - 1; - const auto left_stick = cc.GetLeftStick().value; - display_str += Analog2DToString(left_stick.x, left_stick.y, " ANA", lstick_max); - - constexpr auto rstick_max = (1 << Classic::RIGHT_STICK_BITS) - 1; - const auto right_stick = cc.GetRightStick().value; - display_str += Analog2DToString(right_stick.x, right_stick.y, " R-ANA", rstick_max); - }, - [&](const Guitar::DesiredState&) { display_str += " Guitar"; }, - [&](const Drums::DesiredState&) { display_str += " Drums"; }, - [&](const Turntable::DesiredState&) { display_str += " Turntable"; }, - [&](const UDrawTablet::DesiredState&) { display_str += " UDraw"; }, - [&](const DrawsomeTablet::DesiredState&) { display_str += " Drawsome"; }, - [&](const TaTaCon::DesiredState&) { display_str += " TaTaCon"; }, - [&](const Shinkansen::DesiredState&) { display_str += " Shinkansen"; }, - [](const auto& arg) { - static_assert(std::is_same_v>, - "unimplemented extension"); - }, - }; - std::visit(ext_visitor, state.extension.data); - } - - return display_str; -} - -// NOTE: CPU Thread -void MovieManager::CheckPadStatus(const GCPadStatus* PadStatus, int controllerID) -{ - 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); - - 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); - - 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; - - m_pad_state.AnalogStickX = PadStatus->stickX; - m_pad_state.AnalogStickY = PadStatus->stickY; - - m_pad_state.CStickX = PadStatus->substickX; - m_pad_state.CStickY = PadStatus->substickY; - - m_pad_state.is_connected = PadStatus->isConnected; - - m_pad_state.get_origin = (PadStatus->button & PAD_GET_ORIGIN) != 0; - - m_pad_state.disc = m_has_disc_change; - m_has_disc_change = false; - m_pad_state.reset = m_reset; - m_reset = false; - - { - std::string display_str = GenerateInputDisplayString(m_pad_state, controllerID); - - SetInputDisplay(controllerID, std::move(display_str)); - } -} - -// NOTE: CPU Thread -void MovieManager::RecordInput(const GCPadStatus* PadStatus, int controllerID) -{ - if (!IsRecordingInput() || !IsUsingPad(controllerID)) - return; - - CheckPadStatus(PadStatus, controllerID); - - 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 MovieManager::CheckWiimoteStatus(int wiimote, const DesiredWiimoteState& desired_state) -{ - SetPolledDevice(); - - { - std::string display_str = GenerateWiiInputDisplayString(wiimote, desired_state); - - SetInputDisplay(wiimote + 4, std::move(display_str)); - } - - if (IsRecordingInput()) - RecordWiimote(wiimote, SerializeDesiredState(desired_state)); -} - -void MovieManager::RecordWiimote(int wiimote, const SerializedWiimoteState& serialized_state) -{ - if (!IsRecordingInput() || !IsUsingWiimote(wiimote)) - return; - - InputUpdate(); - - const u8 size = serialized_state.length; - m_temp_input.resize(m_current_byte + size + 1); - m_temp_input[m_current_byte++] = size; - std::copy_n(serialized_state.data.data(), size, m_temp_input.data() + m_current_byte); - m_current_byte += size; -} - -// NOTE: EmuThread / Host Thread -void MovieManager::ReadHeader() -{ - for (int i = 0; i < 4; ++i) - { - 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 - m_controllers[i] = ControllerType::None; - m_wiimotes[i] = (m_temp_header.controllers & (1 << (i + 4))) != 0; - } - m_recording_start_time = m_temp_header.recordingStartTime; - if (m_rerecords < m_temp_header.numRerecords) - m_rerecords = m_temp_header.numRerecords; - - if (m_temp_header.bSaveConfig) - { - 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(); - } - - 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 MovieManager::PlayInput(const std::string& movie_path, - std::optional* savestate_path) -{ - if (m_play_mode != PlayMode::None) - return false; - - File::IOFile recording_file(movie_path, "rb"); - if (!recording_file.ReadArray(&m_temp_header, 1)) - return false; - - if (!IsMovieHeader(m_temp_header.filetype)) - { - PanicAlertFmtT("Invalid recording file"); - return false; - } - - ReadHeader(); - - if (AchievementManager::GetInstance().IsHardcoreModeActive()) - return false; - - 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; - - m_play_mode = PlayMode::Playing; - - // Wiimotes cause desync issues if they're not reset before launching the game - ::Wiimote::ResetAllWiimotes(); - - Core::UpdateWantDeterminism(m_system); - - 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 (m_temp_header.bFromSaveState && savestate_path) - { - const std::string savestate_path_temp = movie_path + ".sav"; - if (File::Exists(savestate_path_temp)) - { - *savestate_path = savestate_path_temp; - } - else - { - PanicAlertFmtT("Movie {0} indicates that it starts from a savestate, but {1} doesn't exist. " - "The movie will likely not sync!", - movie_path, savestate_path_temp); - } - - m_recording_from_save_state = true; - - - LoadInput(movie_path); - } - - return true; -} - -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(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 MovieManager::LoadInput(const std::string& movie_path) -{ - File::IOFile t_record; - if (!t_record.Open(movie_path, "r+b")) - { - PanicAlertFmtT("Failed to read {0}", movie_path); - EndPlayInput(false); - return; - } - - t_record.ReadArray(&m_temp_header, 1); - - if (!IsMovieHeader(m_temp_header.filetype)) - { - PanicAlertFmtT("Savestate movie {0} is corrupted, movie recording stopping...", movie_path); - EndPlayInput(false); - return; - } - ReadHeader(); - if (!m_read_only) - { - m_rerecords++; - m_temp_header.numRerecords = m_rerecords; - t_record.Seek(0, File::SeekOrigin::Begin); - t_record.WriteArray(&m_temp_header, 1); - } - - ChangePads(); - if (m_system.IsWii()) - ChangeWiiPads(); - - u64 totalSavedBytes = t_record.GetSize() - 256; - - bool afterEnd = false; - // This can only happen if the user manually deletes data from the dtm. - 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, m_current_byte + 256, m_temp_header.frameCount, m_current_frame); - afterEnd = true; - } - - if (!m_read_only || m_temp_input.empty()) - { - 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; - - m_temp_input.resize(static_cast(totalSavedBytes)); - t_record.ReadBytes(m_temp_input.data(), m_temp_input.size()); - } - else if (m_current_byte > 0) - { - if (m_current_byte > totalSavedBytes) - { - } - 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.", - m_current_byte + 256, m_temp_input.size() + 256, m_current_input_count, - m_total_input_count); - } - else if (m_current_byte > 0 && !m_temp_input.empty()) - { - // verify identical from movie start to the save's current frame - std::vector movInput(m_current_byte); - t_record.ReadArray(movInput.data(), movInput.size()); - - const auto mismatch_result = std::ranges::mismatch(movInput, m_temp_input); - - if (mismatch_result.in1 != movInput.end()) - { - const ptrdiff_t mismatch_index = std::distance(movInput.begin(), mismatch_result.in1); - - // this is a "you did something wrong" alert for the user's benefit. - // we'll try to say what's going on in excruciating detail, otherwise the user might not - // believe us. - if (IsUsingWiimote(0)) - { - const size_t byte_offset = static_cast(mismatch_index) + sizeof(DTMHeader); - - // TODO: more detail - PanicAlertFmtT("Warning: You loaded a save whose movie mismatches on byte {0} ({1:#x}). " - "You should load another save before continuing, or load this state with " - "read-only mode off. Otherwise you'll probably get a desync.", - byte_offset, byte_offset); - - std::ranges::copy(movInput, m_temp_input.begin()); - } - else - { - const ptrdiff_t frame = mismatch_index / sizeof(ControllerState); - ControllerState curPadState; - memcpy(&curPadState, &m_temp_input[frame * sizeof(ControllerState)], - sizeof(ControllerState)); - ControllerState movPadState; - memcpy(&movPadState, &movInput[frame * sizeof(ControllerState)], sizeof(ControllerState)); - PanicAlertFmtT( - "Warning: You loaded a save whose movie mismatches on frame {0}. You should load " - "another save before continuing, or load this state with read-only mode off. " - "Otherwise you'll probably get a desync.\n\n" - "More information: The current movie is {1} frames long and the savestate's movie " - "is {2} frames long.\n\n" - "On frame {3}, the current movie presses:\n" - "Start={4}, A={5}, B={6}, X={7}, Y={8}, Z={9}, DUp={10}, DDown={11}, DLeft={12}, " - "DRight={13}, L={14}, R={15}, LT={16}, RT={17}, AnalogX={18}, AnalogY={19}, CX={20}, " - "CY={21}, Connected={22}" - "\n\n" - "On frame {23}, the savestate's movie presses:\n" - "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, 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(); - - m_save_config = m_temp_header.bSaveConfig; - - if (!afterEnd) - { - if (m_read_only) - { - if (m_play_mode != PlayMode::Playing) - { - m_play_mode = PlayMode::Playing; - Core::UpdateWantDeterminism(m_system); - Core::DisplayMessage("Switched to playback", 2000); - } - } - else - { - if (m_play_mode != PlayMode::Recording) - { - m_play_mode = PlayMode::Recording; - Core::UpdateWantDeterminism(m_system); - Core::DisplayMessage("Switched to recording", 2000); - } - } - } - else - { - EndPlayInput(false); - } -} - -// NOTE: CPU Thread -void MovieManager::CheckInputEnd() -{ - if (m_current_byte >= m_temp_input.size() || - (m_system.GetCoreTiming().GetTicks() > m_total_tick_count && - !IsRecordingInputFromSaveState())) - { - EndPlayInput(!m_read_only); - } -} - -// NOTE: CPU Thread -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) || m_temp_input.empty()) - return; - - if (m_current_byte + sizeof(ControllerState) > m_temp_input.size()) - { - PanicAlertFmtT("Premature movie end in PlayController. {0} + {1} > {2}", m_current_byte, - sizeof(ControllerState), m_temp_input.size()); - EndPlayInput(!m_read_only); - return; - } - - memcpy(&m_pad_state, &m_temp_input[m_current_byte], sizeof(ControllerState)); - m_current_byte += sizeof(ControllerState); - - PadStatus->isConnected = m_pad_state.is_connected; - - PadStatus->triggerLeft = m_pad_state.TriggerL; - PadStatus->triggerRight = m_pad_state.TriggerR; - - PadStatus->stickX = m_pad_state.AnalogStickX; - PadStatus->stickY = m_pad_state.AnalogStickY; - - PadStatus->substickX = m_pad_state.CStickX; - PadStatus->substickY = m_pad_state.CStickY; - - PadStatus->button = 0; - PadStatus->button |= PAD_USE_ORIGIN; - - if (m_pad_state.A) - { - PadStatus->button |= PAD_BUTTON_A; - PadStatus->analogA = 0xFF; - } - if (m_pad_state.B) - { - PadStatus->button |= PAD_BUTTON_B; - PadStatus->analogB = 0xFF; - } - if (m_pad_state.X) - PadStatus->button |= PAD_BUTTON_X; - if (m_pad_state.Y) - PadStatus->button |= PAD_BUTTON_Y; - if (m_pad_state.Z) - PadStatus->button |= PAD_TRIGGER_Z; - if (m_pad_state.Start) - PadStatus->button |= PAD_BUTTON_START; - - if (m_pad_state.DPadUp) - PadStatus->button |= PAD_BUTTON_UP; - if (m_pad_state.DPadDown) - PadStatus->button |= PAD_BUTTON_DOWN; - if (m_pad_state.DPadLeft) - PadStatus->button |= PAD_BUTTON_LEFT; - if (m_pad_state.DPadRight) - PadStatus->button |= PAD_BUTTON_RIGHT; - - if (m_pad_state.L) - PadStatus->button |= PAD_TRIGGER_L; - if (m_pad_state.R) - PadStatus->button |= PAD_TRIGGER_R; - - if (m_pad_state.get_origin) - PadStatus->button |= PAD_GET_ORIGIN; - - if (m_pad_state.disc) - { - const Core::CPUThreadGuard guard(m_system); - if (!m_system.GetDVDInterface().AutoChangeDisc(guard)) - { - m_system.GetCPU().Break(); - PanicAlertFmtT("Change the disc to {0}", m_disc_change_filename); - } - } - - if (m_pad_state.reset) - m_system.GetProcessorInterface().ResetButton_Tap(); - - { - std::string display_str = GenerateInputDisplayString(m_pad_state, controllerID); - - SetInputDisplay(controllerID, std::move(display_str)); - } - - CheckInputEnd(); -} - -// NOTE: CPU Thread -bool MovieManager::PlayWiimote(int wiimote, DesiredWiimoteState* desired_state) -{ - if (!IsPlayingInput() || !IsUsingWiimote(wiimote) || m_temp_input.empty()) - return false; - - if (m_current_byte + sizeof(u8) > m_temp_input.size()) - { - PanicAlertFmtT("Premature movie end in PlayWiimote. {0} + 1 > {1}", m_current_byte, - m_temp_input.size()); - EndPlayInput(!m_read_only); - return false; - } - - SerializedWiimoteState serialized; - serialized.length = m_temp_input[m_current_byte]; - - if (serialized.length > serialized.data.size()) - { - PanicAlertFmtT("Invalid serialized length:{0} in PlayWiimote. byte:{1}", - static_cast(serialized.length), m_current_byte); - EndPlayInput(!m_read_only); - return false; - } - - ++m_current_byte; - if (m_current_byte + serialized.length > m_temp_input.size()) - { - PanicAlertFmtT("Premature movie end in PlayWiimote. {0} + {1} > {2}", m_current_byte, - static_cast(serialized.length), m_temp_input.size()); - EndPlayInput(!m_read_only); - return false; - } - - std::copy_n(m_temp_input.data() + m_current_byte, serialized.length, serialized.data.data()); - if (!WiimoteEmu::DeserializeDesiredState(desired_state, serialized)) - { - PanicAlertFmtT("Aborting playback. Error in DeserializeDesiredState. byte:{0}{1}", - m_current_byte, - (m_controllers == ControllerTypeArray{}) ? - " Try re-creating the recording with all GameCube controllers " - "disabled (in Configure > GameCube > Device Settings)." : - ""); - EndPlayInput(!m_read_only); - return false; - } - - m_current_byte += serialized.length; - ++m_current_input_count; - - CheckInputEnd(); - return true; -} - -// NOTE: Host / EmuThread / CPU Thread -void MovieManager::EndPlayInput(bool cont) -{ - if (cont) - { - // If !IsMovieActive(), changing m_play_mode requires calling UpdateWantDeterminism - ASSERT(IsMovieActive()); - - m_play_mode = PlayMode::Recording; - Core::DisplayMessage("Reached movie end. Resuming recording.", 2000); - } - else if (m_play_mode != PlayMode::None) - { - // We can be called by EmuThread during boot (CPU::State::PowerDown) - auto& cpu = m_system.GetCPU(); - const bool was_running = Core::IsRunning(m_system) && !cpu.IsStepping(); - if (was_running && Config::Get(Config::MAIN_MOVIE_PAUSE_MOVIE)) - cpu.Break(); - m_rerecords = 0; - m_current_byte = 0; - m_play_mode = PlayMode::None; - Core::DisplayMessage("Movie End.", 2000); - 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 - // m_total_frames = s_totalBytes = 0; - // delete tmpInput; - // tmpInput = nullptr; - - Core::QueueHostJob([](Core::System& system) { Core::UpdateWantDeterminism(system); }); - } -} - -// NOTE: Save State + Host Thread -void MovieManager::SaveRecording(const std::string& filename) -{ - File::IOFile save_record(filename, "wb"); - // Create the real header now and write it - DTMHeader header; - memset(&header, 0, sizeof(DTMHeader)); - - header.filetype[0] = 'D'; - header.filetype[1] = 'T'; - header.filetype[2] = 'M'; - header.filetype[3] = 0x1A; - strncpy(header.gameID.data(), SConfig::GetInstance().GetGameID().c_str(), 6); - header.bWii = m_system.IsWii(); - - 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 = 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.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; - // header.audioEmulator; - - save_record.WriteArray(&header, 1); - - bool success = save_record.WriteBytes(m_temp_input.data(), m_temp_input.size()); - - if (success && m_recording_from_save_state) - { - std::string stateFilename = filename + ".sav"; - success = File::CopyRegularFile(File::GetUserPath(D_STATESAVES_IDX) + "dtm.sav", stateFilename); - } - - if (success) - Core::DisplayMessage(fmt::format("DTM {} saved", filename), 2000); - else - Core::DisplayMessage(fmt::format("Failed to save {}", filename), 2000); -} - -// NOTE: EmuThread / Host Thread -void MovieManager::GetSettings() -{ - using ExpansionInterface::EXIDeviceType; - const EXIDeviceType slot_a_type = Config::Get(Config::MAIN_SLOT_A); - const EXIDeviceType slot_b_type = Config::Get(Config::MAIN_SLOT_B); - const bool slot_a_has_raw_memcard = slot_a_type == EXIDeviceType::MemoryCard; - const bool slot_a_has_gci_folder = slot_a_type == EXIDeviceType::MemoryCardFolder; - const bool slot_b_has_raw_memcard = slot_b_type == EXIDeviceType::MemoryCard; - const bool slot_b_has_gci_folder = slot_b_type == EXIDeviceType::MemoryCardFolder; - - m_save_config = true; - m_net_play = false; - if (m_system.IsWii()) - { - u64 title_id = SConfig::GetInstance().GetTitleID(); - m_clear_save = !File::Exists( - Common::GetTitleDataPath(title_id, Common::FromWhichRoot::Session) + "/banner.bin"); - } - else - { - 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 = [this](ExpansionInterface::Slot card_slot) { - const auto [path, migrate] = ExpansionInterface::CEXIMemoryCard::GetGCIFolderPath( - card_slot, ExpansionInterface::AllowMovieFolder::No, *this); - const u64 number_of_saves = File::ScanDirectoryTree(path, false).size; - return number_of_saves > 0; - }; - - 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)); - } - 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; - - m_revision = ConvertGitRevisionToBytes(Common::GetScmRevGitStr()); - - if (!Config::Get(Config::MAIN_DSP_HLE)) - { - std::string irom_file = File::GetUserPath(D_GCUSER_IDX) + DSP_IROM; - std::string coef_file = File::GetUserPath(D_GCUSER_IDX) + DSP_COEF; - - if (!File::Exists(irom_file)) - irom_file = File::GetSysDirectory() + GC_SYS_DIR DIR_SEP DSP_IROM; - if (!File::Exists(coef_file)) - coef_file = File::GetSysDirectory() + GC_SYS_DIR DIR_SEP DSP_COEF; - std::vector irom(DSP::DSP_IROM_SIZE); - File::IOFile file_irom(irom_file, "rb"); - - file_irom.ReadArray(irom.data(), irom.size()); - file_irom.Close(); - for (u16& entry : irom) - entry = Common::swap16(entry); - - std::vector coef(DSP::DSP_COEF_SIZE); - File::IOFile file_coef(coef_file, "rb"); - - file_coef.ReadArray(coef.data(), coef.size()); - file_coef.Close(); - for (u16& entry : coef) - entry = Common::swap16(entry); - m_dsp_irom_hash = - Common::HashAdler32(reinterpret_cast(irom.data()), DSP::DSP_IROM_BYTE_SIZE); - m_dsp_coef_hash = - Common::HashAdler32(reinterpret_cast(coef.data()), DSP::DSP_COEF_BYTE_SIZE); - } - else - { - m_dsp_irom_hash = 0; - m_dsp_coef_hash = 0; - } -} - -// NOTE: Entrypoint for own thread -void MovieManager::CheckMD5() -{ - if (m_current_file_name.empty()) - return; - - // The MD5 hash was introduced in 3.0-846-gca650d4435. - // Before that, these header bytes were set to zero. - if (m_temp_header.md5 == std::array{}) - return; - - Core::DisplayMessage("Verifying checksum...", 2000); - - std::array game_md5; - mbedtls_md_file(mbedtls_md_info_from_type(MBEDTLS_MD_MD5), m_current_file_name.c_str(), - game_md5.data()); - - 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 -void MovieManager::GetMD5() -{ - if (m_current_file_name.empty()) - return; - - Core::DisplayMessage("Calculating checksum of game file...", 2000); - 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 MovieManager::Shutdown() -{ - 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 deleted file mode 100644 index 6af097c819..0000000000 --- a/Source/Core/Core/Movie.h +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include -#include - -#include - -#include "Common/Quiesce.h" -#include "Common/CommonTypes.h" -#include "Core/HW/WiimoteEmu/DesiredWiimoteState.h" - -struct BootParameters; - -struct GCPadStatus; -class PointerWrap; - -namespace Core -{ -class System; -} - -namespace ExpansionInterface -{ -enum class Slot : int; -} - -namespace WiimoteCommon -{ -class DataReportBuilder; -} - -namespace WiimoteEmu -{ -class EncryptionKey; -enum ExtensionNumber : u8; -} // namespace WiimoteEmu - -// Per-(video )Movie actions - -namespace Movie -{ -// Enumerations and structs -enum class ControllerType -{ - None = 0, - GC, - GBA, -}; -using ControllerTypeArray = std::array; -using WiimoteEnabledArray = std::array; - -// GameCube Controller State -#pragma pack(push, 1) -struct ControllerState -{ - bool Start : 1, A : 1, B : 1, X : 1, Y : 1, Z : 1; // Binary buttons, 6 bits - bool DPadUp : 1, DPadDown : 1, // Binary D-Pad buttons, 4 bits - DPadLeft : 1, DPadRight : 1; - bool L : 1, R : 1; // Binary triggers, 2 bits - bool disc : 1; // Checks for disc being changed - bool reset : 1; // Console reset button - bool is_connected : 1; // Should controller be treated as connected - bool get_origin : 1; // Special bit to indicate analog origin reset - - u8 TriggerL, TriggerR; // Triggers, 16 bits - u8 AnalogStickX, AnalogStickY; // Main Stick, 16 bits - u8 CStickX, CStickY; // Sub-Stick, 16 bits -}; -static_assert(sizeof(ControllerState) == 8, "ControllerState should be 8 bytes"); -#pragma pack(pop) - -// When making changes to the DTM format, keep in mind that there are programs other -// than Dolphin that parse DTM files. The format is expected to be relatively stable. -#pragma pack(push, 1) -struct DTMHeader -{ - std::string_view GetGameID() const - { - return {gameID.data(), strnlen(gameID.data(), gameID.size())}; - } - - std::array filetype; // Unique Identifier (always "DTM"0x1A) - - std::array gameID; // The Game ID - bool bWii; // Wii game - - u8 controllers; // Controllers plugged in (from least to most significant, - // the bits are GC controllers 1-4 and Wiimotes 1-4) - - bool - bFromSaveState; // false indicates that the recording started from bootup, true for savestate - u64 frameCount; // Number of frames in the recording - u64 inputCount; // Number of input frames in recording - u64 lagCount; // Number of lag frames in the recording - u64 uniqueID; // (not implemented) A Unique ID comprised of: md5(time + Game ID) - u32 numRerecords; // Number of rerecords/'cuts' of this TAS - std::array author; // Author's name (encoded in UTF-8) - - std::array videoBackend; // UTF-8 representation of the video backend - std::array audioEmulator; // UTF-8 representation of the audio emulator - std::array md5; // MD5 of game iso - - u64 recordingStartTime; // seconds since 1970 that recording started (used for RTC) - - bool bSaveConfig; // Loads the settings below on startup if true - bool bSkipIdle; - bool bDualCore; - bool bProgressive; - bool bDSPHLE; - bool bFastDiscSpeed; - u8 CPUCore; // Uses the values of PowerPC::CPUCore - bool bEFBAccessEnable; - bool bEFBCopyEnable; - bool bSkipEFBCopyToRam; - bool bEFBCopyCacheEnable; - bool bEFBEmulateFormatChanges; - bool bImmediateXFB; - bool bSkipXFBCopyToRam; - u8 memcards; // Memcards inserted (from least to most significant, the bits are slot A and B) - bool bClearSave; // Create a new memory card when playing back a movie if true - u8 bongos; // Bongos plugged in (from least to most significant, the bits are ports 1-4) - bool bSyncGPU; - bool bNetPlay; - bool bPAL60; - u8 language; - u8 reserved3; - bool bFollowBranch; - bool bUseFMA; - u8 GBAControllers; // GBA Controllers plugged in (the bits are ports 1-4) - bool bWidescreen; // true indicates SYSCONF aspect ratio is 16:9, false for 4:3 - u8 countryCode; // SYSCONF country code - std::array reserved; // Padding for any new config options - std::array discChange; // Name of iso file to switch to, for two disc games. - std::array revision; // Git hash - u32 DSPiromHash; - u32 DSPcoefHash; - u64 tickCount; // Number of ticks in the recording - std::array reserved2; // Make heading 256 bytes, just because we can -}; -static_assert(sizeof(DTMHeader) == 256, "DTMHeader should be 256 bytes"); - -#pragma pack(pop) - -enum class PlayMode -{ - None = 0, - Recording, - Playing, -}; - -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(); - - void FrameUpdate(); - void InputUpdate(); - void Init(const BootParameters& boot); - - void SetPolledDevice(); - - bool IsRecordingInput() const; - bool IsRecordingInputFromSaveState() const; - bool IsJustStartingRecordingInputFromSaveState() const; - bool IsJustStartingPlayingInputFromSaveState() const; - bool IsPlayingInput() const; - bool IsMovieActive() const; - bool IsReadOnly() const; - u64 GetRecordingStartTime() const; - - u64 GetCurrentFrame() const; - u64 GetTotalFrames() const; - u64 GetCurrentInputCount() const; - u64 GetTotalInputCount() const; - u64 GetCurrentLagCount() const; - u64 GetTotalLagCount() const; - - void SetClearSave(bool enabled); - void SignalDiscChange(const std::string& new_path); - void SetReset(bool reset); - - bool IsConfigSaved() const; - bool IsStartingFromClearSave() const; - bool IsUsingMemcard(ExpansionInterface::Slot slot) const; - bool IsNetPlayRecording() const; - - bool IsUsingPad(int controller) const; - bool IsUsingWiimote(int wiimote) const; - bool IsUsingBongo(int controller) const; - bool IsUsingGBA(int controller) const; - void ChangePads(); - void ChangeWiiPads(); - - void SetReadOnly(bool bEnabled); - - bool BeginRecordingInput(const ControllerTypeArray& controllers, - const WiimoteEnabledArray& wiimotes); - void RecordInput(const GCPadStatus* PadStatus, int controllerID); - void RecordWiimote(int wiimote, const WiimoteEmu::SerializedWiimoteState& serialized_state); - - 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, WiimoteEmu::DesiredWiimoteState* desired_state); - 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 WiimoteEmu::DesiredWiimoteState& desired_state); - - std::string GetInputDisplay(); - std::string GetRTCDisplay() const; - std::string GetRerecords() const; - -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). - // One string per pad and Wii remote, written by the CPU thread and shown by the GPU thread: - // each is published whole and freed through m_input_display_quiesce. - struct DisplayString - { - quiesce_node_t node; // first: the free callback casts it back - std::string text; - }; - static void FreeDisplayString(quiesce_node_t* node); - void SetInputDisplay(int index, std::string text); - std::array m_input_display{}; - mutable quiesce_t m_input_display_quiesce{}; - - Core::System& m_system; -}; - -} // namespace Movie diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index d358d5711f..212c26967b 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -40,7 +40,6 @@ #include "Core/HW/HW.h" #include "Core/HW/Memmap.h" #include "Core/HW/Wiimote.h" -#include "Core/Movie.h" #include "Core/PowerPC/PowerPC.h" #include "Core/System.h" @@ -210,9 +209,16 @@ void DoState(Core::System& system, PointerWrap& p) return; } - // 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. - system.GetMovie().DoState(p); + // The movie counters, kept for the state layout: no movie is ever recorded or played. + { + u64 movie_counters[4] = {}; + bool movie_polled = false; + u64 movie_tick_count_at_last_input = 0; + for (u64& counter : movie_counters) + p.Do(counter); + p.Do(movie_polled); + p.Do(movie_tick_count_at_last_input); + } p.DoMarker("Movie"); // Begin with video backend, so that it gets a chance to clear its caches and writeback modified @@ -482,11 +488,7 @@ static void CompressAndDumpState(Core::System& system, const CompressAndDumpStat } } - auto& movie = system.GetMovie(); - if ((movie.IsMovieActive()) && !movie.IsJustStartingRecordingInputFromSaveState()) - movie.SaveRecording(dtmname); - else if (!movie.IsMovieActive()) - File::Delete(dtmname); + File::Delete(dtmname); // Move written state to final location. // TODO: This should also be atomic. This is possible on all systems, but needs a special @@ -847,14 +849,10 @@ static void LoadAsFromCore(Core::System& system, std::string filename) s_compress_and_dump_thread.WaitForCompletion(); // Save temp buffer for undo load state - auto& movie = system.GetMovie(); - if (!movie.IsJustStartingRecordingInputFromSaveState()) { SaveToBuffer(system, s_undo_load_buffer); const std::string dtmpath = File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm"; - if (movie.IsMovieActive()) - movie.SaveRecording(dtmpath); - else if (File::Exists(dtmpath)) + if (File::Exists(dtmpath)) File::Delete(dtmpath); } @@ -878,15 +876,6 @@ static void LoadAsFromCore(Core::System& system, std::string filename) if (loaded_successfully) { Core::DisplayMessage(fmt::format("Loaded State from {}", filename), 2000); - if (File::Exists(filename + ".dtm")) - { - movie.LoadInput(filename + ".dtm"); - } - else if (!movie.IsJustStartingRecordingInputFromSaveState() && - !movie.IsJustStartingPlayingInputFromSaveState()) - { - movie.EndPlayInput(false); - } } else { @@ -998,25 +987,7 @@ void UndoLoadState(Core::System& system) return; } - auto& movie = system.GetMovie(); - if (movie.IsMovieActive()) - { - // Note: Only the CPU thread writes to "undo.dtm". - const std::string dtmpath = File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm"; - if (File::Exists(dtmpath)) - { - LoadFromBuffer(system, s_undo_load_buffer); - movie.LoadInput(dtmpath); - } - else - { - PanicAlertFmtT("No undo.dtm found, aborting undo load state to prevent movie desyncs"); - } - } - else - { - LoadFromBuffer(system, s_undo_load_buffer); - } + LoadFromBuffer(system, s_undo_load_buffer); }); } diff --git a/Source/Core/Core/System.cpp b/Source/Core/Core/System.cpp index 7a5926cacc..cb668ce501 100644 --- a/Source/Core/Core/System.cpp +++ b/Source/Core/Core/System.cpp @@ -24,7 +24,6 @@ #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" @@ -54,7 +53,7 @@ struct System::Impl m_serial_interface(system), m_system_timers(system), m_video_interface(system), m_interpreter(system, m_power_pc.GetPPCState(), m_mmu, m_power_pc.GetBranchWatch(), m_power_pc.GetSymbolDB()), - m_jit_interface(system), m_fifo_player(system), m_movie(system) + m_jit_interface(system), m_fifo_player(system) { } @@ -97,7 +96,6 @@ struct System::Impl JitInterface m_jit_interface; VideoCommon::CustomResourceManager m_custom_resource_manager; FifoPlayer m_fifo_player; - Movie::MovieManager m_movie; }; System::System() : m_impl{std::make_unique(*this)} @@ -229,10 +227,6 @@ 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 { diff --git a/Source/Core/Core/System.h b/Source/Core/Core/System.h index 5472bc9b83..cbd593d4c5 100644 --- a/Source/Core/Core/System.h +++ b/Source/Core/Core/System.h @@ -80,10 +80,6 @@ namespace MemoryInterface { class MemoryInterfaceManager; } -namespace Movie -{ -class MovieManager; -} namespace PixelEngine { class PixelEngineManager; @@ -177,7 +173,6 @@ 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 cb444da832..f87b8700e3 100644 --- a/Source/Core/Core/WiiRoot.cpp +++ b/Source/Core/Core/WiiRoot.cpp @@ -24,7 +24,6 @@ #include "Core/IOS/FS/FileSystem.h" #include "Core/IOS/IOS.h" #include "Core/IOS/Uids.h" -#include "Core/Movie.h" #include "Core/SysConf.h" #include "Core/System.h" @@ -85,15 +84,6 @@ static void RestoreFile(const std::string& path_in_nand) DeleteBackupFile(file_name); } -static void CopySave(FS::FileSystem* source, FS::FileSystem* dest, const u64 title_id) -{ - dest->CreateFullPath(IOS::PID_KERNEL, IOS::PID_KERNEL, Common::GetTitleDataPath(title_id) + '/', - 0, {FS::Mode::ReadWrite, FS::Mode::ReadWrite, FS::Mode::ReadWrite}); - const auto source_save = WiiSave::MakeNandStorage(source, title_id); - const auto dest_save = WiiSave::MakeNandStorage(dest, title_id); - WiiSave::Copy(source_save.get(), dest_save.get()); -} - static bool CopyNandFile(FS::FileSystem* source_fs, const std::string& source_file, FS::FileSystem* dest_fs, const std::string& dest_file) { @@ -123,37 +113,6 @@ static bool CopyNandFile(FS::FileSystem* source_fs, const std::string& source_fi return true; } -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()) - { - // 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)); - } - - if (movie.IsMovieActive() && !movie.IsStartingFromClearSave()) - { - auto* source_fs = configured_fs.get(); - INFO_LOG_FMT(CORE, "Wii Save Init: Copying from configured_fs to session_fs."); - - // Copy the current user's save to the Blank NAND - INFO_LOG_FMT(CORE, "Wii Save Init: Copying {0:016x}.", title_id); - CopySave(source_fs, session_fs, title_id); - - // Copy Mii data - if (!CopyNandFile(source_fs, Common::GetMiiDatabasePath(), session_fs, - Common::GetMiiDatabasePath())) - { - WARN_LOG_FMT(CORE, "Failed to copy Mii database to the NAND"); - } - } -} - static void MoveToBackupIfExists(const std::string& path) { if (File::Exists(path)) @@ -313,8 +272,6 @@ void InitializeWiiFileSystemContents( // Generate a SYSCONF with default settings for the temporary Wii NAND. SysConf sysconf{fs}; sysconf.Save(); - - InitializeDeterministicWiiSaves(fs.get(), boot_session_data); } if (save_redirect) diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index 5b39bd74ba..fd89f839ed 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -14,7 +14,6 @@ #include "Core/Config/MainSettings.h" #include "Core/ConfigManager.h" #include "Core/Core.h" -#include "Core/Movie.h" #include "Core/System.h" #include "VideoCommon/AbstractGfx.h"