/*
* 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__