/* * Copyright (C) 2008-2014 TrinityCore * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #ifndef __BATTLENETBITSTREAM_H__ #define __BATTLENETBITSTREAM_H__ #include "Common.h" #include "ByteConverter.h" #include "MessageBuffer.h" #include #include #include #include namespace Battlenet { class BitStreamPositionException : public std::exception { static uint32 const MessageSize = 128; public: BitStreamPositionException(bool read, uint32 operationSize, uint32 position, uint32 streamSize) { memset(_message, 0, MessageSize); snprintf(_message, MessageSize, "Attempted to %s more bits (%u) %s stream than %s (%u)\n", (read ? "read" : "write"), operationSize + position, (read ? "from" : "to"), (read ? "exist" : "allowed"), streamSize); } char const* what() const throw() { return _message; } private: char _message[MessageSize]; }; class BitStream { public: static uint32 const MaxSize = 0x4000; // length : The maximum number of bytes to read BitStream(uint32 length) : _writePos(length * 8), _readPos(0) { _buffer.resize(length, 0); } BitStream(MessageBuffer&& buffer) : _writePos(buffer.GetActiveSize() * 8), _readPos(0), _buffer(buffer.Move()) { } BitStream() : _writePos(0), _readPos(0) { _buffer.reserve(0x1000); } void AlignToNextByte() { _readPos = (_readPos + 7) & ~7; _writePos = (_writePos + 7) & ~7; } std::string ReadString(uint32 bitCount, int32 baseLength = 0) { uint32 len = Read(bitCount) + baseLength; AlignToNextByte(); std::string str(reinterpret_cast(&_buffer[_readPos >> 3]), len); _readPos += len * 8; return str; } std::unique_ptr ReadBytes(uint32 count) { AlignToNextByte(); if (_readPos + count * 8 > _writePos) throw BitStreamPositionException(true, count * 8, _readPos, _writePos); std::unique_ptr buf(new uint8[count]); memcpy(buf.get(), &_buffer[_readPos >> 3], count); _readPos += count * 8; return buf; } float ReadFloat() { uint32 val = Read(32); return *reinterpret_cast(&val); } std::string ReadFourCC() { uint32 fcc = Read(32); EndianConvertReverse(fcc); size_t len = 4; while (!(fcc & 0xFF)) { fcc >>= 8; --len; } return std::string(reinterpret_cast(&fcc), len); } template T Read(uint32 bitCount) { static_assert(std::is_integral::value || std::is_enum::value, "T must be an integer type"); if (_readPos + bitCount >= _writePos) throw BitStreamPositionException(true, bitCount, _readPos, _writePos); uint64 ret = 0; while (bitCount != 0) { uint32 bitPos = (_readPos & 7); uint32 bitsLeftInByte = 8 - bitPos; if (bitsLeftInByte >= bitCount) bitsLeftInByte = bitCount; bitCount -= bitsLeftInByte; ret |= (uint64)(_buffer[_readPos >> 3] >> bitPos & (uint32)((uint8)(1 << bitsLeftInByte) - 1)) << bitCount; _readPos += bitsLeftInByte; } return static_cast(ret); } void WriteString(std::string const& str, uint32 bitCount, int32 baseLength = 0) { Write(str.length() + baseLength, bitCount); WriteBytes(str.c_str(), str.length()); } template void WriteBytes(T* data, uint32 count) { AlignToNextByte(); if (!count || !data) return; if ((_writePos >> 3) + count > MaxSize) throw BitStreamPositionException(false, count * 8, _writePos, MaxSize * 8); _buffer.resize(_buffer.size() + count); memcpy(&_buffer[_writePos >> 3], data, count); _writePos += count * 8; } void WriteFloat(float value) { uint32 intVal = *reinterpret_cast(&value); Write(intVal, 32); } void WriteFourCC(std::string const& fcc) { uint32 intVal = *(uint32*)fcc.c_str(); size_t len = fcc.length(); EndianConvertReverse(intVal); // Add padding while (len++ < 4) intVal >>= 8; Write(intVal, 32); } template void Write(T value, uint32 bitCount) { static_assert(std::is_integral::value || std::is_enum::value, "T must be an integer type"); if (_writePos + bitCount >= 8 * MaxSize) throw BitStreamPositionException(false, bitCount, _writePos, MaxSize * 8); while (bitCount != 0) { uint32 bitPos = (_writePos & 7); uint32 bitsLeftInByte = 8 - bitPos; if (bitsLeftInByte >= bitCount) bitsLeftInByte = bitCount; bitCount -= bitsLeftInByte; uint8 firstHalf = (uint8)(~(((uint8)(1 << bitsLeftInByte) - 1) << bitPos)); uint8 secondHalf = (uint8)((((uint8)(1 << bitsLeftInByte) - 1) & (uint8)(value >> bitCount)) << bitPos); if (_buffer.size() > (_writePos >> 3)) _buffer[_writePos >> 3] = (uint8)((_buffer[_writePos >> 3] & firstHalf) | secondHalf); else _buffer.push_back(secondHalf); _writePos += bitsLeftInByte; } } void SetReadPos(uint32 bits) { if (bits >= _writePos) throw BitStreamPositionException(true, bits, 0, _writePos); _readPos = bits; } bool IsRead() const { return _readPos >= _writePos; } uint8* GetBuffer() { return _buffer.data(); } uint8 const* GetBuffer() const { return _buffer.data(); } size_t GetSize() const { return ((_writePos + 7) & ~7) / 8; } private: uint32 _writePos; uint32 _readPos; std::vector _buffer; }; template<> bool BitStream::Read(uint32 bitCount); } #endif // __BATTLENETBITSTREAM_H__