diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt
index f93bd67efb..d7327a2a47 100644
--- a/Source/Core/Common/CMakeLists.txt
+++ b/Source/Core/Common/CMakeLists.txt
@@ -36,6 +36,7 @@ add_library(common
QoSSession.cpp
Random.cpp
SDCardUtil.cpp
+ SFMLHelper.cpp
SettingsHandler.cpp
StringUtil.cpp
SymbolDB.cpp
diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj
index 2a38dd721c..9939e0ccd3 100644
--- a/Source/Core/Common/Common.vcxproj
+++ b/Source/Core/Common/Common.vcxproj
@@ -147,6 +147,7 @@
+
@@ -210,6 +211,7 @@
+
diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters
index 76793bd9ae..3737a5a1ff 100644
--- a/Source/Core/Common/Common.vcxproj.filters
+++ b/Source/Core/Common/Common.vcxproj.filters
@@ -68,6 +68,7 @@
+
@@ -299,6 +300,7 @@
+
diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h
index 2255b594f5..3022e7c5b6 100644
--- a/Source/Core/Common/CommonPaths.h
+++ b/Source/Core/Common/CommonPaths.h
@@ -109,6 +109,7 @@
#define GC_SRAM "SRAM.raw"
#define GC_MEMCARDA "MemoryCardA"
#define GC_MEMCARDB "MemoryCardB"
+#define GC_MEMCARD_NETPLAY "NetPlayTemp"
#define WII_STATE "state.dat"
diff --git a/Source/Core/Common/SFMLHelper.cpp b/Source/Core/Common/SFMLHelper.cpp
new file mode 100644
index 0000000000..367bd93a9e
--- /dev/null
+++ b/Source/Core/Common/SFMLHelper.cpp
@@ -0,0 +1,38 @@
+// Copyright 2018 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "Common/SFMLHelper.h"
+
+#include
+
+namespace Common
+{
+// This only exists as a helper for BigEndianValue
+u16 PacketReadU16(sf::Packet& packet)
+{
+ u16 tmp;
+ packet >> tmp;
+ return tmp;
+}
+
+// This only exists as a helper for BigEndianValue
+u32 PacketReadU32(sf::Packet& packet)
+{
+ u32 tmp;
+ packet >> tmp;
+ return tmp;
+}
+
+u64 PacketReadU64(sf::Packet& packet)
+{
+ u32 low, high;
+ packet >> low >> high;
+ return low | (static_cast(high) << 32);
+}
+
+void PacketWriteU64(sf::Packet& packet, const u64 value)
+{
+ packet << static_cast(value) << static_cast(value >> 32);
+}
+} // namespace Common
diff --git a/Source/Core/Common/SFMLHelper.h b/Source/Core/Common/SFMLHelper.h
new file mode 100644
index 0000000000..459af6203b
--- /dev/null
+++ b/Source/Core/Common/SFMLHelper.h
@@ -0,0 +1,23 @@
+// Copyright 2018 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "Common/CommonTypes.h"
+
+namespace sf
+{
+class Packet;
+}
+
+namespace Common
+{
+template
+struct BigEndianValue;
+
+u16 PacketReadU16(sf::Packet& packet);
+u32 PacketReadU32(sf::Packet& packet);
+u64 PacketReadU64(sf::Packet& packet);
+void PacketWriteU64(sf::Packet& packet, u64 value);
+} // namespace Common
diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp
index 8f0c32252b..027c4916a2 100644
--- a/Source/Core/Core/Config/MainSettings.cpp
+++ b/Source/Core/Core/Config/MainSettings.cpp
@@ -36,6 +36,12 @@ const ConfigInfo MAIN_MEMCARD_A_PATH{{System::Main, "Core", "Memcar
const ConfigInfo MAIN_MEMCARD_B_PATH{{System::Main, "Core", "MemcardBPath"}, ""};
const ConfigInfo MAIN_AGP_CART_A_PATH{{System::Main, "Core", "AgpCartAPath"}, ""};
const ConfigInfo MAIN_AGP_CART_B_PATH{{System::Main, "Core", "AgpCartBPath"}, ""};
+const ConfigInfo MAIN_GCI_FOLDER_A_PATH_OVERRIDE{
+ {System::Main, "Core", "GCIFolderAPathOverride"}, ""};
+const ConfigInfo MAIN_GCI_FOLDER_B_PATH_OVERRIDE{
+ {System::Main, "Core", "GCIFolderBPathOverride"}, ""};
+const ConfigInfo MAIN_GCI_FOLDER_CURRENT_GAME_ONLY{
+ {System::Main, "Core", "GCIFolderCurrentGameOnly"}, false};
const ConfigInfo MAIN_SLOT_A{{System::Main, "Core", "SlotA"},
ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER};
const ConfigInfo MAIN_SLOT_B{{System::Main, "Core", "SlotB"},
diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h
index e23e96882d..260c16a1bc 100644
--- a/Source/Core/Core/Config/MainSettings.h
+++ b/Source/Core/Core/Config/MainSettings.h
@@ -37,6 +37,9 @@ extern const ConfigInfo MAIN_MEMCARD_A_PATH;
extern const ConfigInfo MAIN_MEMCARD_B_PATH;
extern const ConfigInfo MAIN_AGP_CART_A_PATH;
extern const ConfigInfo MAIN_AGP_CART_B_PATH;
+extern const ConfigInfo MAIN_GCI_FOLDER_A_PATH_OVERRIDE;
+extern const ConfigInfo MAIN_GCI_FOLDER_B_PATH_OVERRIDE;
+extern const ConfigInfo MAIN_GCI_FOLDER_CURRENT_GAME_ONLY;
extern const ConfigInfo MAIN_SLOT_A;
extern const ConfigInfo MAIN_SLOT_B;
extern const ConfigInfo MAIN_SERIAL_PORT_1;
diff --git a/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.cpp b/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.cpp
index 1d959a286f..4f70d58399 100644
--- a/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.cpp
+++ b/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.cpp
@@ -6,7 +6,9 @@
#include
+#include "Common/CommonPaths.h"
#include "Common/Config/Config.h"
+#include "Common/FileUtil.h"
#include "Core/Config/MainSettings.h"
#include "Core/Config/SYSCONFSettings.h"
#include "Core/NetPlayProto.h"
@@ -39,6 +41,23 @@ public:
layer->Set(Config::SYSCONF_PROGRESSIVE_SCAN, m_settings.m_ProgressiveScan);
layer->Set(Config::SYSCONF_PAL60, m_settings.m_PAL60);
+
+ if (m_settings.m_SyncSaveData)
+ {
+ if (!m_settings.m_IsHosting)
+ {
+ const std::string path = File::GetUserPath(D_GCUSER_IDX) + GC_MEMCARD_NETPLAY DIR_SEP;
+ layer->Set(Config::MAIN_GCI_FOLDER_A_PATH_OVERRIDE, path + "Card A");
+ layer->Set(Config::MAIN_GCI_FOLDER_B_PATH_OVERRIDE, path + "Card B");
+
+ const std::string file = File::GetUserPath(D_GCUSER_IDX) + GC_MEMCARD_NETPLAY + "%c." +
+ m_settings.m_SaveDataRegion + ".raw";
+ layer->Set(Config::MAIN_MEMCARD_A_PATH, StringFromFormat(file.c_str(), 'A'));
+ layer->Set(Config::MAIN_MEMCARD_B_PATH, StringFromFormat(file.c_str(), 'B'));
+ }
+
+ layer->Set(Config::MAIN_GCI_FOLDER_CURRENT_GAME_ONLY, true);
+ }
}
void Save(Config::Layer* layer) override
diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp
index e2747ee415..3eba2b6f6e 100644
--- a/Source/Core/Core/ConfigManager.cpp
+++ b/Source/Core/Core/ConfigManager.cpp
@@ -227,8 +227,6 @@ void SConfig::SaveCoreSettings(IniFile& ini)
core->Set("AudioLatency", iLatency);
core->Set("AudioStretch", m_audio_stretch);
core->Set("AudioStretchMaxLatency", m_audio_stretch_max_latency);
- core->Set("MemcardAPath", m_strMemoryCardA);
- core->Set("MemcardBPath", m_strMemoryCardB);
core->Set("AgpCartAPath", m_strGbaCartA);
core->Set("AgpCartBPath", m_strGbaCartB);
core->Set("SlotA", m_EXIDevice[0]);
@@ -505,8 +503,6 @@ void SConfig::LoadCoreSettings(IniFile& ini)
core->Get("AudioLatency", &iLatency, 20);
core->Get("AudioStretch", &m_audio_stretch, false);
core->Get("AudioStretchMaxLatency", &m_audio_stretch_max_latency, 80);
- core->Get("MemcardAPath", &m_strMemoryCardA);
- core->Get("MemcardBPath", &m_strMemoryCardB);
core->Get("AgpCartAPath", &m_strGbaCartA);
core->Get("AgpCartBPath", &m_strGbaCartB);
core->Get("SlotA", (int*)&m_EXIDevice[0], ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER);
@@ -947,62 +943,12 @@ bool SConfig::SetPathsAndGameMetadata(const BootParameters& boot)
// Set up paths
const std::string region_dir = GetDirectoryForRegion(ToGameCubeRegion(m_region));
- CheckMemcardPath(SConfig::GetInstance().m_strMemoryCardA, region_dir, true);
- CheckMemcardPath(SConfig::GetInstance().m_strMemoryCardB, region_dir, false);
m_strSRAM = File::GetUserPath(F_GCSRAM_IDX);
m_strBootROM = GetBootROMPath(region_dir);
return true;
}
-void SConfig::CheckMemcardPath(std::string& memcardPath, const std::string& gameRegion,
- bool isSlotA)
-{
- std::string ext("." + gameRegion + ".raw");
- if (memcardPath.empty())
- {
- // Use default memcard path if there is no user defined name
- std::string defaultFilename = isSlotA ? GC_MEMCARDA : GC_MEMCARDB;
- memcardPath = File::GetUserPath(D_GCUSER_IDX) + defaultFilename + ext;
- }
- else
- {
- std::string filename = memcardPath;
- std::string region = filename.substr(filename.size() - 7, 3);
- bool hasregion = false;
- hasregion |= region.compare(USA_DIR) == 0;
- hasregion |= region.compare(JAP_DIR) == 0;
- hasregion |= region.compare(EUR_DIR) == 0;
- if (!hasregion)
- {
- // filename doesn't have region in the extension
- if (File::Exists(filename))
- {
- // If the old file exists we are polite and ask if we should copy it
- std::string oldFilename = filename;
- filename.replace(filename.size() - 4, 4, ext);
- if (PanicYesNoT("Memory Card filename in Slot %c is incorrect\n"
- "Region not specified\n\n"
- "Slot %c path was changed to\n"
- "%s\n"
- "Would you like to copy the old file to this new location?\n",
- isSlotA ? 'A' : 'B', isSlotA ? 'A' : 'B', filename.c_str()))
- {
- if (!File::Copy(oldFilename, filename))
- PanicAlertT("Copy failed");
- }
- }
- memcardPath = filename; // Always correct the path!
- }
- else if (region.compare(gameRegion) != 0)
- {
- // filename has region, but it's not == gameRegion
- // Just set the correct filename, the EXI Device will create it if it doesn't exist
- memcardPath = filename.replace(filename.size() - ext.size(), ext.size(), ext);
- }
- }
-}
-
DiscIO::Language SConfig::GetCurrentLanguage(bool wii) const
{
int language_value;
diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h
index c21b22e009..f461f8698d 100644
--- a/Source/Core/Core/ConfigManager.h
+++ b/Source/Core/Core/ConfigManager.h
@@ -212,7 +212,6 @@ struct SConfig
static const char* GetDirectoryForRegion(DiscIO::Region region);
std::string GetBootROMPath(const std::string& region_directory) const;
bool SetPathsAndGameMetadata(const BootParameters& boot);
- void CheckMemcardPath(std::string& memcardPath, const std::string& gameRegion, bool isSlotA);
DiscIO::Language GetCurrentLanguage(bool wii) const;
IniFile LoadDefaultGameIni() const;
@@ -223,8 +222,6 @@ struct SConfig
static IniFile LoadLocalGameIni(const std::string& id, std::optional revision);
static IniFile LoadGameIni(const std::string& id, std::optional revision);
- std::string m_strMemoryCardA;
- std::string m_strMemoryCardB;
std::string m_strGbaCartA;
std::string m_strGbaCartB;
ExpansionInterface::TEXIDevices m_EXIDevice[3];
diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj
index cb9d82584b..0fcf274b9c 100644
--- a/Source/Core/Core/Core.vcxproj
+++ b/Source/Core/Core/Core.vcxproj
@@ -448,6 +448,7 @@
+
diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters
index b60f775010..0e084ca51e 100644
--- a/Source/Core/Core/Core.vcxproj.filters
+++ b/Source/Core/Core/Core.vcxproj.filters
@@ -1272,6 +1272,9 @@
HW %28Flipper/Hollywood%29
+
+ HW %28Flipper/Hollywood%29
+
DSPCore
diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp
index c7a57d2373..5d8eeee0ef 100644
--- a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp
+++ b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp
@@ -13,12 +13,14 @@
#include "Common/ChunkFile.h"
#include "Common/CommonPaths.h"
#include "Common/CommonTypes.h"
+#include "Common/Config/Config.h"
#include "Common/FileUtil.h"
#include "Common/IniFile.h"
#include "Common/Logging/Log.h"
#include "Common/NandPaths.h"
#include "Common/StringUtil.h"
#include "Core/CommonTitles.h"
+#include "Core/Config/MainSettings.h"
#include "Core/ConfigManager.h"
#include "Core/CoreTiming.h"
#include "Core/HW/EXI/EXI.h"
@@ -31,6 +33,7 @@
#include "Core/HW/Sram.h"
#include "Core/HW/SystemTimers.h"
#include "Core/Movie.h"
+#include "Core/NetPlayProto.h"
#include "DiscIO/Enums.h"
namespace ExpansionInterface
@@ -169,24 +172,46 @@ void CEXIMemoryCard::SetupGciFolder(u16 sizeMb)
std::string strDirectoryName = File::GetUserPath(D_GCUSER_IDX);
+ bool migrate = true;
+
if (Movie::IsPlayingInput() && Movie::IsConfigSaved() && Movie::IsUsingMemcard(card_index) &&
Movie::IsStartingFromClearSave())
+ {
strDirectoryName += "Movie" DIR_SEP;
+ migrate = false;
+ }
- strDirectoryName = strDirectoryName + SConfig::GetDirectoryForRegion(region) + DIR_SEP +
- StringFromFormat("Card %c", 'A' + card_index);
+ const std::string path_override =
+ Config::Get(card_index == 0 ? Config::MAIN_GCI_FOLDER_A_PATH_OVERRIDE :
+ Config::MAIN_GCI_FOLDER_B_PATH_OVERRIDE);
+ if (!path_override.empty())
+ {
+ strDirectoryName = path_override;
+ migrate = false;
+ }
+ else
+ {
+ strDirectoryName = strDirectoryName + SConfig::GetDirectoryForRegion(region) + DIR_SEP +
+ StringFromFormat("Card %c", 'A' + card_index);
+ }
const File::FileInfo file_info(strDirectoryName);
- if (!file_info.Exists()) // first use of memcard folder, migrate automatically
+ if (!file_info.Exists())
{
- MigrateFromMemcardFile(strDirectoryName + DIR_SEP, card_index);
+ if (migrate) // first use of memcard folder, migrate automatically
+ MigrateFromMemcardFile(strDirectoryName + DIR_SEP, card_index);
+ else
+ File::CreateFullPath(strDirectoryName + DIR_SEP);
}
else if (!file_info.IsDirectory())
{
if (File::Rename(strDirectoryName, strDirectoryName + ".original"))
{
PanicAlertT("%s was not a directory, moved to *.original", strDirectoryName.c_str());
- MigrateFromMemcardFile(strDirectoryName + DIR_SEP, card_index);
+ if (migrate)
+ MigrateFromMemcardFile(strDirectoryName + DIR_SEP, card_index);
+ else
+ File::CreateFullPath(strDirectoryName + DIR_SEP);
}
else // we tried but the user wants to crash
{
@@ -204,17 +229,21 @@ void CEXIMemoryCard::SetupGciFolder(u16 sizeMb)
void CEXIMemoryCard::SetupRawMemcard(u16 sizeMb)
{
- std::string filename = (card_index == 0) ? SConfig::GetInstance().m_strMemoryCardA :
- SConfig::GetInstance().m_strMemoryCardB;
+ const bool is_slot_a = card_index == 0;
+ std::string filename = is_slot_a ? Config::Get(Config::MAIN_MEMCARD_A_PATH) :
+ Config::Get(Config::MAIN_MEMCARD_B_PATH);
if (Movie::IsPlayingInput() && Movie::IsConfigSaved() && Movie::IsUsingMemcard(card_index) &&
Movie::IsStartingFromClearSave())
- filename = File::GetUserPath(D_GCUSER_IDX) +
- StringFromFormat("Movie%s.raw", (card_index == 0) ? "A" : "B");
+ filename =
+ File::GetUserPath(D_GCUSER_IDX) + StringFromFormat("Movie%s.raw", is_slot_a ? "A" : "B");
+
+ const std::string region_dir =
+ SConfig::GetDirectoryForRegion(SConfig::ToGameCubeRegion(SConfig::GetInstance().m_region));
+ MemoryCard::CheckPath(filename, region_dir, is_slot_a);
if (sizeMb == MemCard251Mb)
- {
filename.insert(filename.find_last_of("."), ".251");
- }
+
memorycard = std::make_unique(filename, card_index, sizeMb);
}
diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp
index 38e24eca42..476aae5e3c 100644
--- a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp
+++ b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp
@@ -16,6 +16,7 @@
#include "Common/Assert.h"
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
+#include "Common/Config/Config.h"
#include "Common/File.h"
#include "Common/FileSearch.h"
#include "Common/FileUtil.h"
@@ -23,8 +24,10 @@
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "Common/Thread.h"
+#include "Core/Config/MainSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
+#include "Core/NetPlayProto.h"
const int NO_INDEX = -1;
static const char* MC_HDR = "MC_SYSTEM_AREA";
@@ -121,6 +124,58 @@ int GCMemcardDirectory::LoadGCI(const std::string& file_name, bool current_game_
return NO_INDEX;
}
+// This is only used by NetPlay but it made sense to put it here to keep the relevant code together
+std::vector GCMemcardDirectory::GetFileNamesForGameID(const std::string& directory,
+ const std::string& game_id)
+{
+ std::vector filenames;
+
+ u32 game_code = 0;
+ if (game_id.length() >= 4 && game_id != "00000000")
+ game_code = BE32(reinterpret_cast(game_id.c_str()));
+
+ std::vector loaded_saves;
+ for (const std::string& file_name : Common::DoFileSearch({directory}, {".gci"}))
+ {
+ File::IOFile gci_file(file_name, "rb");
+ if (!gci_file)
+ continue;
+
+ GCIFile gci;
+ gci.m_filename = file_name;
+ gci.m_dirty = false;
+ if (!gci_file.ReadBytes(&gci.m_gci_header, DENTRY_SIZE))
+ continue;
+
+ const std::string gci_filename = gci.m_gci_header.GCI_FileName();
+ if (std::find(loaded_saves.begin(), loaded_saves.end(), gci_filename) != loaded_saves.end())
+ continue;
+
+ const u16 num_blocks = BE16(gci.m_gci_header.BlockCount);
+ // largest number of free blocks on a memory card
+ // in reality, there are not likely any valid gci files > 251 blocks
+ if (num_blocks > 2043)
+ continue;
+
+ const u32 size = num_blocks * BLOCK_SIZE;
+ const u64 file_size = gci_file.GetSize();
+ if (file_size != size + DENTRY_SIZE)
+ continue;
+
+ // There's technically other available block checks to prevent overfilling the virtual memory
+ // card (see above method), but since we're only loading the saves for one GameID here, we're
+ // definitely not going to run out of space.
+
+ if (game_code == BE32(gci.m_gci_header.Gamecode))
+ {
+ loaded_saves.push_back(gci_filename);
+ filenames.push_back(file_name);
+ }
+ }
+
+ return filenames;
+}
+
GCMemcardDirectory::GCMemcardDirectory(const std::string& directory, int slot, u16 size_mbits,
bool shift_jis, int game_id)
: MemoryCardBase(slot, size_mbits), m_game_id(game_id), m_last_block(-1),
@@ -151,7 +206,8 @@ GCMemcardDirectory::GCMemcardDirectory(const std::string& directory, int slot, u
m_save_directory.c_str());
break;
}
- int index = LoadGCI(gci_file, m_saves.size() > 112);
+ int index = LoadGCI(gci_file, m_saves.size() > 112 ||
+ Config::Get(Config::MAIN_GCI_FOLDER_CURRENT_GAME_ONLY));
if (index != NO_INDEX)
{
m_loaded_saves.push_back(m_saves.at(index).m_gci_header.GCI_FileName());
@@ -687,8 +743,8 @@ void GCIFile::DoState(PointerWrap& p)
void MigrateFromMemcardFile(const std::string& directory_name, int card_index)
{
File::CreateFullPath(directory_name);
- std::string ini_memcard = (card_index == 0) ? SConfig::GetInstance().m_strMemoryCardA :
- SConfig::GetInstance().m_strMemoryCardB;
+ std::string ini_memcard = (card_index == 0) ? Config::Get(Config::MAIN_MEMCARD_A_PATH) :
+ Config::Get(Config::MAIN_MEMCARD_B_PATH);
if (File::Exists(ini_memcard))
{
GCMemcard memcard(ini_memcard.c_str());
diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.h b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.h
index 363c326a8a..ce784b8101 100644
--- a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.h
+++ b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.h
@@ -28,6 +28,8 @@ public:
GCMemcardDirectory(GCMemcardDirectory&&) = default;
GCMemcardDirectory& operator=(GCMemcardDirectory&&) = default;
+ static std::vector GetFileNamesForGameID(const std::string& directory,
+ const std::string& game_id);
void FlushToFile();
void FlushThread();
s32 Read(u32 src_address, s32 length, u8* dest_address) override;
diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcardRaw.cpp b/Source/Core/Core/HW/GCMemcard/GCMemcardRaw.cpp
index 4981aad225..1093b10d13 100644
--- a/Source/Core/Core/HW/GCMemcard/GCMemcardRaw.cpp
+++ b/Source/Core/Core/HW/GCMemcard/GCMemcardRaw.cpp
@@ -12,10 +12,12 @@
#include
#include "Common/ChunkFile.h"
+#include "Common/CommonPaths.h"
#include "Common/CommonTypes.h"
#include "Common/File.h"
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
+#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "Common/Thread.h"
#include "Core/ConfigManager.h"
@@ -71,6 +73,53 @@ MemoryCard::~MemoryCard()
}
}
+void MemoryCard::CheckPath(std::string& memcardPath, const std::string& gameRegion, bool isSlotA)
+{
+ std::string ext("." + gameRegion + ".raw");
+ if (memcardPath.empty())
+ {
+ // Use default memcard path if there is no user defined name
+ std::string defaultFilename = isSlotA ? GC_MEMCARDA : GC_MEMCARDB;
+ memcardPath = File::GetUserPath(D_GCUSER_IDX) + defaultFilename + ext;
+ }
+ else
+ {
+ std::string filename = memcardPath;
+ std::string region = filename.substr(filename.size() - 7, 3);
+ bool hasregion = false;
+ hasregion |= region.compare(USA_DIR) == 0;
+ hasregion |= region.compare(JAP_DIR) == 0;
+ hasregion |= region.compare(EUR_DIR) == 0;
+ if (!hasregion)
+ {
+ // filename doesn't have region in the extension
+ if (File::Exists(filename))
+ {
+ // If the old file exists we are polite and ask if we should copy it
+ std::string oldFilename = filename;
+ filename.replace(filename.size() - 4, 4, ext);
+ if (PanicYesNoT("Memory Card filename in Slot %c is incorrect\n"
+ "Region not specified\n\n"
+ "Slot %c path was changed to\n"
+ "%s\n"
+ "Would you like to copy the old file to this new location?\n",
+ isSlotA ? 'A' : 'B', isSlotA ? 'A' : 'B', filename.c_str()))
+ {
+ if (!File::Copy(oldFilename, filename))
+ PanicAlertT("Copy failed");
+ }
+ }
+ memcardPath = filename; // Always correct the path!
+ }
+ else if (region.compare(gameRegion) != 0)
+ {
+ // filename has region, but it's not == gameRegion
+ // Just set the correct filename, the EXI Device will create it if it doesn't exist
+ memcardPath = filename.replace(filename.size() - ext.size(), ext.size(), ext);
+ }
+ }
+}
+
void MemoryCard::FlushThread()
{
if (!SConfig::GetInstance().bEnableMemcardSdWriting)
diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcardRaw.h b/Source/Core/Core/HW/GCMemcard/GCMemcardRaw.h
index 69d664d4bb..0659e33d3a 100644
--- a/Source/Core/Core/HW/GCMemcard/GCMemcardRaw.h
+++ b/Source/Core/Core/HW/GCMemcard/GCMemcardRaw.h
@@ -19,6 +19,7 @@ class MemoryCard : public MemoryCardBase
public:
MemoryCard(const std::string& filename, int card_index, u16 size_mbits = MemCard2043Mb);
~MemoryCard();
+ static void CheckPath(std::string& memcardPath, const std::string& gameRegion, bool isSlotA);
void FlushThread();
void MakeDirty();
diff --git a/Source/Core/Core/HW/WiiSave.cpp b/Source/Core/Core/HW/WiiSave.cpp
index 67f5340ef8..ddf9eb7f72 100644
--- a/Source/Core/Core/HW/WiiSave.cpp
+++ b/Source/Core/Core/HW/WiiSave.cpp
@@ -32,6 +32,7 @@
#include "Common/StringUtil.h"
#include "Common/Swap.h"
#include "Core/CommonTitles.h"
+#include "Core/HW/WiiSaveStructs.h"
#include "Core/IOS/ES/ES.h"
#include "Core/IOS/FS/FileSystem.h"
#include "Core/IOS/IOS.h"
@@ -48,96 +49,6 @@ constexpr Md5 s_md5_blanker{{0x0E, 0x65, 0x37, 0x81, 0x99, 0xBE, 0x45, 0x17, 0xA
0x45, 0x1A, 0x57, 0x93}};
constexpr u32 s_ng_id = 0x0403AC68;
-enum
-{
- BLOCK_SZ = 0x40,
- ICON_SZ = 0x1200,
- BNR_SZ = 0x60a0,
- FULL_BNR_MIN = 0x72a0, // BNR_SZ + 1*ICON_SZ
- FULL_BNR_MAX = 0xF0A0, // BNR_SZ + 8*ICON_SZ
- BK_LISTED_SZ = 0x70, // Size before rounding to nearest block
- SIG_SZ = 0x40,
- FULL_CERT_SZ = 0x3C0, // SIG_SZ + NG_CERT_SZ + AP_CERT_SZ + 0x80?
-
- BK_HDR_MAGIC = 0x426B0001,
- FILE_HDR_MAGIC = 0x03adf17e
-};
-
-#pragma pack(push, 1)
-struct Header
-{
- Common::BigEndianValue tid;
- Common::BigEndianValue banner_size; // (0x72A0 or 0xF0A0, also seen 0xBAA0)
- u8 permissions;
- u8 unk1; // maybe permissions is a be16
- std::array md5; // md5 of plaintext header with md5 blanker applied
- Common::BigEndianValue unk2;
- u8 banner[FULL_BNR_MAX];
-};
-static_assert(sizeof(Header) == 0xf0c0, "Header has an incorrect size");
-
-struct BkHeader
-{
- Common::BigEndianValue size; // 0x00000070
- // u16 magic; // 'Bk'
- // u16 magic2; // or version (0x0001)
- Common::BigEndianValue magic; // 0x426B0001
- Common::BigEndianValue ngid;
- Common::BigEndianValue number_of_files;
- Common::BigEndianValue size_of_files;
- Common::BigEndianValue unk1;
- Common::BigEndianValue unk2;
- Common::BigEndianValue total_size;
- std::array unk3;
- Common::BigEndianValue tid;
- std::array mac_address;
- std::array padding;
-};
-static_assert(sizeof(BkHeader) == 0x80, "BkHeader has an incorrect size");
-
-struct FileHDR
-{
- Common::BigEndianValue magic; // 0x03adf17e
- Common::BigEndianValue size;
- u8 permissions;
- u8 attrib;
- u8 type; // (1=file, 2=directory)
- std::array name;
- std::array padding;
- std::array iv;
- std::array unk;
-};
-static_assert(sizeof(FileHDR) == 0x80, "FileHDR has an incorrect size");
-#pragma pack(pop)
-
-class Storage
-{
-public:
- struct SaveFile
- {
- enum class Type : u8
- {
- File = 1,
- Directory = 2,
- };
- u8 mode, attributes;
- Type type;
- /// File name relative to the title data directory.
- std::string path;
- // Only valid for regular (i.e. non-directory) files.
- Common::Lazy>> data;
- };
-
- virtual ~Storage() = default;
- virtual bool SaveExists() { return true; }
- virtual std::optional ReadHeader() = 0;
- virtual std::optional ReadBkHeader() = 0;
- virtual std::optional> ReadFiles() = 0;
- virtual bool WriteHeader(const Header& header) = 0;
- virtual bool WriteBkHeader(const BkHeader& bk_header) = 0;
- virtual bool WriteFiles(const std::vector& files) = 0;
-};
-
void StorageDeleter::operator()(Storage* p) const
{
delete p;
diff --git a/Source/Core/Core/HW/WiiSaveStructs.h b/Source/Core/Core/HW/WiiSaveStructs.h
new file mode 100644
index 0000000000..6ef5a38feb
--- /dev/null
+++ b/Source/Core/Core/HW/WiiSaveStructs.h
@@ -0,0 +1,112 @@
+// Copyright 2018 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+// Based off of tachtig/twintig http://git.infradead.org/?p=users/segher/wii.git
+// Copyright 2007,2008 Segher Boessenkool
+// Licensed under the terms of the GNU GPL, version 2
+// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#include "Common/CommonTypes.h"
+#include "Common/Lazy.h"
+#include "Common/Swap.h"
+
+namespace WiiSave
+{
+enum
+{
+ BLOCK_SZ = 0x40,
+ ICON_SZ = 0x1200,
+ BNR_SZ = 0x60a0,
+ FULL_BNR_MIN = 0x72a0, // BNR_SZ + 1*ICON_SZ
+ FULL_BNR_MAX = 0xF0A0, // BNR_SZ + 8*ICON_SZ
+ BK_LISTED_SZ = 0x70, // Size before rounding to nearest block
+ SIG_SZ = 0x40,
+ FULL_CERT_SZ = 0x3C0, // SIG_SZ + NG_CERT_SZ + AP_CERT_SZ + 0x80?
+
+ BK_HDR_MAGIC = 0x426B0001,
+ FILE_HDR_MAGIC = 0x03adf17e
+};
+
+#pragma pack(push, 1)
+struct Header
+{
+ Common::BigEndianValue tid;
+ Common::BigEndianValue banner_size; // (0x72A0 or 0xF0A0, also seen 0xBAA0)
+ u8 permissions;
+ u8 unk1; // maybe permissions is a be16
+ std::array md5; // md5 of plaintext header with md5 blanker applied
+ Common::BigEndianValue unk2;
+ u8 banner[FULL_BNR_MAX];
+};
+static_assert(sizeof(Header) == 0xf0c0, "Header has an incorrect size");
+
+struct BkHeader
+{
+ Common::BigEndianValue size; // 0x00000070
+ // u16 magic; // 'Bk'
+ // u16 magic2; // or version (0x0001)
+ Common::BigEndianValue magic; // 0x426B0001
+ Common::BigEndianValue ngid;
+ Common::BigEndianValue number_of_files;
+ Common::BigEndianValue size_of_files;
+ Common::BigEndianValue unk1;
+ Common::BigEndianValue unk2;
+ Common::BigEndianValue total_size;
+ std::array unk3;
+ Common::BigEndianValue tid;
+ std::array mac_address;
+ std::array padding;
+};
+static_assert(sizeof(BkHeader) == 0x80, "BkHeader has an incorrect size");
+
+struct FileHDR
+{
+ Common::BigEndianValue magic; // 0x03adf17e
+ Common::BigEndianValue size;
+ u8 permissions;
+ u8 attrib;
+ u8 type; // (1=file, 2=directory)
+ std::array name;
+ std::array padding;
+ std::array iv;
+ std::array unk;
+};
+static_assert(sizeof(FileHDR) == 0x80, "FileHDR has an incorrect size");
+#pragma pack(pop)
+
+class Storage
+{
+public:
+ struct SaveFile
+ {
+ enum class Type : u8
+ {
+ File = 1,
+ Directory = 2,
+ };
+ u8 mode, attributes;
+ Type type;
+ /// File name relative to the title data directory.
+ std::string path;
+ // Only valid for regular (i.e. non-directory) files.
+ Common::Lazy>> data;
+ };
+
+ virtual ~Storage() = default;
+ virtual bool SaveExists() { return true; }
+ virtual std::optional ReadHeader() = 0;
+ virtual std::optional ReadBkHeader() = 0;
+ virtual std::optional> ReadFiles() = 0;
+ virtual bool WriteHeader(const Header& header) = 0;
+ virtual bool WriteBkHeader(const BkHeader& bk_header) = 0;
+ virtual bool WriteFiles(const std::vector& files) = 0;
+};
+} // namespace WiiSave
diff --git a/Source/Core/Core/Movie.cpp b/Source/Core/Core/Movie.cpp
index 7a32e04410..d779f0a71d 100644
--- a/Source/Core/Core/Movie.cpp
+++ b/Source/Core/Core/Movie.cpp
@@ -1398,7 +1398,7 @@ void GetSettings()
}
else
{
- s_bClearSave = !File::Exists(SConfig::GetInstance().m_strMemoryCardA);
+ s_bClearSave = !File::Exists(Config::Get(Config::MAIN_MEMCARD_A_PATH));
}
s_memcards |=
(SConfig::GetInstance().m_EXIDevice[0] == ExpansionInterface::EXIDEVICE_MEMORYCARD ||
@@ -1491,4 +1491,4 @@ void Shutdown()
s_currentInputCount = s_totalInputCount = s_totalFrames = s_tickCountAtLastInput = 0;
s_temp_input.clear();
}
-};
+} // namespace Movie
diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp
index 83a0aa0938..d29b931ae0 100644
--- a/Source/Core/Core/NetPlayClient.cpp
+++ b/Source/Core/Core/NetPlayClient.cpp
@@ -13,18 +13,23 @@
#include
#include
#include
+#include
+#include
#include
#include "Common/Assert.h"
#include "Common/CommonPaths.h"
#include "Common/CommonTypes.h"
#include "Common/ENetUtil.h"
+#include "Common/File.h"
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/MD5.h"
#include "Common/MsgHandler.h"
+#include "Common/NandPaths.h"
#include "Common/QoSSession.h"
+#include "Common/SFMLHelper.h"
#include "Common/StringUtil.h"
#include "Common/Timer.h"
#include "Common/Version.h"
@@ -34,12 +39,19 @@
#include "Core/HW/SI/SI.h"
#include "Core/HW/SI/SI_DeviceGCController.h"
#include "Core/HW/Sram.h"
+#include "Core/HW/WiiSave.h"
+#include "Core/HW/WiiSaveStructs.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
+#include "Core/IOS/FS/FileSystem.h"
+#include "Core/IOS/FS/HostBackend/FS.h"
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
+#include "Core/IOS/Uids.h"
#include "Core/Movie.h"
#include "Core/PowerPC/PowerPC.h"
+#include "Core/WiiRoot.h"
#include "InputCommon/GCAdapter.h"
+#include "UICommon/GameFile.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/VideoConfig.h"
@@ -47,6 +59,7 @@ namespace NetPlay
{
static std::mutex crit_netplay_client;
static NetPlayClient* netplay_client = nullptr;
+static std::unique_ptr s_wii_sync_fs;
// called from ---GUI--- thread
NetPlayClient::~NetPlayClient()
@@ -467,10 +480,11 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
packet >> tmp;
m_net_settings.m_EXIDevice[1] = static_cast(tmp);
- u32 time_low, time_high;
- packet >> time_low;
- packet >> time_high;
- g_netplay_initial_rtc = time_low | ((u64)time_high << 32);
+ g_netplay_initial_rtc = Common::PacketReadU64(packet);
+
+ packet >> m_net_settings.m_SyncSaveData;
+ packet >> m_net_settings.m_SaveDataRegion;
+ m_net_settings.m_IsHosting = m_dialog->IsHosting();
}
m_dialog->OnMsgStartGame();
@@ -553,6 +567,194 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
}
break;
+ case NP_MSG_SYNC_SAVE_DATA:
+ {
+ MessageId sub_id;
+ packet >> sub_id;
+
+ switch (sub_id)
+ {
+ case SYNC_SAVE_DATA_NOTIFY:
+ {
+ packet >> m_sync_save_data_count;
+ m_sync_save_data_success_count = 0;
+
+ if (m_sync_save_data_count == 0)
+ SyncSaveDataResponse(true);
+ else
+ m_dialog->AppendChat(GetStringT("Synchronizing save data..."));
+ }
+ break;
+
+ case SYNC_SAVE_DATA_RAW:
+ {
+ if (m_dialog->IsHosting())
+ return 0;
+
+ bool is_slot_a;
+ std::string region;
+ bool mc251;
+ packet >> is_slot_a >> region >> mc251;
+
+ const std::string path = File::GetUserPath(D_GCUSER_IDX) + GC_MEMCARD_NETPLAY +
+ (is_slot_a ? "A." : "B.") + region + (mc251 ? ".251" : "") + ".raw";
+ if (File::Exists(path) && !File::Delete(path))
+ {
+ PanicAlertT("Failed to delete NetPlay memory card. Verify your write permissions.");
+ SyncSaveDataResponse(false);
+ return 0;
+ }
+
+ const bool success = DecompressPacketIntoFile(packet, path);
+ SyncSaveDataResponse(success);
+ }
+ break;
+
+ case SYNC_SAVE_DATA_GCI:
+ {
+ if (m_dialog->IsHosting())
+ return 0;
+
+ bool is_slot_a;
+ u8 file_count;
+ packet >> is_slot_a >> file_count;
+
+ const std::string path = File::GetUserPath(D_GCUSER_IDX) + GC_MEMCARD_NETPLAY DIR_SEP +
+ StringFromFormat("Card %c", is_slot_a ? 'A' : 'B');
+
+ if ((File::Exists(path) && !File::DeleteDirRecursively(path + DIR_SEP)) ||
+ !File::CreateFullPath(path + DIR_SEP))
+ {
+ PanicAlertT("Failed to reset NetPlay GCI folder. Verify your write permissions.");
+ SyncSaveDataResponse(false);
+ return 0;
+ }
+
+ for (u8 i = 0; i < file_count; i++)
+ {
+ std::string file_name;
+ packet >> file_name;
+
+ if (!DecompressPacketIntoFile(packet, path + DIR_SEP + file_name))
+ {
+ SyncSaveDataResponse(false);
+ return 0;
+ }
+ }
+
+ SyncSaveDataResponse(true);
+ }
+ break;
+
+ case SYNC_SAVE_DATA_WII:
+ {
+ if (m_dialog->IsHosting())
+ 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))
+ {
+ PanicAlertT("Failed to reset NetPlay NAND folder. Verify your write permissions.");
+ SyncSaveDataResponse(false);
+ return 0;
+ }
+
+ 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());
+
+ bool exists;
+ packet >> exists;
+ if (exists)
+ {
+ // Header
+ WiiSave::Header header;
+ header.tid = Common::PacketReadU64(packet);
+ header.banner_size = Common::PacketReadU32(packet);
+ packet >> header.permissions;
+ packet >> header.unk1;
+ for (size_t i = 0; i < header.md5.size(); i++)
+ packet >> header.md5[i];
+ header.unk2 = Common::PacketReadU16(packet);
+ for (size_t i = 0; i < header.banner_size; i++)
+ packet >> header.banner[i];
+
+ // BkHeader
+ WiiSave::BkHeader bk_header;
+ bk_header.size = Common::PacketReadU32(packet);
+ bk_header.magic = Common::PacketReadU32(packet);
+ bk_header.ngid = Common::PacketReadU32(packet);
+ bk_header.number_of_files = Common::PacketReadU32(packet);
+ bk_header.size_of_files = Common::PacketReadU32(packet);
+ bk_header.unk1 = Common::PacketReadU32(packet);
+ bk_header.unk2 = Common::PacketReadU32(packet);
+ bk_header.total_size = Common::PacketReadU32(packet);
+ for (size_t i = 0; i < bk_header.unk3.size(); i++)
+ packet >> bk_header.unk3[i];
+ bk_header.tid = Common::PacketReadU64(packet);
+ for (size_t i = 0; i < bk_header.mac_address.size(); i++)
+ packet >> bk_header.mac_address[i];
+
+ // Files
+ std::vector files;
+ for (u32 i = 0; i < bk_header.number_of_files; i++)
+ {
+ WiiSave::Storage::SaveFile file;
+ packet >> file.mode >> file.attributes;
+ {
+ u8 tmp;
+ packet >> tmp;
+ file.type = static_cast(tmp);
+ }
+ packet >> file.path;
+
+ if (file.type == WiiSave::Storage::SaveFile::Type::File)
+ {
+ auto buffer = DecompressPacketIntoBuffer(packet);
+ if (!buffer)
+ {
+ SyncSaveDataResponse(false);
+ return 0;
+ }
+
+ file.data = std::move(*buffer);
+ }
+
+ files.push_back(std::move(file));
+ }
+
+ if (!save->WriteHeader(header) || !save->WriteBkHeader(bk_header) ||
+ !save->WriteFiles(files))
+ {
+ PanicAlertT("Failed to write Wii save.");
+ SyncSaveDataResponse(false);
+ return 0;
+ }
+ }
+
+ SetWiiSyncFS(std::move(temp_fs));
+ SyncSaveDataResponse(true);
+ }
+ break;
+
+ default:
+ PanicAlertT("Unknown SYNC_SAVE_DATA message received with id: %d", sub_id);
+ break;
+ }
+ }
+ break;
+
case NP_MSG_COMPUTE_MD5:
{
std::string file_identifier;
@@ -895,6 +1097,120 @@ bool NetPlayClient::StartGame(const std::string& path)
return true;
}
+void NetPlayClient::SyncSaveDataResponse(const bool success)
+{
+ m_dialog->AppendChat(success ? GetStringT("Data received!") :
+ GetStringT("Error processing data."));
+
+ if (success)
+ {
+ if (++m_sync_save_data_success_count >= m_sync_save_data_count)
+ {
+ sf::Packet response_packet;
+ response_packet << static_cast(NP_MSG_SYNC_SAVE_DATA);
+ response_packet << static_cast(SYNC_SAVE_DATA_SUCCESS);
+
+ Send(response_packet);
+ }
+ }
+ else
+ {
+ sf::Packet response_packet;
+ response_packet << static_cast(NP_MSG_SYNC_SAVE_DATA);
+ response_packet << static_cast(SYNC_SAVE_DATA_FAILURE);
+
+ Send(response_packet);
+ }
+}
+
+bool NetPlayClient::DecompressPacketIntoFile(sf::Packet& packet, const std::string& file_path)
+{
+ u64 file_size = Common::PacketReadU64(packet);
+ ;
+
+ if (file_size == 0)
+ return true;
+
+ File::IOFile file(file_path, "wb");
+ if (!file)
+ {
+ PanicAlertT("Failed to open file \"%s\". Verify your write permissions.", file_path.c_str());
+ return false;
+ }
+
+ std::vector in_buffer(NETPLAY_LZO_OUT_LEN);
+ std::vector out_buffer(NETPLAY_LZO_IN_LEN);
+
+ while (true)
+ {
+ lzo_uint32 cur_len = 0; // number of bytes to read
+ lzo_uint new_len = 0; // number of bytes to write
+
+ packet >> cur_len;
+ if (!cur_len)
+ break; // We reached the end of the data stream
+
+ for (size_t j = 0; j < cur_len; j++)
+ {
+ packet >> in_buffer[j];
+ }
+
+ if (lzo1x_decompress(in_buffer.data(), cur_len, out_buffer.data(), &new_len, nullptr) !=
+ LZO_E_OK)
+ {
+ PanicAlertT("Internal LZO Error - decompression failed");
+ return false;
+ }
+
+ if (!file.WriteBytes(out_buffer.data(), new_len))
+ {
+ PanicAlertT("Error writing file: %s", file_path.c_str());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+std::optional> NetPlayClient::DecompressPacketIntoBuffer(sf::Packet& packet)
+{
+ u64 size = Common::PacketReadU64(packet);
+ ;
+
+ std::vector out_buffer(size);
+
+ if (size == 0)
+ return out_buffer;
+
+ std::vector in_buffer(NETPLAY_LZO_OUT_LEN);
+
+ lzo_uint i = 0;
+ while (true)
+ {
+ lzo_uint32 cur_len = 0; // number of bytes to read
+ lzo_uint new_len = 0; // number of bytes to write
+
+ packet >> cur_len;
+ if (!cur_len)
+ break; // We reached the end of the data stream
+
+ for (size_t j = 0; j < cur_len; j++)
+ {
+ packet >> in_buffer[j];
+ }
+
+ if (lzo1x_decompress(in_buffer.data(), cur_len, &out_buffer[i], &new_len, nullptr) != LZO_E_OK)
+ {
+ PanicAlertT("Internal LZO Error - decompression failed");
+ return {};
+ }
+
+ i += new_len;
+ }
+
+ return out_buffer;
+}
+
// called from ---GUI--- thread
bool NetPlayClient::ChangeGame(const std::string&)
{
@@ -1178,6 +1494,8 @@ bool NetPlayClient::StopGame()
// stop game
m_dialog->StopGame();
+ ClearWiiSyncFS();
+
return true;
}
@@ -1278,8 +1596,7 @@ void NetPlayClient::SendTimeBase()
sf::Packet packet;
packet << static_cast(NP_MSG_TIMEBASE);
- packet << static_cast(timebase);
- packet << static_cast(timebase << 32);
+ Common::PacketWriteU64(packet, timebase);
packet << netplay_client->m_timebase_frame;
netplay_client->SendAsync(std::move(packet));
@@ -1358,6 +1675,26 @@ const NetSettings& GetNetSettings()
return netplay_client->GetNetSettings();
}
+IOS::HLE::FS::FileSystem* GetWiiSyncFS()
+{
+ return s_wii_sync_fs.get();
+}
+
+void SetWiiSyncFS(std::unique_ptr fs)
+{
+ s_wii_sync_fs = std::move(fs);
+}
+
+void ClearWiiSyncFS()
+{
+ // 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;
+ if (File::Exists(path))
+ File::DeleteDirRecursively(path);
+
+ s_wii_sync_fs.reset();
+}
+
void NetPlay_Enable(NetPlayClient* const np)
{
std::lock_guard lk(crit_netplay_client);
diff --git a/Source/Core/Core/NetPlayClient.h b/Source/Core/Core/NetPlayClient.h
index 5306c0f369..ad9d3ee971 100644
--- a/Source/Core/Core/NetPlayClient.h
+++ b/Source/Core/Core/NetPlayClient.h
@@ -7,10 +7,13 @@
#include
#include
#include