mirror of https://github.com/OpenMW/openmw.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
178 lines
5.9 KiB
C++
178 lines
5.9 KiB
C++
#include "protocol.hpp"
|
|
|
|
#include <components/serialization/binaryreader.hpp>
|
|
#include <components/serialization/binarywriter.hpp>
|
|
#include <components/serialization/format.hpp>
|
|
#include <components/serialization/sizeaccumulator.hpp>
|
|
|
|
#include <iomanip>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
namespace NavMeshTool
|
|
{
|
|
namespace
|
|
{
|
|
std::string formatMagic(const char (&value)[std::size(messageMagic)])
|
|
{
|
|
std::ostringstream stream;
|
|
for (const char v : value)
|
|
{
|
|
if (std::isprint(v) && !std::isspace(v))
|
|
stream << '\'' << v << '\'';
|
|
else
|
|
stream << "0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(2)
|
|
<< static_cast<int>(v);
|
|
stream << ' ';
|
|
}
|
|
return stream.str();
|
|
}
|
|
|
|
template <Serialization::Mode mode>
|
|
struct Format : Serialization::Format<mode, Format<mode>>
|
|
{
|
|
using Serialization::Format<mode, Format<mode>>::operator();
|
|
|
|
template <class Visitor, class T>
|
|
auto operator()(Visitor&& visitor, T& value) const
|
|
-> std::enable_if_t<std::is_same_v<std::decay_t<T>, Message>>
|
|
{
|
|
if constexpr (mode == Serialization::Mode::Write)
|
|
visitor(*this, messageMagic);
|
|
else
|
|
{
|
|
static_assert(mode == Serialization::Mode::Read);
|
|
char magic[std::size(messageMagic)];
|
|
visitor(*this, magic);
|
|
if (std::memcmp(magic, messageMagic, sizeof(magic)) != 0)
|
|
throw std::runtime_error("Bad navmeshtool message magic: " + formatMagic(magic));
|
|
}
|
|
visitor(*this, value.mType);
|
|
visitor(*this, value.mSize);
|
|
if constexpr (mode == Serialization::Mode::Write)
|
|
visitor(*this, value.mData, value.mSize);
|
|
else
|
|
visitor(*this, value.mData);
|
|
}
|
|
|
|
template <class Visitor, class T>
|
|
auto operator()(Visitor&& visitor, T& value) const
|
|
-> std::enable_if_t<std::is_same_v<std::decay_t<T>, ExpectedCells>>
|
|
{
|
|
visitor(*this, value.mCount);
|
|
}
|
|
|
|
template <class Visitor, class T>
|
|
auto operator()(Visitor&& visitor, T& value) const
|
|
-> std::enable_if_t<std::is_same_v<std::decay_t<T>, ProcessedCells>>
|
|
{
|
|
visitor(*this, value.mCount);
|
|
}
|
|
|
|
template <class Visitor, class T>
|
|
auto operator()(Visitor&& visitor, T& value) const
|
|
-> std::enable_if_t<std::is_same_v<std::decay_t<T>, ExpectedTiles>>
|
|
{
|
|
visitor(*this, value.mCount);
|
|
}
|
|
|
|
template <class Visitor, class T>
|
|
auto operator()(Visitor&& visitor, T& value) const
|
|
-> std::enable_if_t<std::is_same_v<std::decay_t<T>, GeneratedTiles>>
|
|
{
|
|
visitor(*this, value.mCount);
|
|
}
|
|
};
|
|
|
|
template <class T>
|
|
std::vector<std::byte> serializeToVector(const T& value)
|
|
{
|
|
constexpr Format<Serialization::Mode::Write> format;
|
|
Serialization::SizeAccumulator sizeAccumulator;
|
|
format(sizeAccumulator, value);
|
|
std::vector<std::byte> buffer(sizeAccumulator.value());
|
|
format(Serialization::BinaryWriter(buffer.data(), buffer.data() + buffer.size()), value);
|
|
return buffer;
|
|
}
|
|
|
|
template <class T>
|
|
std::vector<std::byte> serializeImpl(const T& value)
|
|
{
|
|
const auto data = serializeToVector(value);
|
|
const Message message{ static_cast<std::uint64_t>(T::sMessageType), static_cast<std::uint64_t>(data.size()),
|
|
data.data() };
|
|
return serializeToVector(message);
|
|
}
|
|
}
|
|
|
|
std::vector<std::byte> serialize(const ExpectedCells& value)
|
|
{
|
|
return serializeImpl(value);
|
|
}
|
|
|
|
std::vector<std::byte> serialize(const ProcessedCells& value)
|
|
{
|
|
return serializeImpl(value);
|
|
}
|
|
|
|
std::vector<std::byte> serialize(const ExpectedTiles& value)
|
|
{
|
|
return serializeImpl(value);
|
|
}
|
|
|
|
std::vector<std::byte> serialize(const GeneratedTiles& value)
|
|
{
|
|
return serializeImpl(value);
|
|
}
|
|
|
|
const std::byte* deserialize(const std::byte* begin, const std::byte* end, Message& message)
|
|
{
|
|
try
|
|
{
|
|
constexpr Format<Serialization::Mode::Read> format;
|
|
Serialization::BinaryReader reader(begin, end);
|
|
format(reader, message);
|
|
return message.mData + message.mSize;
|
|
}
|
|
catch (const Serialization::NotEnoughData&)
|
|
{
|
|
return begin;
|
|
}
|
|
}
|
|
|
|
TypedMessage decode(const Message& message)
|
|
{
|
|
constexpr Format<Serialization::Mode::Read> format;
|
|
Serialization::BinaryReader reader(message.mData, message.mData + message.mSize);
|
|
switch (static_cast<MessageType>(message.mType))
|
|
{
|
|
case MessageType::ExpectedCells:
|
|
{
|
|
ExpectedCells value;
|
|
format(reader, value);
|
|
return value;
|
|
}
|
|
case MessageType::ProcessedCells:
|
|
{
|
|
ProcessedCells value;
|
|
format(reader, value);
|
|
return value;
|
|
}
|
|
case MessageType::ExpectedTiles:
|
|
{
|
|
ExpectedTiles value;
|
|
format(reader, value);
|
|
return value;
|
|
}
|
|
case MessageType::GeneratedTiles:
|
|
{
|
|
GeneratedTiles value;
|
|
format(reader, value);
|
|
return value;
|
|
}
|
|
}
|
|
throw std::logic_error("Unsupported message type: " + std::to_string(message.mType));
|
|
}
|
|
}
|