diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index b34e9bbbe5..22f9b4f017 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -155,6 +155,11 @@ const std::vector& BootSessionData::GetWiiSyncTitles() const return m_wii_sync_titles; } +const std::string& BootSessionData::GetWiiSyncRedirectFolder() const +{ + return m_wii_sync_redirect_folder; +} + void BootSessionData::InvokeWiiSyncCleanup() const { if (m_wii_sync_cleanup) @@ -162,10 +167,12 @@ void BootSessionData::InvokeWiiSyncCleanup() const } void BootSessionData::SetWiiSyncData(std::unique_ptr fs, - std::vector titles, WiiSyncCleanupFunction cleanup) + std::vector titles, std::string redirect_folder, + WiiSyncCleanupFunction cleanup) { m_wii_sync_fs = std::move(fs); m_wii_sync_titles = std::move(titles); + m_wii_sync_redirect_folder = std::move(redirect_folder); m_wii_sync_cleanup = std::move(cleanup); } diff --git a/Source/Core/Core/Boot/Boot.h b/Source/Core/Core/Boot/Boot.h index e27d6a41df..236dddaeca 100644 --- a/Source/Core/Core/Boot/Boot.h +++ b/Source/Core/Core/Boot/Boot.h @@ -66,9 +66,10 @@ public: IOS::HLE::FS::FileSystem* GetWiiSyncFS() const; const std::vector& GetWiiSyncTitles() const; + const std::string& GetWiiSyncRedirectFolder() const; void InvokeWiiSyncCleanup() const; void SetWiiSyncData(std::unique_ptr fs, std::vector titles, - WiiSyncCleanupFunction cleanup); + std::string redirect_folder, WiiSyncCleanupFunction cleanup); private: std::optional m_savestate_path; @@ -76,6 +77,7 @@ private: std::unique_ptr m_wii_sync_fs; std::vector m_wii_sync_titles; + std::string m_wii_sync_redirect_folder; WiiSyncCleanupFunction m_wii_sync_cleanup; }; diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index 66fc47e253..45782dfe21 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -1077,6 +1077,7 @@ void NetPlayClient::OnSyncSaveDataGCI(sf::Packet& packet) void NetPlayClient::OnSyncSaveDataWii(sf::Packet& packet) { const std::string path = File::GetUserPath(D_USER_IDX) + "Wii" GC_MEMCARD_NETPLAY DIR_SEP; + std::string redirect_path = File::GetUserPath(D_USER_IDX) + "Redirect" GC_MEMCARD_NETPLAY DIR_SEP; if (File::Exists(path) && !File::DeleteDirRecursively(path)) { @@ -1084,6 +1085,12 @@ void NetPlayClient::OnSyncSaveDataWii(sf::Packet& packet) SyncSaveDataResponse(false); return; } + if (File::Exists(redirect_path) && !File::DeleteDirRecursively(redirect_path)) + { + PanicAlertFmtT("Failed to reset NetPlay redirect folder. Verify your write permissions."); + SyncSaveDataResponse(false); + return; + } auto temp_fs = std::make_unique(path); std::vector titles; @@ -1190,7 +1197,19 @@ void NetPlayClient::OnSyncSaveDataWii(sf::Packet& packet) } } - SetWiiSyncData(std::move(temp_fs), std::move(titles)); + bool has_redirected_save; + packet >> has_redirected_save; + if (has_redirected_save) + { + if (!DecompressPacketIntoFolder(packet, redirect_path)) + { + PanicAlertFmtT("Failed to write redirected save."); + SyncSaveDataResponse(false); + return; + } + } + + SetWiiSyncData(std::move(temp_fs), std::move(titles), std::move(redirect_path)); SyncSaveDataResponse(true); } @@ -1721,12 +1740,18 @@ bool NetPlayClient::StartGame(const std::string& path) // boot game auto boot_session_data = std::make_unique(); - boot_session_data->SetWiiSyncData(std::move(m_wii_sync_fs), std::move(m_wii_sync_titles), [] { - // on emulation end clean up the Wii save sync directory -- see OnSyncSaveDataWii() - const std::string path = File::GetUserPath(D_USER_IDX) + "Wii" GC_MEMCARD_NETPLAY DIR_SEP; - if (File::Exists(path)) - File::DeleteDirRecursively(path); - }); + boot_session_data->SetWiiSyncData( + std::move(m_wii_sync_fs), std::move(m_wii_sync_titles), std::move(m_wii_sync_redirect_folder), + [] { + // on emulation end clean up the Wii save sync directory -- see OnSyncSaveDataWii() + const std::string path = File::GetUserPath(D_USER_IDX) + "Wii" GC_MEMCARD_NETPLAY DIR_SEP; + if (File::Exists(path)) + File::DeleteDirRecursively(path); + const std::string redirect_path = + File::GetUserPath(D_USER_IDX) + "Redirect" GC_MEMCARD_NETPLAY DIR_SEP; + if (File::Exists(redirect_path)) + File::DeleteDirRecursively(redirect_path); + }); m_dialog->BootGame(path, std::move(boot_session_data)); UpdateDevices(); @@ -2501,10 +2526,11 @@ void NetPlayClient::AdjustPadBufferSize(const unsigned int size) } void NetPlayClient::SetWiiSyncData(std::unique_ptr fs, - std::vector titles) + std::vector titles, std::string redirect_folder) { m_wii_sync_fs = std::move(fs); m_wii_sync_titles = std::move(titles); + m_wii_sync_redirect_folder = std::move(redirect_folder); } SyncIdentifier NetPlayClient::GetSDCardIdentifier() diff --git a/Source/Core/Core/NetPlayClient.h b/Source/Core/Core/NetPlayClient.h index 24cf494634..684d1a2616 100644 --- a/Source/Core/Core/NetPlayClient.h +++ b/Source/Core/Core/NetPlayClient.h @@ -86,7 +86,7 @@ public: virtual void HideChunkedProgressDialog() = 0; virtual void SetChunkedProgress(int pid, u64 progress) = 0; - virtual void SetHostWiiSyncTitles(std::vector titles) = 0; + virtual void SetHostWiiSyncData(std::vector titles, std::string redirect_folder) = 0; }; class Player @@ -157,7 +157,8 @@ public: void AdjustPadBufferSize(unsigned int size); - void SetWiiSyncData(std::unique_ptr fs, std::vector titles); + void SetWiiSyncData(std::unique_ptr fs, std::vector titles, + std::string redirect_folder); static SyncIdentifier GetSDCardIdentifier(); @@ -328,6 +329,7 @@ private: std::unique_ptr m_wii_sync_fs; std::vector m_wii_sync_titles; + std::string m_wii_sync_redirect_folder; }; void NetPlay_Enable(NetPlayClient* const np); diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp index c0548a3384..e25f881be8 100644 --- a/Source/Core/Core/NetPlayServer.cpp +++ b/Source/Core/Core/NetPlayServer.cpp @@ -31,6 +31,7 @@ #include "Common/Version.h" #include "Core/ActionReplay.h" +#include "Core/Boot/Boot.h" #include "Core/Config/GraphicsSettings.h" #include "Core/Config/MainSettings.h" #include "Core/Config/NetplaySettings.h" @@ -60,6 +61,7 @@ #include "Core/SyncIdentifier.h" #include "DiscIO/Enums.h" +#include "DiscIO/RiivolutionPatcher.h" #include "InputCommon/ControllerEmu/ControlGroup/Attachments.h" #include "InputCommon/GCPadStatus.h" @@ -1616,6 +1618,17 @@ bool NetPlayServer::SyncSaveData() save_count++; } + std::optional redirected_save; + if (wii_save && game->GetBlobType() == DiscIO::BlobType::MOD_DESCRIPTOR) + { + auto boot_params = BootParameters::GenerateFromFile(game->GetFilePath()); + if (boot_params) + { + redirected_save = + DiscIO::Riivolution::ExtractSavegameRedirect(boot_params->riivolution_patches); + } + } + for (const auto& config : m_gba_config) { if (config.enabled && config.has_rom) @@ -1818,8 +1831,20 @@ bool NetPlayServer::SyncSaveData() } } + if (redirected_save) + { + pac << true; + if (!CompressFolderIntoPacket(redirected_save->m_target_path, pac)) + return false; + } + else + { + pac << false; // no redirected save + } + // Set titles for host-side loading in WiiRoot - m_dialog->SetHostWiiSyncTitles(std::move(titles)); + m_dialog->SetHostWiiSyncData(std::move(titles), + redirected_save ? redirected_save->m_target_path : ""); SendChunkedToClients(std::move(pac), 1, "Wii Save Synchronization"); } diff --git a/Source/Core/Core/WiiRoot.cpp b/Source/Core/Core/WiiRoot.cpp index 2af5901dfb..149eb50627 100644 --- a/Source/Core/Core/WiiRoot.cpp +++ b/Source/Core/Core/WiiRoot.cpp @@ -35,9 +35,19 @@ namespace Core namespace FS = IOS::HLE::FS; static std::string s_temp_wii_root; +static std::string s_temp_redirect_root; static bool s_wii_root_initialized = false; static std::vector s_nand_redirects; +// When Temp NAND + Redirects are both active, we need to keep track of where each redirect path +// should be copied back to after a successful session finish. +struct TempRedirectPath +{ + std::string real_path; + std::string temp_path; +}; +static std::vector s_temp_nand_redirects; + const std::vector& GetActiveNandRedirects() { return s_nand_redirects; @@ -175,6 +185,28 @@ static void InitializeDeterministicWiiSaves(FS::FileSystem* session_fs, WARN_LOG_FMT(CORE, "Failed to copy Mii database to the NAND"); } } + + const auto& netplay_redirect_folder = boot_session_data.GetWiiSyncRedirectFolder(); + if (!netplay_redirect_folder.empty()) + File::CopyDir(netplay_redirect_folder, s_temp_redirect_root + "/"); + } +} + +static void MoveToBackupIfExists(const std::string& path) +{ + if (File::Exists(path)) + { + const std::string backup_path = path.substr(0, path.size() - 1) + ".backup" DIR_SEP; + WARN_LOG_FMT(IOS_FS, "Temporary directory at {} exists, moving to backup...", path); + + // If backup exists, delete it as we don't want a mess + if (File::Exists(backup_path)) + { + WARN_LOG_FMT(IOS_FS, "Temporary backup directory at {} exists, deleting...", backup_path); + File::DeleteDirRecursively(backup_path); + } + + File::CopyDir(path, backup_path, true); } } @@ -185,24 +217,13 @@ void InitializeWiiRoot(bool use_temporary) if (use_temporary) { s_temp_wii_root = File::GetUserPath(D_USER_IDX) + "WiiSession" DIR_SEP; + s_temp_redirect_root = File::GetUserPath(D_USER_IDX) + "RedirectSession" DIR_SEP; WARN_LOG_FMT(IOS_FS, "Using temporary directory {} for minimal Wii FS", s_temp_wii_root); + WARN_LOG_FMT(IOS_FS, "Using temporary directory {} for redirected saves", s_temp_redirect_root); // If directory exists, make a backup - if (File::Exists(s_temp_wii_root)) - { - const std::string backup_path = - s_temp_wii_root.substr(0, s_temp_wii_root.size() - 1) + ".backup" DIR_SEP; - WARN_LOG_FMT(IOS_FS, "Temporary Wii FS directory exists, moving to backup..."); - - // If backup exists, delete it as we don't want a mess - if (File::Exists(backup_path)) - { - WARN_LOG_FMT(IOS_FS, "Temporary Wii FS backup directory exists, deleting..."); - File::DeleteDirRecursively(backup_path); - } - - File::CopyDir(s_temp_wii_root, backup_path, true); - } + MoveToBackupIfExists(s_temp_wii_root); + MoveToBackupIfExists(s_temp_redirect_root); File::SetUserPath(D_SESSION_WIIROOT_IDX, s_temp_wii_root); } @@ -221,6 +242,9 @@ void ShutdownWiiRoot() { File::DeleteDirRecursively(s_temp_wii_root); s_temp_wii_root.clear(); + File::DeleteDirRecursively(s_temp_redirect_root); + s_temp_redirect_root.clear(); + s_temp_nand_redirects.clear(); } s_nand_redirects.clear(); @@ -312,7 +336,8 @@ void InitializeWiiFileSystemContents( if (!CopySysmenuFilesToFS(fs.get(), File::GetSysDirectory() + WII_USER_DIR, "")) WARN_LOG_FMT(CORE, "Failed to copy initial System Menu files to the NAND"); - if (WiiRootIsTemporary()) + const bool is_temp_nand = WiiRootIsTemporary(); + if (is_temp_nand) { // Generate a SYSCONF with default settings for the temporary Wii NAND. SysConf sysconf{fs}; @@ -320,16 +345,26 @@ void InitializeWiiFileSystemContents( InitializeDeterministicWiiSaves(fs.get(), boot_session_data); } - else if (save_redirect) + + if (save_redirect) { const u64 title_id = SConfig::GetInstance().GetTitleID(); std::string source_path = Common::GetTitleDataPath(title_id); + + if (is_temp_nand) + { + // remember the actual path for copying back on shutdown and redirect to a temp folder instead + s_temp_nand_redirects.emplace_back( + TempRedirectPath{save_redirect->m_target_path, s_temp_redirect_root}); + save_redirect->m_target_path = s_temp_redirect_root; + } + if (!File::IsDirectory(save_redirect->m_target_path)) { File::CreateFullPath(save_redirect->m_target_path + "/"); if (save_redirect->m_clone) { - File::CopyDir(Common::GetTitleDataPath(title_id, Common::FROM_CONFIGURED_ROOT), + File::CopyDir(Common::GetTitleDataPath(title_id, Common::FROM_SESSION_ROOT), save_redirect->m_target_path); } } @@ -347,7 +382,16 @@ void CleanUpWiiFileSystemContents(const BootSessionData& boot_session_data) return; } + // copy back the temp nand redirected files to where they should normally be redirected to + for (const auto& redirect : s_temp_nand_redirects) + File::CopyDir(redirect.temp_path, redirect.real_path + "/", true); + IOS::HLE::EmulationKernel* ios = IOS::HLE::GetIOS(); + + // clear the redirects in the session FS, otherwise the back-copy might grab redirected files + s_nand_redirects.clear(); + ios->GetFS()->SetNandRedirects({}); + const auto configured_fs = FS::MakeFileSystem(FS::Location::Configured); // Copy back Mii data diff --git a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp index bcf5704a29..674fad4e5e 100644 --- a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp +++ b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp @@ -1179,9 +1179,9 @@ void NetPlayDialog::SetChunkedProgress(const int pid, const u64 progress) }); } -void NetPlayDialog::SetHostWiiSyncTitles(std::vector titles) +void NetPlayDialog::SetHostWiiSyncData(std::vector titles, std::string redirect_folder) { auto client = Settings::Instance().GetNetPlayClient(); if (client) - client->SetWiiSyncData(nullptr, std::move(titles)); + client->SetWiiSyncData(nullptr, std::move(titles), std::move(redirect_folder)); } diff --git a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h index fd14cea4e7..03111925bd 100644 --- a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h +++ b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h @@ -95,7 +95,7 @@ public: void HideChunkedProgressDialog() override; void SetChunkedProgress(int pid, u64 progress) override; - void SetHostWiiSyncTitles(std::vector titles) override; + void SetHostWiiSyncData(std::vector titles, std::string redirect_folder) override; signals: void Stop();