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.
This commit is contained in:
Techjar 2018-10-02 09:16:12 -04:00
parent f4eb4fab08
commit b06b7e5686
8 changed files with 169 additions and 66 deletions

View File

@ -53,5 +53,7 @@ const ConfigInfo<bool> NETPLAY_STRICT_SETTINGS_SYNC{{System::Main, "NetPlay", "S
false};
const ConfigInfo<bool> NETPLAY_HOST_INPUT_AUTHORITY{{System::Main, "NetPlay", "HostInputAuthority"},
false};
const ConfigInfo<bool> NETPLAY_SYNC_ALL_WII_SAVES{{System::Main, "NetPlay", "SyncAllWiiSaves"},
false};
} // namespace Config

View File

@ -44,5 +44,6 @@ extern const ConfigInfo<bool> NETPLAY_RECORD_INPUTS;
extern const ConfigInfo<bool> NETPLAY_REDUCE_POLLING_RATE;
extern const ConfigInfo<bool> NETPLAY_STRICT_SETTINGS_SYNC;
extern const ConfigInfo<bool> NETPLAY_HOST_INPUT_AUTHORITY;
extern const ConfigInfo<bool> NETPLAY_SYNC_ALL_WII_SAVES;
} // namespace Config

View File

@ -62,6 +62,7 @@ namespace NetPlay
static std::mutex crit_netplay_client;
static NetPlayClient* netplay_client = nullptr;
static std::unique_ptr<IOS::HLE::FS::FileSystem> s_wii_sync_fs;
static std::vector<u64> 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<IOS::HLE::FS::HostFileSystem>(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<u64> 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<IOS::HLE::FS::FileSystem> fs)
const std::vector<u64>& GetWiiSyncTitles()
{
s_wii_sync_fs = std::move(fs);
return s_wii_sync_titles;
}
void ClearWiiSyncFS()
void SetWiiSyncData(std::unique_ptr<IOS::HLE::FS::FileSystem> fs, const std::vector<u64>& 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<std::mutex> lk(crit_netplay_client);
if (netplay_client)
return netplay_client->GetNetSettings().m_SyncAllWiiSaves;
return false;
}
void NetPlay_Enable(NetPlayClient* const np)
{
std::lock_guard<std::mutex> lk(crit_netplay_client);

View File

@ -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<IOS::HLE::FS::FileSystem> fs);
void ClearWiiSyncFS();
const std::vector<u64>& GetWiiSyncTitles();
void SetWiiSyncData(std::unique_ptr<IOS::HLE::FS::FileSystem> fs, const std::vector<u64>& titles);
void ClearWiiSyncData();
void SetSIPollBatching(bool state);
void SendPowerButtonEvent();
bool IsSyncingAllWiiSaves();
} // namespace NetPlay

View File

@ -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<std::pair<u64, WiiSave::StoragePointer>> 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<u64> titles;
sf::Packet pac;
pac << static_cast<MessageId>(NP_MSG_SYNC_SAVE_DATA);
pac << static_cast<MessageId>(SYNC_SAVE_DATA_WII);
pac << static_cast<u32>(saves.size());
if (save->SaveExists())
for (const auto& pair : saves)
{
const std::optional<WiiSave::Header> header = save->ReadHeader();
const std::optional<WiiSave::BkHeader> bk_header = save->ReadBkHeader();
const std::optional<std::vector<WiiSave::Storage::SaveFile>> 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<u8>(file.type) << file.path;
const std::optional<WiiSave::Header> header = save->ReadHeader();
const std::optional<WiiSave::BkHeader> bk_header = save->ReadBkHeader();
const std::optional<std::vector<WiiSave::Storage::SaveFile>> 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<std::vector<u8>>& data = *file.data;
if (!data || !CompressBufferIntoPacket(*data, pac))
return false;
pac << file.mode << file.attributes << static_cast<u8>(file.type) << file.path;
if (file.type == WiiSave::Storage::SaveFile::Type::File)
{
const std::optional<std::vector<u8>>& 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");
}

View File

@ -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);
}
}
}
}

View File

@ -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<void (QSpinBox::*)(int)>(&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)

View File

@ -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;