diff --git a/src/public/tier0/binstream.h b/src/public/tier0/binstream.h index f13a44cc..82e02cbf 100644 --- a/src/public/tier0/binstream.h +++ b/src/public/tier0/binstream.h @@ -3,32 +3,39 @@ class CIOStream { public: - enum Mode_t + enum class Mode_e { - NONE = 0, - READ = std::ios::in, - WRITE = std::ios::out, - BINARY = std::ios::binary, + None = 0, + Read, + Write, + ReadWrite, // For existing files only. + ReadWriteCreate }; CIOStream(); ~CIOStream(); - bool Open(const char* const pFilePath, const int nFlags); + bool Open(const char* const filePath, const Mode_e mode); + inline bool Open(const std::string& filePath, const Mode_e mode) { return Open(filePath.c_str(), mode); }; + void Close(); + void Reset(); void Flush(); - std::streampos TellGet(); - std::streampos TellPut(); + std::streamoff TellGet(); + std::streamoff TellPut(); - void SeekGet(const std::streampos nOffset); - void SeekPut(const std::streampos nOffset); - void Seek(const std::streampos nOffset); + void SeekGet(const std::streamoff offset, const std::ios_base::seekdir way = std::ios::beg); + void SeekPut(const std::streamoff offset, const std::ios_base::seekdir way = std::ios::beg); + void Seek(const std::streamoff offset, const std::ios_base::seekdir way = std::ios::beg); const std::filebuf* GetData() const; - const std::streampos GetSize() const; + const std::streamoff GetSize() const; - bool IsReadable(); + bool IsReadMode() const; + bool IsWriteMode() const; + + bool IsReadable() const; bool IsWritable() const; bool IsEof() const; @@ -37,39 +44,39 @@ public: // Purpose: reads any value from the file //----------------------------------------------------------------------------- template - void Read(T& tValue) + inline void Read(T& value) { if (IsReadable()) - m_Stream.read(reinterpret_cast(&tValue), sizeof(tValue)); + m_stream.read(reinterpret_cast(&value), sizeof(value)); } //----------------------------------------------------------------------------- // Purpose: reads any value from the file with specified size //----------------------------------------------------------------------------- template - void Read(T* tValue, const size_t nSize) + inline void Read(T* const value, const size_t size) { if (IsReadable()) - m_Stream.read(reinterpret_cast(tValue), nSize); + m_stream.read(reinterpret_cast(value), size); } template - void Read(T& tValue, const size_t nSize) + inline void Read(T& value, const size_t size) { if (IsReadable()) - m_Stream.read(reinterpret_cast(&tValue), nSize); + m_stream.read(reinterpret_cast(&value), size); } //----------------------------------------------------------------------------- // Purpose: reads any value from the file and returns it //----------------------------------------------------------------------------- template - T Read() + inline T Read() { T value{}; if (!IsReadable()) return value; - m_Stream.read(reinterpret_cast(&value), sizeof(value)); + m_stream.read(reinterpret_cast(&value), sizeof(value)); return value; } bool ReadString(std::string& svOut); @@ -79,31 +86,40 @@ public: // Purpose: writes any value to the file //----------------------------------------------------------------------------- template - void Write(T tValue) + inline void Write(const T& value) { if (!IsWritable()) return; - m_Stream.write(reinterpret_cast(&tValue), sizeof(tValue)); - m_nSize += sizeof(tValue); + const size_t count = sizeof(value); + + m_stream.write(reinterpret_cast(&value), count); + CalcAddDelta(count); } //----------------------------------------------------------------------------- // Purpose: writes any value to the file with specified size //----------------------------------------------------------------------------- template - void Write(T* tValue, size_t nSize) + inline void Write(const T* const value, const size_t size) { if (!IsWritable()) return; - m_Stream.write(reinterpret_cast(tValue), nSize); - m_nSize += nSize; + m_stream.write(reinterpret_cast(value), size); + CalcAddDelta(size); } - bool WriteString(const std::string& svInput); + bool WriteString(const std::string& svInput, const bool nullterminate); + void Pad(const size_t count); + +protected: + void CalcAddDelta(const size_t count); + void CalcSkipDelta(const std::streamoff offset, const std::ios_base::seekdir way); private: - std::streampos m_nSize; // File size. - int m_nFlags; // Stream flags. - std::fstream m_Stream; // I/O stream. + std::fstream m_stream; // I/O stream. + std::streamoff m_size; // File size. + std::streamoff m_skip; // Amount skipped back. + std::ios_base::openmode m_flags; // Stream flags. + Mode_e m_mode; // Stream mode. }; diff --git a/src/rtech/pak/pakdecode.cpp b/src/rtech/pak/pakdecode.cpp index 847091f6..c42ac4de 100644 --- a/src/rtech/pak/pakdecode.cpp +++ b/src/rtech/pak/pakdecode.cpp @@ -807,7 +807,7 @@ bool Pak_DecodePakFile(const char* const inPakFile, const char* const outPakFile CIOStream inPakStream; - if (!inPakStream.Open(inPakFile, CIOStream::READ | CIOStream::BINARY)) + if (!inPakStream.Open(inPakFile, CIOStream::Mode_e::Read)) { Error(eDLL_T::RTECH, NO_ERROR, "%s: failed to open pak file '%s' for read!\n", __FUNCTION__, inPakFile); @@ -817,7 +817,7 @@ bool Pak_DecodePakFile(const char* const inPakFile, const char* const outPakFile CIOStream outPakStream; - if (!outPakStream.Open(outPakFile, CIOStream::WRITE | CIOStream::BINARY)) + if (!outPakStream.Open(outPakFile, CIOStream::Mode_e::Write)) { Error(eDLL_T::RTECH, NO_ERROR, "%s: failed to open pak file '%s' for write!\n", __FUNCTION__, outPakFile); diff --git a/src/rtech/pak/pakencode.cpp b/src/rtech/pak/pakencode.cpp index 8f9fb70c..f0025450 100644 --- a/src/rtech/pak/pakencode.cpp +++ b/src/rtech/pak/pakencode.cpp @@ -84,7 +84,7 @@ bool Pak_EncodePakFile(const char* const inPakFile, const char* const outPakFile CIOStream inPakStream; - if (!inPakStream.Open(inPakFile, CIOStream::READ | CIOStream::BINARY)) + if (!inPakStream.Open(inPakFile, CIOStream::Mode_e::Read)) { Error(eDLL_T::RTECH, NO_ERROR, "%s: failed to open pak file '%s' for read!\n", __FUNCTION__, inPakFile); @@ -94,7 +94,7 @@ bool Pak_EncodePakFile(const char* const inPakFile, const char* const outPakFile CIOStream outPakStream; - if (!outPakStream.Open(outPakFile, CIOStream::WRITE | CIOStream::BINARY)) + if (!outPakStream.Open(outPakFile, CIOStream::Mode_e::Write)) { Error(eDLL_T::RTECH, NO_ERROR, "%s: failed to open pak file '%s' for write!\n", __FUNCTION__, outPakFile); diff --git a/src/rtech/pak/paktools.cpp b/src/rtech/pak/paktools.cpp index 0544e6f0..646ff81f 100644 --- a/src/rtech/pak/paktools.cpp +++ b/src/rtech/pak/paktools.cpp @@ -365,7 +365,7 @@ bool Pak_UpdatePatchHeaders(uint8_t* const inBuf, const char* const outPakFile) // unable to open patch while there should be one, we must calculate // new file sizes here, or else the runtime would fail to load them - if (!inPatch.Open(patchFile, CIOStream::READ | CIOStream::BINARY)) + if (!inPatch.Open(patchFile, CIOStream::Mode_e::Read)) return false; const size_t fileSize = inPatch.GetSize(); diff --git a/src/sdklauncher/sdklauncher.cpp b/src/sdklauncher/sdklauncher.cpp index 3cb83551..c7e0e497 100644 --- a/src/sdklauncher/sdklauncher.cpp +++ b/src/sdklauncher/sdklauncher.cpp @@ -303,7 +303,7 @@ void CLauncher::SetupLaunchContext(const char* szConfig, const char* szGameDll, { cfgFileName.Format(GAME_CFG_PATH"%s", szConfig); - if (cfgFile.Open(cfgFileName.String(), CIOStream::READ)) + if (cfgFile.Open(cfgFileName.String(), CIOStream::Mode_e::Read)) { if (!cfgFile.ReadString(commandLine.Access(), commandLine.GetMaxLength())) { diff --git a/src/tier0/binstream.cpp b/src/tier0/binstream.cpp index 73f04a66..3249e55e 100644 --- a/src/tier0/binstream.cpp +++ b/src/tier0/binstream.cpp @@ -6,8 +6,7 @@ //----------------------------------------------------------------------------- CIOStream::CIOStream() { - m_nSize = 0; - m_nFlags = Mode_t::NONE; + Reset(); } //----------------------------------------------------------------------------- @@ -15,53 +14,85 @@ CIOStream::CIOStream() //----------------------------------------------------------------------------- CIOStream::~CIOStream() { - if (m_Stream.is_open()) - m_Stream.close(); - if (m_Stream.is_open()) - m_Stream.close(); + Close(); +} + +//----------------------------------------------------------------------------- +// Purpose: get internal stream mode from selected mode +//----------------------------------------------------------------------------- +static std::ios_base::openmode GetInternalStreamMode(const CIOStream::Mode_e mode) +{ + switch (mode) + { + case CIOStream::Mode_e::Read: + return (std::ios::in | std::ios::binary); + case CIOStream::Mode_e::Write: + return (std::ios::out | std::ios::binary); + case CIOStream::Mode_e::ReadWrite: + return (std::ios::in | std::ios::out | std::ios::binary); + case CIOStream::Mode_e::ReadWriteCreate: + return (std::ios::in | std::ios::out | std::ios::binary | std::ios::trunc); + } + + assert(0); // code bug, can never reach this. + return 0; } //----------------------------------------------------------------------------- // Purpose: opens the file in specified mode -// Input : *pFilePath - -// nFlags - +// Input : *filePath - +// mode - // Output : true if operation is successful //----------------------------------------------------------------------------- -bool CIOStream::Open(const char* const pFilePath, const int nFlags) +bool CIOStream::Open(const char* const filePath, const Mode_e mode) { - m_nFlags = nFlags; + m_flags = GetInternalStreamMode(mode); + m_mode = mode; - if (m_Stream.is_open()) + if (m_stream.is_open()) { - m_Stream.close(); + m_stream.close(); } - m_Stream.open(pFilePath, nFlags); - if (!m_Stream.is_open() || !m_Stream.good()) + + m_stream.open(filePath, GetInternalStreamMode(mode)); + + if (!m_stream.is_open() || !m_stream.good()) { - m_nFlags = Mode_t::NONE; return false; } - if (nFlags & Mode_t::READ) + if (IsReadMode()) { struct _stat64 status; - if (_stat64(pFilePath, &status) != NULL) + if (_stat64(filePath, &status) != NULL) { return false; } - m_nSize = status.st_size; + m_size = status.st_size; } return true; } +//----------------------------------------------------------------------------- +// Purpose: resets the state +//----------------------------------------------------------------------------- +void CIOStream::Reset() +{ + m_size = 0; + m_skip = 0; + m_mode = Mode_e::None; + m_flags = 0; +} + //----------------------------------------------------------------------------- // Purpose: closes the stream //----------------------------------------------------------------------------- void CIOStream::Close() { - m_Stream.close(); + m_stream.close(); + Reset(); } //----------------------------------------------------------------------------- @@ -70,67 +101,88 @@ void CIOStream::Close() void CIOStream::Flush() { if (IsWritable()) - m_Stream.flush(); + m_stream.flush(); } //----------------------------------------------------------------------------- // Purpose: gets the position of the current character in the stream -// Input : mode - // Output : std::streampos //----------------------------------------------------------------------------- -std::streampos CIOStream::TellGet() +std::streamoff CIOStream::TellGet() { - return m_Stream.tellg(); + assert(IsReadMode()); + return m_stream.tellg(); } -std::streampos CIOStream::TellPut() +std::streamoff CIOStream::TellPut() { - return m_Stream.tellp(); + assert(IsWriteMode()); + return m_stream.tellp(); } //----------------------------------------------------------------------------- // Purpose: sets the position of the current character in the stream -// Input : nOffset - -// mode - +// Input : offset - +// way - //----------------------------------------------------------------------------- -void CIOStream::SeekGet(const std::streampos nOffset) +void CIOStream::SeekGet(const std::streamoff offset, const std::ios_base::seekdir way) { - m_Stream.seekg(nOffset, std::ios::beg); + assert(IsReadMode()); + m_stream.seekg(offset, way); } -void CIOStream::SeekPut(const std::streampos nOffset) +//----------------------------------------------------------------------------- +// NOTE: if you seek beyond the end of the file to try and pad it out, use the +// Pad() method instead as the behavior of seek is operating system dependent +//----------------------------------------------------------------------------- +void CIOStream::SeekPut(const std::streamoff offset, const std::ios_base::seekdir way) { - m_Stream.seekp(nOffset, std::ios::beg); + assert(IsWriteMode()); + + CalcSkipDelta(offset, way); + m_stream.seekp(offset, way); } -void CIOStream::Seek(const std::streampos nOffset) +void CIOStream::Seek(const std::streamoff offset, const std::ios_base::seekdir way) { - SeekGet(nOffset); - SeekPut(nOffset); + if (IsReadMode()) + SeekGet(offset, way); + if (IsWriteMode()) + SeekPut(offset, way); } //----------------------------------------------------------------------------- -// Purpose: returns the data (ifstream only) +// Purpose: returns the data // Output : std::filebuf* //----------------------------------------------------------------------------- const std::filebuf* CIOStream::GetData() const { - return m_Stream.rdbuf(); + return m_stream.rdbuf(); } //----------------------------------------------------------------------------- -// Purpose: returns the data size (ifstream only) +// Purpose: returns the data size // Output : std::streampos //----------------------------------------------------------------------------- -const std::streampos CIOStream::GetSize() const +const std::streamoff CIOStream::GetSize() const { - return m_nSize; + return m_size; +} + +bool CIOStream::IsReadMode() const +{ + return (m_flags & std::ios::in); +} + +bool CIOStream::IsWriteMode() const +{ + return (m_flags & std::ios::out); } //----------------------------------------------------------------------------- // Purpose: checks if we are able to read the file // Output : true on success, false otherwise //----------------------------------------------------------------------------- -bool CIOStream::IsReadable() +bool CIOStream::IsReadable() const { - if (!(m_nFlags & Mode_t::READ) || !m_Stream || m_Stream.eof()) + if (!IsReadMode() || !m_stream || m_stream.eof()) return false; return true; @@ -142,7 +194,7 @@ bool CIOStream::IsReadable() //----------------------------------------------------------------------------- bool CIOStream::IsWritable() const { - if (!(m_nFlags & Mode_t::WRITE) || !m_Stream) + if (!IsWriteMode() || !m_stream) return false; return true; @@ -154,7 +206,7 @@ bool CIOStream::IsWritable() const //----------------------------------------------------------------------------- bool CIOStream::IsEof() const { - return m_Stream.eof(); + return m_stream.eof(); } //----------------------------------------------------------------------------- @@ -162,69 +214,168 @@ bool CIOStream::IsEof() const // Input : &svOut - // Output : true on success, false otherwise //----------------------------------------------------------------------------- -bool CIOStream::ReadString(std::string& svOut) +bool CIOStream::ReadString(std::string& out) { - if (IsReadable()) + if (!IsReadable()) + return false; + + while (!m_stream.eof()) { - while (!m_Stream.eof()) - { - const char c = Read(); + const char c = Read(); - if (c == '\0') - break; + if (c == '\0') + break; - svOut += c; - } - - return true; + out += c; } - return false; + return true; } //----------------------------------------------------------------------------- // Purpose: reads a string from the file into a fixed size buffer -// Input : *pBuf - -// nLen - +// Input : *buf - +// len - // Output : true on success, false otherwise //----------------------------------------------------------------------------- -bool CIOStream::ReadString(char* const pBuf, const size_t nLen) +bool CIOStream::ReadString(char* const buf, const size_t len) { - if (IsReadable()) + if (!IsReadable()) + return false; + + size_t i = 0; + + while (i < len && !m_stream.eof()) { - size_t i = 0; + const char c = Read(); - while (i < nLen && !m_Stream.eof()) - { - const char c = Read(); + if (c == '\0') + break; - if (c == '\0') - break; - - pBuf[i++] = c; - } - - return true; + buf[i++] = c; } - return false; + return true; } //----------------------------------------------------------------------------- // Purpose: writes a string to the file -// Input : &svInput - +// Input : &input - // Output : true on success, false otherwise //----------------------------------------------------------------------------- -bool CIOStream::WriteString(const std::string& svInput) +bool CIOStream::WriteString(const std::string& input, const bool nullterminate) { if (!IsWritable()) return false; - const char* const szText = svInput.c_str(); - const size_t nSize = svInput.size(); + const char* const text = input.c_str(); + const size_t len = input.length() + nullterminate; - m_Stream.write(szText, nSize); - m_nSize += nSize; + m_stream.write(text, len); + CalcAddDelta(len); return true; } + +// limit number of io calls and allocations by just using this static buffer +// for padding out the stream. +static constexpr size_t PAD_BUF_SIZE = 4096; +const static char s_padBuf[PAD_BUF_SIZE]; + +//----------------------------------------------------------------------------- +// Purpose: pads the out stream up to count bytes +// Input : count - +//----------------------------------------------------------------------------- +void CIOStream::Pad(const size_t count) +{ + assert(count > 0); + size_t remainder = count; + + while (remainder) + { + const size_t writeCount = (std::min)(count, PAD_BUF_SIZE); + Write(s_padBuf, writeCount); + + remainder -= writeCount; + } +} + +//----------------------------------------------------------------------------- +// Purpose: makes sure that the size gets incremented if we exceeded the end of +// the stream with the delta amount +//----------------------------------------------------------------------------- +void CIOStream::CalcAddDelta(const size_t count) +{ + if (m_skip > 0) + { + m_skip -= count; + + if (m_skip < 0) + { + m_size += -m_skip; // Add the overshoot to the file size. + m_skip = 0; + } + } + else + m_size += count; +} + +//----------------------------------------------------------------------------- +// Purpose: if we seek backwards, and then write new data, we should not add +// this to the total output size of the stream as we modify and not +// add. we have to keep by how much we shifted backwards and advanced +// forward until we can start adding again. +//----------------------------------------------------------------------------- +void CIOStream::CalcSkipDelta(const std::streamoff offset, const std::ios_base::seekdir way) +{ + switch (way) + { + case std::ios_base::beg: + { + if (offset < 0) + { + assert(false && "Negative offset in std::ios_base::beg is invalid."); + return; + } + + if (offset > m_size) + { + m_size = offset; + m_skip = 0; + } + else + m_skip = m_size - offset; + break; + } + case std::ios_base::cur: + { + if (offset > 0) + CalcAddDelta(offset); + else + m_skip += -offset; + break; + } + case std::ios_base::end: + { + if (offset >= 0) + { + m_size += offset; + m_skip = 0; + } + else + m_skip += -offset; + break; + } + default: + assert(false && "Unsupported seek direction."); + break; + } + + // Ensure m_skip is non-negative, this can happen if you call this method + // with cur or end, and a negative value who's absolute value is greater + // than the total stream size. If you hit this, you have a bug somewhere. + assert(m_skip >= 0); + + if (m_skip < 0) + m_skip = 0; +} diff --git a/src/tier0/sigcache.cpp b/src/tier0/sigcache.cpp index 0f7704d0..9784df0e 100644 --- a/src/tier0/sigcache.cpp +++ b/src/tier0/sigcache.cpp @@ -93,7 +93,7 @@ bool CSigCache::ReadCache(const char* szCacheFile) } CIOStream reader; - if (!reader.Open(szCacheFile, CIOStream::READ | CIOStream::BINARY)) + if (!reader.Open(szCacheFile, CIOStream::Mode_e::Read)) { return false; } @@ -168,7 +168,7 @@ bool CSigCache::WriteCache(const char* szCacheFile) const } CIOStream writer; - if (!writer.Open(szCacheFile, CIOStream::WRITE | CIOStream::BINARY)) + if (!writer.Open(szCacheFile, CIOStream::Mode_e::Write)) { Error(eDLL_T::COMMON, NO_ERROR, "%s - Unable to write to '%s' (read-only?)\n", __FUNCTION__, szCacheFile);