dolphin/Source/Core/Core/NetPlayCommon.cpp

301 lines
6.8 KiB
C++

// Copyright 2021 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Core/NetPlayCommon.h"
#include <algorithm>
#include <fmt/format.h>
#include <lzo/lzo1x.h>
#include "Common/FileUtil.h"
#include "Common/IOFile.h"
#include "Common/MsgHandler.h"
#include "Common/SFMLHelper.h"
namespace NetPlay
{
constexpr u32 LZO_IN_LEN = 1024 * 64;
constexpr u32 LZO_OUT_LEN = LZO_IN_LEN + (LZO_IN_LEN / 16) + 64 + 3;
bool CompressFileIntoPacket(const std::string& file_path, sf::Packet& packet)
{
File::IOFile file(file_path, "rb");
if (!file)
{
PanicAlertFmtT("Failed to open file \"{0}\".", file_path);
return false;
}
const sf::Uint64 size = file.GetSize();
packet << size;
if (size == 0)
return true;
std::vector<u8> in_buffer(LZO_IN_LEN);
std::vector<u8> out_buffer(LZO_OUT_LEN);
std::vector<u8> wrkmem(LZO1X_1_MEM_COMPRESS);
lzo_uint i = 0;
while (true)
{
lzo_uint32 cur_len = 0; // number of bytes to read
lzo_uint out_len = 0; // number of bytes to write
if ((i + LZO_IN_LEN) >= size)
{
cur_len = static_cast<lzo_uint32>(size - i);
}
else
{
cur_len = LZO_IN_LEN;
}
if (cur_len <= 0)
break; // EOF
if (!file.ReadBytes(in_buffer.data(), cur_len))
{
PanicAlertFmtT("Error reading file: {0}", file_path.c_str());
return false;
}
if (lzo1x_1_compress(in_buffer.data(), cur_len, out_buffer.data(), &out_len, wrkmem.data()) !=
LZO_E_OK)
{
PanicAlertFmtT("Internal LZO Error - compression failed");
return false;
}
// The size of the data to write is 'out_len'
packet << static_cast<u32>(out_len);
for (size_t j = 0; j < out_len; j++)
{
packet << out_buffer[j];
}
if (cur_len != LZO_IN_LEN)
break;
i += cur_len;
}
// Mark end of data
packet << static_cast<u32>(0);
return true;
}
static bool CompressFolderIntoPacketInternal(const File::FSTEntry& folder, sf::Packet& packet)
{
const sf::Uint64 size = folder.children.size();
packet << size;
for (const auto& child : folder.children)
{
const bool is_folder = child.isDirectory;
packet << child.virtualName;
packet << is_folder;
const bool success = is_folder ? CompressFolderIntoPacketInternal(child, packet) :
CompressFileIntoPacket(child.physicalName, packet);
if (!success)
return false;
}
return true;
}
bool CompressFolderIntoPacket(const std::string& folder_path, sf::Packet& packet)
{
if (!File::IsDirectory(folder_path))
{
packet << false;
return true;
}
packet << true;
return CompressFolderIntoPacketInternal(File::ScanDirectoryTree(folder_path, true), packet);
}
bool CompressBufferIntoPacket(const std::vector<u8>& in_buffer, sf::Packet& packet)
{
const sf::Uint64 size = in_buffer.size();
packet << size;
if (size == 0)
return true;
std::vector<u8> out_buffer(LZO_OUT_LEN);
std::vector<u8> wrkmem(LZO1X_1_MEM_COMPRESS);
lzo_uint i = 0;
while (true)
{
lzo_uint32 cur_len = 0; // number of bytes to read
lzo_uint out_len = 0; // number of bytes to write
if ((i + LZO_IN_LEN) >= size)
{
cur_len = static_cast<lzo_uint32>(size - i);
}
else
{
cur_len = LZO_IN_LEN;
}
if (cur_len <= 0)
break; // end of buffer
if (lzo1x_1_compress(&in_buffer[i], cur_len, out_buffer.data(), &out_len, wrkmem.data()) !=
LZO_E_OK)
{
PanicAlertFmtT("Internal LZO Error - compression failed");
return false;
}
// The size of the data to write is 'out_len'
packet << static_cast<u32>(out_len);
for (size_t j = 0; j < out_len; j++)
{
packet << out_buffer[j];
}
if (cur_len != LZO_IN_LEN)
break;
i += cur_len;
}
// Mark end of data
packet << static_cast<u32>(0);
return true;
}
bool 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)
{
PanicAlertFmtT("Failed to open file \"{0}\". Verify your write permissions.", file_path);
return false;
}
std::vector<u8> in_buffer(LZO_OUT_LEN);
std::vector<u8> out_buffer(LZO_IN_LEN);
while (true)
{
u32 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)
{
PanicAlertFmtT("Internal LZO Error - decompression failed");
return false;
}
if (!file.WriteBytes(out_buffer.data(), new_len))
{
PanicAlertFmtT("Error writing file: {0}", file_path);
return false;
}
}
return true;
}
static bool DecompressPacketIntoFolderInternal(sf::Packet& packet, const std::string& folder_path)
{
if (!File::CreateFullPath(folder_path + "/"))
return false;
sf::Uint64 size;
packet >> size;
for (size_t i = 0; i < size; ++i)
{
std::string name;
packet >> name;
if (name.find('/') != std::string::npos)
return false;
#ifdef _WIN32
if (name.find('\\') != std::string::npos)
return false;
#endif
if (std::all_of(name.begin(), name.end(), [](char c) { return c == '.'; }))
return false;
bool is_folder;
packet >> is_folder;
std::string path = fmt::format("{}/{}", folder_path, name);
const bool success = is_folder ? DecompressPacketIntoFolderInternal(packet, path) :
DecompressPacketIntoFile(packet, path);
if (!success)
return false;
}
return true;
}
bool DecompressPacketIntoFolder(sf::Packet& packet, const std::string& folder_path)
{
bool folder_existed;
packet >> folder_existed;
if (!folder_existed)
return true;
return DecompressPacketIntoFolderInternal(packet, folder_path);
}
std::optional<std::vector<u8>> DecompressPacketIntoBuffer(sf::Packet& packet)
{
u64 size = Common::PacketReadU64(packet);
std::vector<u8> out_buffer(size);
if (size == 0)
return out_buffer;
std::vector<u8> in_buffer(LZO_OUT_LEN);
lzo_uint i = 0;
while (true)
{
u32 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)
{
PanicAlertFmtT("Internal LZO Error - decompression failed");
return {};
}
i += new_len;
}
return out_buffer;
}
} // namespace NetPlay