From e8244a33599abcf5f39cb2bae1091519544df8c2 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Mon, 3 Aug 2020 06:07:32 +0100 Subject: [PATCH] Add packet generation + handling automagically --- src/DedicatedServer.cpp | 64 ++++++++- src/DedicatedServer.h | 8 +- src/MinecraftClient.h | 8 +- src/NetworkMessage.h | 12 +- src/PacketReader.h | 14 +- src/PacketTypes.h | 39 ------ src/Protocol.cpp | 125 ----------------- src/Types.h | 22 +++ src/meson.build | 1 - src/{ => protocol}/Protocol.h | 14 +- src/protocol/generate_protocol.py | 216 +++++++++++++++++++++++++----- src/protocol/protocol.hjson | 128 +++++++++++++----- src/util/ByteUtil.h | 56 ++++---- 13 files changed, 413 insertions(+), 294 deletions(-) delete mode 100644 src/PacketTypes.h delete mode 100644 src/Protocol.cpp create mode 100644 src/Types.h rename src/{ => protocol}/Protocol.h (73%) diff --git a/src/DedicatedServer.cpp b/src/DedicatedServer.cpp index 96e7e9d..2e39106 100644 --- a/src/DedicatedServer.cpp +++ b/src/DedicatedServer.cpp @@ -2,15 +2,13 @@ #include "config/ServerProperties.h" #include "PacketReader.h" -#include namespace Feather { DedicatedServer::DedicatedServer(ServerProperties* properties) : m_properties(properties), m_listener(properties->serverPort.GetValue(), this), - m_status(), - m_protocol(*this) + m_status() { m_status.descriptionText = properties->motd.GetValue(); m_status.maxPlayers = properties->maxPlayers.GetValue(); @@ -27,7 +25,7 @@ namespace Feather while (offset != data.size()) { auto reader = PacketReader(&data[offset]); - m_protocol.HandlePacket(client, reader); + Protocol::ProcessPacket(*this, client, reader, client.GetContext().GetState()); offset += reader.Size(); } data.clear(); @@ -56,4 +54,62 @@ namespace Feather auto [clients, lock] = m_clients.borrow(); clients.remove_if([&](MinecraftClient& other) { return other.GetTCPClient().get() == client; }); } + + using namespace Protocol; + + void DedicatedServer::HandleLegacyPing(MinecraftClient& client) + { + Log_Info("Got legacy server list ping."); + } + + template <> + void DedicatedServer::HandlePacket(MinecraftClient& client, const Handholding::ServerboundHandshake& handshake) + { + Log_Info("Client Intention Packet: version=%d, serverIp=%s, port=%u, intention=%d\n", + handshake.protocolVersion, + handshake.serverIP.c_str(), + handshake.port, + handshake.intention + ); + + client.GetContext().SetState(handshake.intention); + } + + template <> + void DedicatedServer::HandlePacket(MinecraftClient& client, const Status::ServerboundRequest& request) + { + Log_Info("Client sent STATUS_PING_REQUEST"); + + Status::ClientboundResponse message = + { + .jsonResponse = m_status.GetServerStatusJSON() + }; + + client.SendMessage(message); + } + + template <> + void DedicatedServer::HandlePacket(MinecraftClient& client, const Status::ServerboundPing& ping) + { + Log_Info("Client sent STATUS_PING: %llu", ping.timestamp); + + Status::ClientboundPong message = + { + .timestamp = ping.timestamp + }; + + client.SendMessage(message); + } + + template <> + void DedicatedServer::HandlePacket(MinecraftClient& client, const Login::ServerboundStart& start) + { + Login::ClientboundSuccess message = + { + .uuid = { 4658857991808325907ull, 7518717155607718277ull }, + .username = start.username + }; + + client.SendMessage(message); + } } diff --git a/src/DedicatedServer.h b/src/DedicatedServer.h index 8568997..3b99884 100644 --- a/src/DedicatedServer.h +++ b/src/DedicatedServer.h @@ -2,7 +2,7 @@ #include "MinecraftClient.h" #include "ServerStatus.h" -#include "Protocol.h" +#include "protocol/Protocol.h" #include "network/IListenerInterface.h" @@ -19,13 +19,15 @@ namespace Feather void OnClientConnect(Network::TCPClientHandle&& client) override; void OnClientDisconnect(const Network::TCPClient* client) override; - ServerStatus& GetStatus() { return m_status; } + void HandleLegacyPing(MinecraftClient& client); + + template + void HandlePacket(MinecraftClient& client, const T& message); private: ServerProperties* m_properties; Network::TCPListener m_listener; ServerStatus m_status; - Protocol m_protocol; LockableList m_clients; }; diff --git a/src/MinecraftClient.h b/src/MinecraftClient.h index 9ca4cc4..bf4997e 100644 --- a/src/MinecraftClient.h +++ b/src/MinecraftClient.h @@ -2,7 +2,7 @@ #include "network/TCPListener.h" #include "network/TCPClient.h" -#include "Protocol.h" +#include "protocol/Protocol.h" #include "NetworkMessage.h" namespace Feather @@ -14,12 +14,12 @@ namespace Feather ~MinecraftClient(); inline Network::TCPClientHandle& GetTCPClient() { return m_client; } - inline ProtocolContext& GetContext() { return m_context; } + inline Protocol::ProtocolContext& GetContext() { return m_context; } void SendMessage(const NetworkMessage& message); private: - Network::TCPClientHandle m_client; - ProtocolContext m_context; + Network::TCPClientHandle m_client; + Protocol::ProtocolContext m_context; }; } diff --git a/src/NetworkMessage.h b/src/NetworkMessage.h index 07cbf29..610c1fd 100644 --- a/src/NetworkMessage.h +++ b/src/NetworkMessage.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #define VARINT_MAX_SIZE 5 @@ -16,9 +17,7 @@ namespace Feather public: NetworkMessage(int32_t expectedSize) { - size_t initialSize = VARINT_MAX_SIZE + expectedSize; - - m_data.reserve(initialSize); + m_data.reserve(expectedSize); m_data.resize(VARINT_MAX_SIZE); } @@ -37,7 +36,7 @@ namespace Feather { if constexpr (asEndian == Endian::Big && sizeof(T) > 1) { - T valueReversed = ReverseBytes(value); + T valueReversed = ReverseBytes(value); WriteData(&valueReversed, sizeof(T)); } else @@ -67,6 +66,11 @@ namespace Feather WriteData(string, stringSize); } + inline void WriteString(const std::string& string) + { + WriteString(string.c_str(), string.length()); + } + inline void Finalize() { PrependVarIntSize(); diff --git a/src/PacketReader.h b/src/PacketReader.h index 5f4a460..ab2006b 100644 --- a/src/PacketReader.h +++ b/src/PacketReader.h @@ -27,7 +27,7 @@ namespace Feather { T value = *reinterpret_cast(&m_data[m_offset]); - return (endianMode == Endian::Big) ? ReverseBytes(value) : value; + return (endianMode == Endian::Big) ? ReverseBytes(value) : value; } // Read the next value @@ -51,11 +51,9 @@ namespace Feather int32_t length = ReadVarInt(&m_lengthSize); // HACK: handle Legacy Server List Ping - if (length == 0xFE) { - if (PeekByte() == 0x01) { - return 1; - } - } + m_legacy = length == 0xFE && PeekByte() == 0x01; + if (m_legacy) + return 1; return length; } @@ -100,11 +98,15 @@ namespace Feather uint32_t Length() const { return m_length; } uint32_t Size() const { return m_length + m_lengthSize; } + + bool IsLegacyPing() const { return m_legacy; } + private: const uint8_t *const m_data; uint32_t m_offset; const uint32_t m_length; uint32_t m_lengthSize; + bool m_legacy; }; // Use fast Read and Peek for uint8_t diff --git a/src/PacketTypes.h b/src/PacketTypes.h deleted file mode 100644 index 3203524..0000000 --- a/src/PacketTypes.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -namespace Feather -{ - enum class ServerboundHandholdingPacketId : int32_t - { - Handshake = 0, - }; - - enum class ServerboundStatusPacketId : int32_t - { - Request = 0, - Ping = 1 - }; - - enum class ClientBoundStatusPacketId : int32_t - { - Response = 0, - Pong = 1 - }; - - enum class ServerboundLoginPacketId : int32_t - { - LoginStart = 0, - EncryptionResponse = 1, - LoginPluginResponse = 2 - }; - - enum class ClientboundLoginPacketId : int32_t - { - Disconnect = 0, - EncryptionRequest = 1, - LoginSuccess = 2, - SetCompression = 3, - LoginPluginRequest = 4 - }; - - constexpr uint8_t LegacyServerListPing = 0xFE; -} diff --git a/src/Protocol.cpp b/src/Protocol.cpp deleted file mode 100644 index 585a923..0000000 --- a/src/Protocol.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#include "Protocol.h" -#include "PacketReader.h" -#include "PacketTypes.h" -#include "MinecraftClient.h" -#include "DedicatedServer.h" - -#include - -namespace Feather -{ - void Protocol::HandlePacket(MinecraftClient& client, PacketReader& packet) - { - auto& context = client.GetContext(); - switch (context.GetState()) - { - case ProtocolState::Handholding: - { - auto id = packet.ReadVarInt(); - uint8_t legacyId = packet.Peek(); - if (id != ServerboundHandholdingPacketId::Handshake && legacyId != LegacyServerListPing) - { - printf("[Protocol] Client sent packet with non-zero ID 0x%x while state was HANDSHAKING\n", id); - break; - } - - if (id != ServerboundHandholdingPacketId::Handshake) - { - // Legacy server ping. - // TODO: Do server list ping here. - break; - } - - HandshakeMessage handshake(packet); - - printf("[Protocol] Client Intention Packet: version=%d, serverIp=%s, port=%u, intention=%d\n", - handshake.protocolVersion, - handshake.serverIP.c_str(), - handshake.port, - handshake.intention - ); - - context.SetState(handshake.intention); - - break; - } - - case ProtocolState::Status: - { - auto id = packet.ReadVarInt(); - switch (id) - { - case ServerboundStatusPacketId::Request: - { - printf("[Protocol] Client sent STATUS_PING_REQUEST\n"); - - std::string status = m_server.GetStatus().GetServerStatusJSON(); - - NetworkMessage msg(VARINT_MAX_SIZE + status.length()); - // Packet ID - msg.WriteVarInt(ClientBoundStatusPacketId::Response); - // JSON Contents - msg.WriteString(status.c_str(), static_cast(status.length())); - msg.Finalize(); - client.SendMessage(msg); - - break; - } - case ServerboundStatusPacketId::Ping: - { - PingMessage ping(packet); - printf("[Protocol] Client sent STATUS_PING: %lld\n", ping.timestamp); - - NetworkMessage msg(VARINT_MAX_SIZE + sizeof(int64_t)); - - msg.WriteVarInt(ClientBoundStatusPacketId::Pong); - msg.Write(ping.timestamp); - msg.Finalize(); - - client.SendMessage(msg); - - break; - } - } - break; - } - - case ProtocolState::Login: - { - auto id = packet.ReadVarInt(); - - switch (id) - { - case ServerboundLoginPacketId::LoginStart: - { - LoginStartMessage loginStart(packet); - - // 1.15.2... - //std::string uuid = "ecb99913-96a8-40a7-8529-a2ca6ad95768"; - //uuid.resize(36); - //NetworkMessage msg(VARINT_MAX_SIZE * 3 + uuid.length() + loginStart.username.length()); - - NetworkMessage msg(VARINT_MAX_SIZE * 3 + 2 * sizeof(uint64_t) + loginStart.username.length()); - - msg.WriteVarInt(ClientboundLoginPacketId::LoginSuccess); - // UUID 1.16.1 - msg.Write(4658857991808325907LL); - msg.Write(7518717155607718277LL); - - // UUID 1.15.2 - //msg.WriteString(uuid.c_str(), uuid.length()); - - msg.WriteString(loginStart.username.c_str(), loginStart.username.length()); - msg.Finalize(); - - client.SendMessage(msg); - - break; - } - } - - break; - } - } - } -} diff --git a/src/Types.h b/src/Types.h new file mode 100644 index 0000000..7977e97 --- /dev/null +++ b/src/Types.h @@ -0,0 +1,22 @@ +#pragma once + +#include "util/ByteUtil.h" + +#include +#include + +namespace Feather +{ + struct MinecraftUUID + { + std::array data; + }; + + inline MinecraftUUID ReverseBytes(MinecraftUUID uuid) + { + return MinecraftUUID + { + .data = { ReverseBytes(uuid.data[0]), ReverseBytes(uuid.data[1]) } + }; + } +} \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index c5328c7..9df8375 100644 --- a/src/meson.build +++ b/src/meson.build @@ -2,7 +2,6 @@ feather_src = [ 'Main.cpp', 'DedicatedServer.cpp', 'MinecraftClient.cpp', - 'Protocol.cpp', 'logging/Logger.cpp', diff --git a/src/Protocol.h b/src/protocol/Protocol.h similarity index 73% rename from src/Protocol.h rename to src/protocol/Protocol.h index 0ba9b4a..b7198f5 100644 --- a/src/Protocol.h +++ b/src/protocol/Protocol.h @@ -8,10 +8,13 @@ namespace Feather { - class DedicatedServer; class PacketReader; class MinecraftClient; + class DedicatedServer; +} +namespace Feather::Protocol +{ class ProtocolContext { public: @@ -28,13 +31,4 @@ namespace Feather private: ProtocolState m_state = ProtocolState::Handholding; }; - - class Protocol - { - DedicatedServer& m_server; - public: - Protocol(DedicatedServer& server) : m_server(server) {} - - void HandlePacket(MinecraftClient& client, PacketReader& packet); - }; } diff --git a/src/protocol/generate_protocol.py b/src/protocol/generate_protocol.py index a0eb4bc..de590c6 100644 --- a/src/protocol/generate_protocol.py +++ b/src/protocol/generate_protocol.py @@ -2,22 +2,75 @@ import hjson import sys text = '' +varint_max_size = 5 +indent_count = 0 + +def indent(): + global indent_count + indent_count += 1 + +def unindent(): + global indent_count + indent_count -= 1 def add_text(fmt, *args): global text + global indent_count + for i in range(indent_count): + text += ' ' text += (fmt + '\n').format(*args) -def resolve_type(aliases, type): - if type in aliases: - return aliases[type] +def newline(): + global text + text += '\n' + +def extract_array_type(type): + if '[' in type and ']' in type: + return type[:type.rfind('[')] return type +def extract_array_count(type): + if '[' in type and ']' in type: + return int(type[type.find('[')+1:type.rfind(']')]) + return 1 + +def resolve_type(aliases, type): + type_no_array = type.rstrip('_') + if type_no_array in aliases: + return aliases[type_no_array] + return type + +def get_type_size(type): + global varint_max_size + count = extract_array_count(type) + type = extract_array_type(type) + + if type == 'varint': + return varint_max_size + elif type == 'int64' or type == 'uint64' or type == 'double': + return 8 + elif type == 'int32' or type == 'uint32' or type == 'float': + return 4 + elif type == 'int16' or type == 'uint16': + return 2 + elif type == 'int8' or type == 'uint8': + return 1 + elif type == 'string': + return count + elif type == 'uuid': + return 16 + else: + print(type) + assert False + def print_states(states): - add_text(' enum class ProtocolState : int32_t') - add_text(' {{') + add_text('enum class ProtocolState : int32_t') + add_text('{{') + indent() for state, value in states.items(): - add_text(' {} = {},', state, value) - add_text(' }};') + add_text('{} = {},', state, value) + unindent() + add_text('}};') def get_rw_func(primitiveType, aliasedType, read): prefix = 'Read' if read else 'Write' @@ -29,30 +82,113 @@ def get_rw_func(primitiveType, aliasedType, read): elif aliasedType == 'string': return '{}String'.format(prefix) else: - return 'Read<{}>'.format(aliasedType) + return '{}<{}>'.format(prefix, primitiveType) -def print_messages(list, aliases, primitives, serverbound): +def print_messages(list, aliases, primitives): global text - for message_name, message in list.items(): - add_text(' struct {}Message', message_name) - add_text(' {{') - add_text(' static constexpr int32_t PacketId = {};', message['id']) - add_text(' static constexpr bool ServerBound = {};', 'true' if serverbound else 'false') - add_text(' static constexpr ProtocolState PacketState = ProtocolState::{};', message['state']) - add_text('') - if serverbound: - add_text(' {}Message(PacketReader& reader)', message_name) - add_text(' {{') - for name, type in message['vars'].items(): - add_text(' {} = reader.{}();', name, get_rw_func(resolve_type(primitives, type), resolve_type(aliases, type), True)) - add_text(' }}') - add_text('') + for state, direction_list in list.items(): + add_text('namespace {}', state.capitalize()) + add_text('{{') + indent() + for direction, messages in direction_list.items(): + serverbound = direction == 'serverbound' + for message_name, message in messages.items(): + global varint_max_size + size = varint_max_size # Packet Length + size += varint_max_size # Packet Id + for name, type in message['vars'].items(): + size += get_type_size(resolve_type(aliases, type)) + + struct_name = '{}{}'.format(direction.capitalize(), message_name) + add_text('struct {}', struct_name) + add_text('{{') + indent() + add_text('static constexpr int32_t PacketId = {};', message['id']) + add_text('static constexpr bool Serverbound = {};', 'true' if serverbound else 'false') + add_text('static constexpr ProtocolState PacketState = ProtocolState::{};', state.capitalize()) + add_text('static constexpr size_t MaxSize = {};', size) + newline() + if serverbound: + add_text('{}(PacketReader& reader)', struct_name) + add_text('{{') + indent() + for name, type in message['vars'].items(): + add_text('{} = reader.{}();', name, get_rw_func(resolve_type(primitives, extract_array_type(type)), resolve_type(aliases, extract_array_type(type)), True)) + unindent() + add_text('}}') + newline() + else: + add_text('operator NetworkMessage() const') + add_text('{{') + indent() + add_text('NetworkMessage msg(MaxSize);') + add_text('msg.WriteVarInt(PacketId);') + for name, type in message['vars'].items(): + add_text('msg.{}({});', get_rw_func(resolve_type(primitives, extract_array_type(type)), resolve_type(aliases, extract_array_type(type)), False), name) + add_text('msg.Finalize();') + add_text('return msg;') + unindent() + add_text('}}') + + for name, type in message['vars'].items(): + resolved_type = resolve_type(primitives, extract_array_type(type)) + if not serverbound and resolved_type == 'std::string': + add_text('const {}& {};', resolved_type, name) + else: + add_text('{} {};', resolved_type, name) + + unindent() + add_text('}};') + newline() + unindent() + add_text('}}') + newline() + +def print_handler(list): + add_text('template ') + add_text('void ProcessPacket(HandlerType& handler, ClientType& client, PacketReader& packet, ProtocolState state)') + add_text('{{') + indent() + add_text('if (packet.IsLegacyPing())') + add_text('{{') + indent() + add_text('handler.HandleLegacyPing(client);') + add_text('return;') + unindent() + add_text('}}') + newline() + add_text('const int32_t packetId = packet.ReadVarInt();') + add_text('switch (state)') + add_text('{{') + indent() + for state, direction_list in list.items(): + add_text('case ProtocolState::{}:', state.capitalize()) + add_text('{{') + indent() + add_text('switch (packetId)') + add_text('{{') + indent() + for direction, messages in direction_list.items(): + serverbound = direction == 'serverbound' + if not serverbound: + continue + for message_name, message in messages.items(): + name = '{}::Serverbound{}'.format(state.capitalize(), message_name) + add_text('case {}::PacketId:', name) + indent() + add_text('handler.HandlePacket<{}>(client, {}(packet));', name, name) + add_text('break;') + unindent() + unindent() + add_text('}}') + add_text('break;') + unindent() + add_text('}}') + unindent() + add_text('}}') + unindent() + add_text('}}') - for name, type in message['vars'].items(): - add_text(' {} {};', resolve_type(primitives, type), name) - - add_text(' }};') - add_text('') def print_protocol(): with open(sys.argv[1]) as message_file: @@ -60,29 +196,37 @@ def print_protocol(): print_states(message_scheme['states']) - add_text('') + newline() print_messages( - message_scheme['messages']['serverbound'], + message_scheme['messages'], message_scheme['types']['aliases'], - message_scheme['types']['primitives'], - True) + message_scheme['types']['primitives']) + + newline() + + print_handler(message_scheme['messages']) def main(): + global text + if len(sys.argv) != 3: print('Usage: generate_protocol.py ') return add_text('#pragma once') - add_text('') + newline() add_text('#include ') + add_text('#include "Types.h"') add_text('#include "PacketReader.h"') - add_text('') - add_text('namespace Feather') + newline() + add_text('namespace Feather::Protocol') add_text('{{') + indent() print_protocol() + unindent() add_text('}}') - add_text('') + newline() with open(sys.argv[2], 'w') as out_file: out_file.write(text) diff --git a/src/protocol/protocol.hjson b/src/protocol/protocol.hjson index 6259574..87eb01c 100644 --- a/src/protocol/protocol.hjson +++ b/src/protocol/protocol.hjson @@ -10,6 +10,16 @@ { varint : int32_t string : std::string + uuid : MinecraftUUID + + uint64 : uint64_t + int64 : int64_t + uint32 : uint32_t + int32 : int32_t + uint16 : uint16_t + int16 : int16_t + uint8 : uint8_t + int8 : int8_t } } @@ -23,45 +33,103 @@ messages : { - serverbound : + handholding : { - Handshake : + serverbound : { - id : 0 - state : Handholding - vars : + Handshake : { - protocolVersion : varint - serverIP : string - port : uint16_t - intention : ProtocolState - } - } - - Ping : - { - id : 1 - state : Login - vars : - { - timestamp : uint64_t - } - } - - LoginStart : - { - id : 0 - state : Login - vars : - { - username : string + id : 0 + vars : + { + protocolVersion : varint + serverIP : string[255] + port : uint16 + intention : ProtocolState + } } } } - clientbound : + status : { + serverbound : + { + Request : + { + id : 0 + vars : + { + } + } + Ping : + { + id : 1 + vars : + { + timestamp : uint64 + } + } + } + + clientbound : + { + Response : + { + id : 0 + vars : + { + jsonResponse : string[32767] + } + } + + Pong : + { + id : 1 + vars : + { + timestamp : uint64 + } + } + } + } + + login : + { + clientbound : + { + Disconnect : + { + id : 0 + vars : + { + username : string[32767] + } + } + + Success : + { + id : 2 + vars : + { + uuid : uuid + username : string[16] + } + } + } + + serverbound : + { + Start : + { + id : 0 + vars : + { + username : string[16] + } + } + } } } } \ No newline at end of file diff --git a/src/util/ByteUtil.h b/src/util/ByteUtil.h index eda6fb0..7cefd95 100644 --- a/src/util/ByteUtil.h +++ b/src/util/ByteUtil.h @@ -1,38 +1,30 @@ #pragma once -#ifdef _MSC_VER -#include -#endif +#include +#include -enum class Endian +namespace Feather { - Little, - Big -}; + enum class Endian + { + Little, + Big + }; -// Reverses the byte order of a primitive value of any typical size -template -inline constexpr T ReverseBytes(T n) -{ - // 1 byte, cannot reverse - if constexpr (sizeof(T) == 1) return n; - -#ifdef __GNUC__ - // GCC intrinsic byte swaps - // https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fbswap16 - - if constexpr (sizeof(T) == 2) return __builtin_bswap16(n); // 2 bytes - else if constexpr (sizeof(T) == 4) return __builtin_bswap32(n); // 4 bytes - else if constexpr (sizeof(T) == 8) return __builtin_bswap64(n); // 8 bytes - -#elif defined(_MSC_VER) - // MSVC intrinsic byteswaps - // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/byteswap-uint64-byteswap-ulong-byteswap-ushort - - if constexpr (sizeof(T) == 2) return _byteswap_ushort(n); // 2 bytes - else if constexpr (sizeof(T) == 4) return _byteswap_ulong(n); // 4 bytes - else if constexpr (sizeof(T) == 8) return _byteswap_uint64(n); // 8 bytes -#endif - //else static_assert(false, "Attempted to call ReverseBytes() on type with unusual size."); -} + // TODO: Make this constexpr + inline uint8_t ReverseBytes(uint8_t n) { return n; } + #ifdef __GNUC__ + inline uint16_t ReverseBytes(uint16_t n) { return __builtin_bswap16(n); } + inline uint32_t ReverseBytes(uint32_t n) { return __builtin_bswap32(n); } + inline uint32_t ReverseBytes(uint64_t n) { return __builtin_bswap64(n); } + #else + inline uint16_t ReverseBytes(uint16_t n) { return _byteswap_ushort(n); } + inline uint32_t ReverseBytes(uint32_t n) { return _byteswap_ulong (n); } + inline uint64_t ReverseBytes(uint64_t n) { return _byteswap_uint64(n); } + #endif + inline int8_t ReverseBytes(int8_t n) { return n; } + inline int16_t ReverseBytes(int16_t n) { return int16_t(ReverseBytes(uint16_t(n))); } + inline int32_t ReverseBytes(int32_t n) { return int32_t(ReverseBytes(uint32_t(n))); } + inline int64_t ReverseBytes(int64_t n) { return int64_t(ReverseBytes(uint64_t(n))); } +} \ No newline at end of file