iw4x-client/src/Utils/Stream.cpp

365 lines
8.0 KiB
C++
Raw Normal View History

2016-07-11 11:14:58 -04:00
#include "STDInclude.hpp"
namespace Utils
{
std::string Stream::Reader::readString()
2016-07-11 11:14:58 -04:00
{
std::string str;
while (char byte = this->readByte())
2016-07-11 11:14:58 -04:00
{
str.append(&byte, 1);
}
return str;
}
const char* Stream::Reader::readCString()
2016-07-11 11:14:58 -04:00
{
return this->allocator->duplicateString(this->readString());
2016-07-11 11:14:58 -04:00
}
char Stream::Reader::readByte()
2016-07-11 11:14:58 -04:00
{
if ((this->position + 1) <= this->buffer.size())
2016-07-11 11:14:58 -04:00
{
return this->buffer[this->position++];
2016-07-11 11:14:58 -04:00
}
return 0;
}
void* Stream::Reader::read(size_t size, size_t count)
2016-07-11 11:14:58 -04:00
{
size_t bytes = size * count;
if ((this->position + bytes) <= this->buffer.size())
2016-07-11 11:14:58 -04:00
{
void* _buffer = this->allocator->allocate(bytes);
2016-07-11 11:14:58 -04:00
std::memcpy(_buffer, this->buffer.data() + this->position, bytes);
this->position += bytes;
2016-07-11 11:14:58 -04:00
return _buffer;
2016-07-11 11:14:58 -04:00
}
return nullptr;
}
bool Stream::Reader::end()
2016-07-11 11:14:58 -04:00
{
return (this->buffer.size() == this->position);
2016-07-11 11:14:58 -04:00
}
void Stream::Reader::seek(unsigned int _position)
2016-07-11 11:14:58 -04:00
{
if (this->buffer.size() >= _position)
2016-07-11 11:14:58 -04:00
{
this->position = _position;
2016-07-11 11:14:58 -04:00
}
}
Stream::Stream() : criticalSectionState(0)
2016-07-11 11:14:58 -04:00
{
memset(this->blockSize, 0, sizeof(this->blockSize));
#ifdef DEBUG
if(this->writeLog) return;
if(fopen_s(&this->writeLog, "userraw/logs/zb_writes.log", "w"))
{
Components::Logger::Print("WARNING: Couldn't open write log. Writes from ZoneBuilder will not be logged.\n");
}
#endif
2016-07-11 11:14:58 -04:00
}
Stream::Stream(size_t size) : Stream()
{
this->buffer.reserve(size);
2016-07-11 11:14:58 -04:00
}
Stream::~Stream()
{
this->buffer.clear();
2016-07-11 11:14:58 -04:00
if (this->criticalSectionState != 0)
2016-07-11 11:14:58 -04:00
{
MessageBoxA(0, Utils::String::VA("Invalid critical section state '%i' for stream destruction!", this->criticalSectionState), "WARNING", MB_ICONEXCLAMATION);
2016-07-11 11:14:58 -04:00
}
#ifdef DEBUG
if(this->writeLog)
{
fclose(this->writeLog);
}
#endif
2016-07-11 11:14:58 -04:00
};
size_t Stream::length()
2016-07-11 11:14:58 -04:00
{
return this->buffer.length();
2016-07-11 11:14:58 -04:00
}
size_t Stream::capacity()
2016-07-11 11:14:58 -04:00
{
return this->buffer.capacity();
2016-07-11 11:14:58 -04:00
}
char* Stream::save(const void* _str, size_t size, size_t count)
2016-07-11 11:14:58 -04:00
{
return this->save(this->getCurrentBlock(), _str, size, count);
2016-07-11 11:14:58 -04:00
}
char* Stream::save(Game::XFILE_BLOCK_TYPES stream, const void * _str, size_t size, size_t count)
2016-07-11 11:14:58 -04:00
{
// Only those seem to actually write data.
// As I'm not sure though, I'll still write the data
// Use IncreaseBlockSize to fill virtual streams
if (stream != Game::XFILE_BLOCK_TEMP && stream != Game::XFILE_BLOCK_VIRTUAL && stream != Game::XFILE_BLOCK_PHYSICAL)
{
this->increaseBlockSize(stream, size * count);
return this->at();
}
auto data = this->data();
2016-07-11 11:14:58 -04:00
if (this->isCriticalSection() && this->length() + (size * count) > this->capacity())
2016-07-11 11:14:58 -04:00
{
MessageBoxA(0, Utils::String::VA("Potential stream reallocation during critical operation detected! Writing data of the length 0x%X exceeds the allocated stream size of 0x%X\n", (size * count), this->capacity()), "ERROR", MB_ICONERROR);
2016-07-11 11:14:58 -04:00
__debugbreak();
}
this->buffer.append(static_cast<const char*>(_str), size * count);
2016-07-11 11:14:58 -04:00
// log the write for zonebuilder debugging
SAVE_LOG_WRITE(size * count);
if (this->data() != data && this->isCriticalSection())
2016-07-11 11:14:58 -04:00
{
MessageBoxA(0, "Stream reallocation during critical operations not permitted!\nPlease increase the initial memory size or reallocate memory during non-critical sections!", "ERROR", MB_ICONERROR);
__debugbreak();
}
this->increaseBlockSize(stream, size * count);
2016-07-11 11:14:58 -04:00
return this->at() - (size * count);
2016-07-11 11:14:58 -04:00
}
char* Stream::save(Game::XFILE_BLOCK_TYPES stream, int value, size_t count)
2016-07-11 11:14:58 -04:00
{
auto ret = this->length();
2016-07-11 11:14:58 -04:00
for (size_t i = 0; i < count; ++i)
{
this->save(stream, &value, 4, 1);
2016-07-11 11:14:58 -04:00
}
return this->data() + ret;
2016-07-11 11:14:58 -04:00
}
char* Stream::saveString(std::string string)
2016-07-11 11:14:58 -04:00
{
return this->saveString(string.data()/*, string.size()*/);
2016-07-11 11:14:58 -04:00
}
char* Stream::saveString(const char* string)
2016-07-11 11:14:58 -04:00
{
return this->saveString(string, strlen(string));
2016-07-11 11:14:58 -04:00
}
char* Stream::saveString(const char* string, size_t len)
2016-07-11 11:14:58 -04:00
{
auto ret = this->length();
2016-07-11 11:14:58 -04:00
if (string)
{
this->save(string, len);
2016-07-11 11:14:58 -04:00
}
this->saveNull();
2016-07-11 11:14:58 -04:00
return this->data() + ret;
2016-07-11 11:14:58 -04:00
}
char* Stream::saveText(std::string string)
2016-07-11 11:14:58 -04:00
{
return this->save(string.data(), string.length());
2016-07-11 11:14:58 -04:00
}
char* Stream::saveByte(unsigned char byte, size_t count)
2016-07-11 11:14:58 -04:00
{
auto ret = this->length();
2016-07-11 11:14:58 -04:00
for (size_t i = 0; i < count; ++i)
{
this->save(&byte, 1);
2016-07-11 11:14:58 -04:00
}
return this->data() + ret;
2016-07-11 11:14:58 -04:00
}
char* Stream::saveNull(size_t count)
2016-07-11 11:14:58 -04:00
{
return this->saveByte(0, count);
2016-07-11 11:14:58 -04:00
}
char* Stream::saveMax(size_t count)
2016-07-11 11:14:58 -04:00
{
return this->saveByte(static_cast<unsigned char>(-1), count);
2016-07-11 11:14:58 -04:00
}
void Stream::align(Stream::Alignment align)
2016-07-11 11:14:58 -04:00
{
uint32_t size = 2 << align;
// Not power of 2!
if (!size || (size & (size - 1))) return;
--size;
Game::XFILE_BLOCK_TYPES stream = this->getCurrentBlock();
this->blockSize[stream] = ~size & (this->getBlockSize(stream) + size);
2016-07-11 11:14:58 -04:00
}
bool Stream::pushBlock(Game::XFILE_BLOCK_TYPES stream)
2016-07-11 11:14:58 -04:00
{
this->streamStack.push_back(stream);
return this->isValidBlock(stream);
2016-07-11 11:14:58 -04:00
}
bool Stream::popBlock()
2016-07-11 11:14:58 -04:00
{
if (!this->streamStack.empty())
2016-07-11 11:14:58 -04:00
{
this->streamStack.pop_back();
2016-07-11 11:14:58 -04:00
return true;
}
return false;
}
bool Stream::isValidBlock(Game::XFILE_BLOCK_TYPES stream)
2016-07-11 11:14:58 -04:00
{
return (stream < Game::MAX_XFILE_COUNT && stream >= Game::XFILE_BLOCK_TEMP);
}
void Stream::increaseBlockSize(Game::XFILE_BLOCK_TYPES stream, unsigned int size)
2016-07-11 11:14:58 -04:00
{
if (this->isValidBlock(stream))
2016-07-11 11:14:58 -04:00
{
this->blockSize[stream] += size;
2016-07-11 11:14:58 -04:00
}
}
void Stream::increaseBlockSize(unsigned int size)
2016-07-11 11:14:58 -04:00
{
return this->increaseBlockSize(this->getCurrentBlock(), size);
2016-07-11 11:14:58 -04:00
}
Game::XFILE_BLOCK_TYPES Stream::getCurrentBlock()
2016-07-11 11:14:58 -04:00
{
if (!this->streamStack.empty())
2016-07-11 11:14:58 -04:00
{
return this->streamStack.back();
2016-07-11 11:14:58 -04:00
}
return Game::XFILE_BLOCK_INVALID;
}
char* Stream::at()
2016-07-11 11:14:58 -04:00
{
return reinterpret_cast<char*>(this->data() + this->length());
2016-07-11 11:14:58 -04:00
}
char* Stream::data()
2016-07-11 11:14:58 -04:00
{
return const_cast<char*>(this->buffer.data());
2016-07-11 11:14:58 -04:00
}
unsigned int Stream::getBlockSize(Game::XFILE_BLOCK_TYPES stream)
2016-07-11 11:14:58 -04:00
{
if (this->isValidBlock(stream))
2016-07-11 11:14:58 -04:00
{
return this->blockSize[stream];
2016-07-11 11:14:58 -04:00
}
return 0;
}
DWORD Stream::getPackedOffset()
2016-07-11 11:14:58 -04:00
{
Game::XFILE_BLOCK_TYPES block = this->getCurrentBlock();
2016-07-11 11:14:58 -04:00
Stream::Offset offset;
offset.block = block;
offset.offset = this->getBlockSize(block);
return offset.getPackedOffset();
2016-07-11 11:14:58 -04:00
}
void Stream::toBuffer(std::string& outBuffer)
2016-07-11 11:14:58 -04:00
{
outBuffer.clear();
outBuffer.append(this->data(), this->length());
2016-07-11 11:14:58 -04:00
}
std::string Stream::toBuffer()
2016-07-11 11:14:58 -04:00
{
std::string _buffer;
this->toBuffer(_buffer);
return _buffer;
2016-07-11 11:14:58 -04:00
}
void Stream::enterCriticalSection()
2016-07-11 11:14:58 -04:00
{
++this->criticalSectionState;
2016-07-11 11:14:58 -04:00
}
void Stream::leaveCriticalSection()
2016-07-11 11:14:58 -04:00
{
--this->criticalSectionState;
2016-07-11 11:14:58 -04:00
}
bool Stream::isCriticalSection()
2016-07-11 11:14:58 -04:00
{
if (this->criticalSectionState < 0)
2016-07-11 11:14:58 -04:00
{
MessageBoxA(0, "CriticalSectionState in stream has been overrun!", "ERROR", MB_ICONERROR);
__debugbreak();
}
return (this->criticalSectionState != 0);
2016-07-11 11:14:58 -04:00
}
#ifdef DEBUG
FILE* Stream::writeLog = nullptr;
int Stream::structLevel = 0;
void Stream::enterStruct(const char* structName)
{
if(!this->writeLog) return;
fprintf(this->writeLog, "%*s%s\n", this->structLevel++, "", structName);
}
void Stream::leaveStruct()
{
if(!this->writeLog) return;
this->structLevel--;
if(this->structLevel < 0) {
Components::Logger::Print("Stream::exitStruct underflow! All following writes will not be logged!\n");
fclose(this->writeLog);
this->writeLog = nullptr;
return;
}
fprintf(this->writeLog, "%*s-----\n", this->structLevel, "");
}
void Stream::logWrite(int writeLen)
{
if(!this->writeLog) return;
fprintf(this->writeLog, "%*s%d\n", this->structLevel, "", writeLen);
}
#endif
2016-07-11 11:14:58 -04:00
}