#pragma once #include "Common.h" #include "util/ByteUtil.h" #include #include #include using std::string; namespace Feather { // Class to read packet data, such as is produced by NetworkMessage class PacketReader { public: PacketReader(const uint8_t *const dataPtr) : m_data(dataPtr), m_offset(0), m_length(ReadLength()) { } // Peek the next value to be read // If big endian (the default), will reverse byte order. template inline T Peek() { T value = *reinterpret_cast(&m_data[m_offset]); return (endianMode == Endian::Big) ? ReverseBytes(value) : value; } // Read the next value // If big endian (the default), will reverse byte order. template inline T Read() { T value = Peek(); m_offset += sizeof(T); return value; } // Read bool as 1 byte template <> inline bool Read() { // TODO: Could warn on non-bool value return (ReadByte() != 0x00); } // Fast way to peek the next byte inline uint8_t PeekByte() { return m_data[m_offset]; } // Fast way to read a byte inline uint8_t ReadByte() { return m_data[m_offset++]; } inline int32_t ReadLength() { int32_t length = ReadVarInt(&m_lengthSize); // HACK: handle Legacy Server List Ping m_legacy = length == 0xFE && PeekByte() == 0x01; if (m_legacy) return 1; return length; } template inline T ReadVarInt(uint32_t* outCount = nullptr) { uint32_t numRead = 0; int32_t result = 0; uint8_t read; do { read = ReadByte(); int value = (read & 0b01111111); result |= (value << (7 * numRead)); numRead++; if (numRead > 5) { // complain //throw new RuntimeException("VarInt is too big"); } } while ((read & 0b10000000) != 0); if (outCount != nullptr) *outCount = numRead; return static_cast(result); } // can be assigned to string&, string_view string ReadString() { int size = ReadVarInt(); string str; for (int i = 0; i < size; i++) { str += ReadByte(); } return str; } inline BlockPos ReadPosition() { return BlockPos(Read()); } 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 template <> inline uint8_t PacketReader::Peek() { return PeekByte(); } template<> inline uint8_t PacketReader::Read() { return ReadByte(); } }