From b06b7e5686429f30b46e2c422cd9d988477f6268 Mon Sep 17 00:00:00 2001 From: Techjar Date: Tue, 2 Oct 2018 09:16:12 -0400 Subject: [PATCH] NetPlay: Add full Wii save sync This adds the ability to sync all Wii saves, instead of only the selected game. Useful for cases like launching a game though GeckoOS. --- Source/Core/Core/Config/NetplaySettings.cpp | 2 + Source/Core/Core/Config/NetplaySettings.h | 1 + Source/Core/Core/NetPlayClient.cpp | 61 ++++++---- Source/Core/Core/NetPlayProto.h | 7 +- Source/Core/Core/NetPlayServer.cpp | 113 +++++++++++------- Source/Core/Core/WiiRoot.cpp | 37 +++++- .../Core/DolphinQt/NetPlay/NetPlayDialog.cpp | 13 ++ Source/Core/DolphinQt/NetPlay/NetPlayDialog.h | 1 + 8 files changed, 169 insertions(+), 66 deletions(-) diff --git a/Source/Core/Core/Config/NetplaySettings.cpp b/Source/Core/Core/Config/NetplaySettings.cpp index c3ac30a0b2..6abedb7f82 100644 --- a/Source/Core/Core/Config/NetplaySettings.cpp +++ b/Source/Core/Core/Config/NetplaySettings.cpp @@ -53,5 +53,7 @@ const ConfigInfo NETPLAY_STRICT_SETTINGS_SYNC{{System::Main, "NetPlay", "S false}; const ConfigInfo NETPLAY_HOST_INPUT_AUTHORITY{{System::Main, "NetPlay", "HostInputAuthority"}, false}; +const ConfigInfo NETPLAY_SYNC_ALL_WII_SAVES{{System::Main, "NetPlay", "SyncAllWiiSaves"}, + false}; } // namespace Config diff --git a/Source/Core/Core/Config/NetplaySettings.h b/Source/Core/Core/Config/NetplaySettings.h index 1a7103c590..a7d9cf667d 100644 --- a/Source/Core/Core/Config/NetplaySettings.h +++ b/Source/Core/Core/Config/NetplaySettings.h @@ -44,5 +44,6 @@ extern const ConfigInfo NETPLAY_RECORD_INPUTS; extern const ConfigInfo NETPLAY_REDUCE_POLLING_RATE; extern const ConfigInfo NETPLAY_STRICT_SETTINGS_SYNC; extern const ConfigInfo NETPLAY_HOST_INPUT_AUTHORITY; +extern const ConfigInfo NETPLAY_SYNC_ALL_WII_SAVES; } // namespace Config diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index 3a13aa99c5..adc9a86e9d 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -62,6 +62,7 @@ namespace NetPlay static std::mutex crit_netplay_client; static NetPlayClient* netplay_client = nullptr; static std::unique_ptr s_wii_sync_fs; +static std::vector s_wii_sync_titles; static bool s_si_poll_batching; // called from ---GUI--- thread @@ -608,6 +609,7 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) packet >> m_net_settings.m_SyncSaveData; packet >> m_net_settings.m_SaveDataRegion; packet >> m_net_settings.m_SyncCodes; + packet >> m_net_settings.m_SyncAllWiiSaves; m_net_settings.m_IsHosting = m_local_player->IsHost(); m_net_settings.m_HostInputAuthority = m_host_input_authority; @@ -787,13 +789,6 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) if (m_local_player->IsHost()) return 0; - const auto game = m_dialog->FindGameFile(m_selected_game); - if (game == nullptr) - { - SyncSaveDataResponse(true); // whatever, we won't be booting anyways - return 0; - } - const std::string path = File::GetUserPath(D_USER_IDX) + "Wii" GC_MEMCARD_NETPLAY DIR_SEP; if (File::Exists(path) && !File::DeleteDirRecursively(path)) @@ -804,16 +799,25 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) } auto temp_fs = std::make_unique(path); - temp_fs->CreateDirectory(IOS::PID_KERNEL, IOS::PID_KERNEL, - Common::GetTitleDataPath(game->GetTitleID()), 0, - {IOS::HLE::FS::Mode::ReadWrite, IOS::HLE::FS::Mode::ReadWrite, - IOS::HLE::FS::Mode::ReadWrite}); - auto save = WiiSave::MakeNandStorage(temp_fs.get(), game->GetTitleID()); + std::vector titles; - bool exists; - packet >> exists; - if (exists) + u32 save_count; + packet >> save_count; + for (u32 n = 0; n < save_count; n++) { + u64 title_id = Common::PacketReadU64(packet); + titles.push_back(title_id); + temp_fs->CreateDirectory(IOS::PID_KERNEL, IOS::PID_KERNEL, + Common::GetTitleDataPath(title_id), 0, + {IOS::HLE::FS::Mode::ReadWrite, IOS::HLE::FS::Mode::ReadWrite, + IOS::HLE::FS::Mode::ReadWrite}); + auto save = WiiSave::MakeNandStorage(temp_fs.get(), title_id); + + bool exists; + packet >> exists; + if (!exists) + continue; + // Header WiiSave::Header header; packet >> header.tid; @@ -879,7 +883,7 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) } } - SetWiiSyncFS(std::move(temp_fs)); + SetWiiSyncData(std::move(temp_fs), titles); SyncSaveDataResponse(true); } break; @@ -1925,7 +1929,7 @@ bool NetPlayClient::StopGame() // stop game m_dialog->StopGame(); - ClearWiiSyncFS(); + ClearWiiSyncData(); return true; } @@ -2126,12 +2130,18 @@ IOS::HLE::FS::FileSystem* GetWiiSyncFS() return s_wii_sync_fs.get(); } -void SetWiiSyncFS(std::unique_ptr fs) +const std::vector& GetWiiSyncTitles() { - s_wii_sync_fs = std::move(fs); + return s_wii_sync_titles; } -void ClearWiiSyncFS() +void SetWiiSyncData(std::unique_ptr fs, const std::vector& titles) +{ + s_wii_sync_fs = std::move(fs); + s_wii_sync_titles.insert(s_wii_sync_titles.end(), titles.begin(), titles.end()); +} + +void ClearWiiSyncData() { // We're just assuming it will always be here because it is const std::string path = File::GetUserPath(D_USER_IDX) + "Wii" GC_MEMCARD_NETPLAY DIR_SEP; @@ -2139,6 +2149,7 @@ void ClearWiiSyncFS() File::DeleteDirRecursively(path); s_wii_sync_fs.reset(); + s_wii_sync_titles.clear(); } void SetSIPollBatching(bool state) @@ -2152,6 +2163,16 @@ void SendPowerButtonEvent() netplay_client->SendPowerButtonEvent(); } +bool IsSyncingAllWiiSaves() +{ + std::lock_guard lk(crit_netplay_client); + + if (netplay_client) + return netplay_client->GetNetSettings().m_SyncAllWiiSaves; + + return false; +} + void NetPlay_Enable(NetPlayClient* const np) { std::lock_guard lk(crit_netplay_client); diff --git a/Source/Core/Core/NetPlayProto.h b/Source/Core/Core/NetPlayProto.h index 18711e9a43..2e33069930 100644 --- a/Source/Core/Core/NetPlayProto.h +++ b/Source/Core/Core/NetPlayProto.h @@ -79,6 +79,7 @@ struct NetSettings bool m_SyncSaveData; bool m_SyncCodes; std::string m_SaveDataRegion; + bool m_SyncAllWiiSaves; bool m_IsHosting; bool m_HostInputAuthority; }; @@ -202,8 +203,10 @@ bool IsNetPlayRunning(); // IsNetPlayRunning() must be true before calling this. const NetSettings& GetNetSettings(); IOS::HLE::FS::FileSystem* GetWiiSyncFS(); -void SetWiiSyncFS(std::unique_ptr fs); -void ClearWiiSyncFS(); +const std::vector& GetWiiSyncTitles(); +void SetWiiSyncData(std::unique_ptr fs, const std::vector& titles); +void ClearWiiSyncData(); void SetSIPollBatching(bool state); void SendPowerButtonEvent(); +bool IsSyncingAllWiiSaves(); } // namespace NetPlay diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp index 915eb78781..7e6ee2f863 100644 --- a/Source/Core/Core/NetPlayServer.cpp +++ b/Source/Core/Core/NetPlayServer.cpp @@ -42,7 +42,9 @@ #include "Core/HW/Sram.h" #include "Core/HW/WiiSave.h" #include "Core/HW/WiiSaveStructs.h" +#include "Core/IOS/ES/ES.h" #include "Core/IOS/FS/FileSystem.h" +#include "Core/IOS/IOS.h" #include "Core/NetPlayClient.h" //for NetPlayUI #include "DiscIO/Enums.h" #include "InputCommon/GCPadStatus.h" @@ -1202,6 +1204,7 @@ bool NetPlayServer::StartGame() spac << m_settings.m_SyncSaveData; spac << region; spac << m_settings.m_SyncCodes; + spac << m_settings.m_SyncAllWiiSaves; SendAsyncToClients(std::move(spac)); @@ -1240,7 +1243,8 @@ bool NetPlayServer::SyncSaveData() bool wii_save = false; if (m_settings.m_CopyWiiSave && (game->GetPlatform() == DiscIO::Platform::WiiDisc || - game->GetPlatform() == DiscIO::Platform::WiiWAD)) + game->GetPlatform() == DiscIO::Platform::WiiWAD || + game->GetPlatform() == DiscIO::Platform::ELFOrDOL)) { wii_save = true; save_count++; @@ -1339,58 +1343,87 @@ bool NetPlayServer::SyncSaveData() if (wii_save) { const auto configured_fs = IOS::HLE::FS::MakeFileSystem(IOS::HLE::FS::Location::Configured); - const auto save = WiiSave::MakeNandStorage(configured_fs.get(), game->GetTitleID()); + + std::vector> saves; + if (m_settings.m_SyncAllWiiSaves) + { + IOS::HLE::Kernel ios; + for (const u64 title : ios.GetES()->GetInstalledTitles()) + { + auto save = WiiSave::MakeNandStorage(configured_fs.get(), title); + saves.push_back(std::make_pair(title, std::move(save))); + } + } + else if (game->GetPlatform() == DiscIO::Platform::WiiDisc || + game->GetPlatform() == DiscIO::Platform::WiiWAD) + { + auto save = WiiSave::MakeNandStorage(configured_fs.get(), game->GetTitleID()); + saves.push_back(std::make_pair(game->GetTitleID(), std::move(save))); + } + + std::vector titles; sf::Packet pac; pac << static_cast(NP_MSG_SYNC_SAVE_DATA); pac << static_cast(SYNC_SAVE_DATA_WII); + pac << static_cast(saves.size()); - if (save->SaveExists()) + for (const auto& pair : saves) { - const std::optional header = save->ReadHeader(); - const std::optional bk_header = save->ReadBkHeader(); - const std::optional> files = save->ReadFiles(); - if (!header || !bk_header || !files) - return false; + pac << sf::Uint64{pair.first}; + titles.push_back(pair.first); + const auto& save = pair.second; - pac << true; // save exists - - // Header - pac << sf::Uint64{header->tid}; - pac << header->banner_size << header->permissions << header->unk1; - for (size_t i = 0; i < header->md5.size(); i++) - pac << header->md5[i]; - pac << header->unk2; - for (size_t i = 0; i < header->banner_size; i++) - pac << header->banner[i]; - - // BkHeader - pac << bk_header->size << bk_header->magic << bk_header->ngid << bk_header->number_of_files - << bk_header->size_of_files << bk_header->unk1 << bk_header->unk2 - << bk_header->total_size; - for (size_t i = 0; i < bk_header->unk3.size(); i++) - pac << bk_header->unk3[i]; - pac << sf::Uint64{bk_header->tid}; - for (size_t i = 0; i < bk_header->mac_address.size(); i++) - pac << bk_header->mac_address[i]; - - // Files - for (const WiiSave::Storage::SaveFile& file : *files) + if (save->SaveExists()) { - pac << file.mode << file.attributes << static_cast(file.type) << file.path; + const std::optional header = save->ReadHeader(); + const std::optional bk_header = save->ReadBkHeader(); + const std::optional> files = save->ReadFiles(); + if (!header || !bk_header || !files) + return false; - if (file.type == WiiSave::Storage::SaveFile::Type::File) + pac << true; // save exists + + // Header + pac << sf::Uint64{header->tid}; + pac << header->banner_size << header->permissions << header->unk1; + for (size_t i = 0; i < header->md5.size(); i++) + pac << header->md5[i]; + pac << header->unk2; + for (size_t i = 0; i < header->banner_size; i++) + pac << header->banner[i]; + + // BkHeader + pac << bk_header->size << bk_header->magic << bk_header->ngid << bk_header->number_of_files + << bk_header->size_of_files << bk_header->unk1 << bk_header->unk2 + << bk_header->total_size; + for (size_t i = 0; i < bk_header->unk3.size(); i++) + pac << bk_header->unk3[i]; + pac << sf::Uint64{bk_header->tid}; + for (size_t i = 0; i < bk_header->mac_address.size(); i++) + pac << bk_header->mac_address[i]; + + // Files + for (const WiiSave::Storage::SaveFile& file : *files) { - const std::optional>& data = *file.data; - if (!data || !CompressBufferIntoPacket(*data, pac)) - return false; + pac << file.mode << file.attributes << static_cast(file.type) << file.path; + + if (file.type == WiiSave::Storage::SaveFile::Type::File) + { + const std::optional>& data = *file.data; + if (!data || !CompressBufferIntoPacket(*data, pac)) + return false; + } } } + else + { + pac << false; // save does not exist + } } - else - { - pac << false; // save does not exist - } + + // Set titles for host-side loading in WiiRoot + SetWiiSyncData(nullptr, titles); SendChunkedToClients(std::move(pac), 1, "Wii Save Synchronization"); } diff --git a/Source/Core/Core/WiiRoot.cpp b/Source/Core/Core/WiiRoot.cpp index ef6cea8fe5..1e846eca55 100644 --- a/Source/Core/Core/WiiRoot.cpp +++ b/Source/Core/Core/WiiRoot.cpp @@ -17,6 +17,7 @@ #include "Common/StringUtil.h" #include "Core/ConfigManager.h" #include "Core/HW/WiiSave.h" +#include "Core/IOS/ES/ES.h" #include "Core/IOS/FS/FileSystem.h" #include "Core/IOS/IOS.h" #include "Core/IOS/Uids.h" @@ -30,6 +31,16 @@ namespace FS = IOS::HLE::FS; static std::string s_temp_wii_root; +static void CopySave(FS::FileSystem* source, FS::FileSystem* dest, const u64 title_id) +{ + dest->CreateDirectory(IOS::PID_KERNEL, IOS::PID_KERNEL, Common::GetTitleDataPath(title_id), 0, + {IOS::HLE::FS::Mode::ReadWrite, IOS::HLE::FS::Mode::ReadWrite, + IOS::HLE::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 void InitializeDeterministicWiiSaves(FS::FileSystem* session_fs) { const u64 title_id = SConfig::GetInstance().GetTitleID(); @@ -53,10 +64,28 @@ static void InitializeDeterministicWiiSaves(FS::FileSystem* session_fs) { // Copy the current user's save to the Blank NAND auto* sync_fs = NetPlay::GetWiiSyncFS(); - const auto user_save = - WiiSave::MakeNandStorage(sync_fs ? sync_fs : configured_fs.get(), title_id); - const auto session_save = WiiSave::MakeNandStorage(session_fs, title_id); - WiiSave::Copy(user_save.get(), session_save.get()); + auto& sync_titles = NetPlay::GetWiiSyncTitles(); + if (sync_fs) + { + for (const u64 title : sync_titles) + { + CopySave(sync_fs, session_fs, title); + } + } + else + { + if (NetPlay::IsSyncingAllWiiSaves()) + { + for (const u64 title : sync_titles) + { + CopySave(configured_fs.get(), session_fs, title); + } + } + else + { + CopySave(configured_fs.get(), session_fs, title_id); + } + } } } diff --git a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp index 9780773b8a..fb79123d88 100644 --- a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp +++ b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.cpp @@ -85,6 +85,7 @@ NetPlayDialog::NetPlayDialog(QWidget* parent) const bool reduce_polling_rate = Config::Get(Config::NETPLAY_REDUCE_POLLING_RATE); const bool strict_settings_sync = Config::Get(Config::NETPLAY_STRICT_SETTINGS_SYNC); const bool host_input_authority = Config::Get(Config::NETPLAY_HOST_INPUT_AUTHORITY); + const bool sync_all_wii_saves = Config::Get(Config::NETPLAY_SYNC_ALL_WII_SAVES); m_buffer_size_box->setValue(buffer_size); m_save_sd_box->setChecked(write_save_sdcard_data); @@ -95,6 +96,7 @@ NetPlayDialog::NetPlayDialog(QWidget* parent) m_reduce_polling_rate_box->setChecked(reduce_polling_rate); m_strict_settings_sync_box->setChecked(strict_settings_sync); m_host_input_authority_box->setChecked(host_input_authority); + m_sync_all_wii_saves_box->setChecked(sync_all_wii_saves); ConnectWidgets(); @@ -127,6 +129,7 @@ void NetPlayDialog::CreateMainLayout() m_strict_settings_sync_box = new QCheckBox(tr("Strict Settings Sync")); m_host_input_authority_box = new QCheckBox(tr("Host Input Authority")); m_sync_codes_box = new QCheckBox(tr("Sync Codes")); + m_sync_all_wii_saves_box = new QCheckBox(tr("Sync All Wii Saves")); m_buffer_label = new QLabel(tr("Buffer:")); m_quit_button = new QPushButton(tr("Quit")); m_splitter = new QSplitter(Qt::Horizontal); @@ -200,6 +203,7 @@ void NetPlayDialog::CreateMainLayout() options_boxes->addWidget(m_save_sd_box); options_boxes->addWidget(m_load_wii_box); options_boxes->addWidget(m_sync_save_data_box); + options_boxes->addWidget(m_sync_all_wii_saves_box); options_boxes->addWidget(m_sync_codes_box); options_boxes->addWidget(m_record_input_box); options_boxes->addWidget(m_reduce_polling_rate_box); @@ -339,6 +343,9 @@ void NetPlayDialog::ConnectWidgets() } }); + connect(m_sync_save_data_box, &QCheckBox::stateChanged, this, + [this](bool checked) { m_sync_all_wii_saves_box->setEnabled(checked); }); + // SaveSettings() - Save Hosting-Dialog Settings connect(m_buffer_size_box, static_cast(&QSpinBox::valueChanged), this, @@ -351,6 +358,7 @@ void NetPlayDialog::ConnectWidgets() connect(m_reduce_polling_rate_box, &QCheckBox::stateChanged, this, &NetPlayDialog::SaveSettings); connect(m_strict_settings_sync_box, &QCheckBox::stateChanged, this, &NetPlayDialog::SaveSettings); connect(m_host_input_authority_box, &QCheckBox::stateChanged, this, &NetPlayDialog::SaveSettings); + connect(m_sync_all_wii_saves_box, &QCheckBox::stateChanged, this, &NetPlayDialog::SaveSettings); } void NetPlayDialog::OnChat() @@ -464,6 +472,8 @@ void NetPlayDialog::OnStart() settings.m_StrictSettingsSync = m_strict_settings_sync_box->isChecked(); settings.m_SyncSaveData = m_sync_save_data_box->isChecked(); settings.m_SyncCodes = m_sync_codes_box->isChecked(); + settings.m_SyncAllWiiSaves = + m_sync_all_wii_saves_box->isChecked() && m_sync_save_data_box->isChecked(); // Unload GameINI to restore things to normal Config::RemoveLayer(Config::LayerType::GlobalGame); @@ -517,6 +527,7 @@ void NetPlayDialog::show(std::string nickname, bool use_traversal) m_reduce_polling_rate_box->setHidden(!is_hosting); m_strict_settings_sync_box->setHidden(!is_hosting); m_host_input_authority_box->setHidden(!is_hosting); + m_sync_all_wii_saves_box->setHidden(!is_hosting); m_kick_button->setHidden(!is_hosting); m_assign_ports_button->setHidden(!is_hosting); m_md5_button->setHidden(!is_hosting); @@ -817,6 +828,7 @@ void NetPlayDialog::SetOptionsEnabled(bool enabled) m_reduce_polling_rate_box->setEnabled(enabled); m_strict_settings_sync_box->setEnabled(enabled); m_host_input_authority_box->setEnabled(enabled); + m_sync_all_wii_saves_box->setEnabled(enabled && m_sync_save_data_box->isChecked()); } m_record_input_box->setEnabled(enabled); @@ -1011,6 +1023,7 @@ void NetPlayDialog::SaveSettings() Config::SetBase(Config::NETPLAY_REDUCE_POLLING_RATE, m_reduce_polling_rate_box->isChecked()); Config::SetBase(Config::NETPLAY_STRICT_SETTINGS_SYNC, m_strict_settings_sync_box->isChecked()); Config::SetBase(Config::NETPLAY_HOST_INPUT_AUTHORITY, m_host_input_authority_box->isChecked()); + Config::SetBase(Config::NETPLAY_SYNC_ALL_WII_SAVES, m_sync_all_wii_saves_box->isChecked()); } void NetPlayDialog::ShowMD5Dialog(const std::string& file_identifier) diff --git a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h index b3a17908c7..3cad57c795 100644 --- a/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h +++ b/Source/Core/DolphinQt/NetPlay/NetPlayDialog.h @@ -123,6 +123,7 @@ private: QCheckBox* m_reduce_polling_rate_box; QCheckBox* m_strict_settings_sync_box; QCheckBox* m_host_input_authority_box; + QCheckBox* m_sync_all_wii_saves_box; QPushButton* m_quit_button; QSplitter* m_splitter;