From afad20020943f745c0c7743f1618be9db2715b92 Mon Sep 17 00:00:00 2001 From: Jordan Cristiano Date: Wed, 20 Apr 2016 21:18:56 -0400 Subject: [PATCH] WIP SVC_CreateStringTable data parsing --- demboyz/netmessages/svc_createstringtable.cpp | 113 +++++++++++++++++- external/sourcesdk/common.cpp | 72 +++++++++++ external/sourcesdk/include/sourcesdk/common.h | 8 ++ .../include/sourcesdk/valve_support.h | 18 +-- 4 files changed, 198 insertions(+), 13 deletions(-) create mode 100644 external/sourcesdk/common.cpp create mode 100644 external/sourcesdk/include/sourcesdk/common.h diff --git a/demboyz/netmessages/svc_createstringtable.cpp b/demboyz/netmessages/svc_createstringtable.cpp index 18a9446..702016d 100644 --- a/demboyz/netmessages/svc_createstringtable.cpp +++ b/demboyz/netmessages/svc_createstringtable.cpp @@ -5,6 +5,93 @@ #include "netmath.h" #include "netcontants.h" +// #define WIP_STRINGTABLE + +#ifdef WIP_STRINGTABLE +#include "sourcesdk/common.h" +#include + +#define SUBSTRING_BITS 5 +struct StringHistoryEntry +{ + char string[(1 << SUBSTRING_BITS)]; +}; + +static void StringTable_BitRead(NetHandlers::BitRead& bitbuf, SourceGameContext& context, NetMsg::SVC_CreateStringTable* data) +{ + const size_t numEncodeBits = math::log2(data->maxEntries); + std::vector history; + int lastEntry = -1; + for (uint i = 0; i < data->numEntries; ++i) + { + int entryIndex = lastEntry + 1; + + if (bitbuf.ReadOneBit() == 0) + { + entryIndex = bitbuf.ReadUBitLong(numEncodeBits); + } + lastEntry = entryIndex; + + const char *pEntry = NULL; + char entry[1024]; + char substr[1024]; + if (bitbuf.ReadOneBit() != 0) + { + bool substringcheck = bitbuf.ReadOneBit() != 0; + if (substringcheck) + { + int index = bitbuf.ReadUBitLong(5); + int bytestocopy = bitbuf.ReadUBitLong(SUBSTRING_BITS); + strncpy(entry, history.at(index).string, bytestocopy + 1); + entry[bytestocopy + 1] = '\0'; + bitbuf.ReadString(substr, sizeof(substr)); + strncat(entry, substr, sizeof(entry)); + } + else + { + bitbuf.ReadString(entry, sizeof(entry)); + } + pEntry = entry; + printf("%s\n", pEntry); + } + const int MAX_USERDATA_BITS = 14; + unsigned char tempbuf[(1 << MAX_USERDATA_BITS)] = { 0 }; + const void *pUserData = NULL; + int nBytes = 0; + if (bitbuf.ReadOneBit() != 0) + { + if (data->isUserDataFixedSize) + { + nBytes = data->userDataSize; + tempbuf[nBytes - 1] = 0; + bitbuf.ReadBits(tempbuf, data->userDataSizeBits); + } + else + { + nBytes = bitbuf.ReadUBitLong(MAX_USERDATA_BITS); + bitbuf.ReadBytes(tempbuf, nBytes); + } + pUserData = tempbuf; + } + + if (pEntry == NULL) + { + pEntry = ""; + } + + if (history.size() > 31) + { + history.erase(history.begin()); + } + + StringHistoryEntry she; + strncpy(she.string, pEntry, sizeof(she.string)); + history.emplace_back(she); + } +} + +#endif // WIP_STRINGTABLE + namespace NetHandlers { bool SVC_CreateStringTable_BitRead_Internal(BitRead& bitbuf, SourceGameContext& context, NetMsg::SVC_CreateStringTable* data) @@ -20,7 +107,9 @@ namespace NetHandlers } bitbuf.ReadString(data->tableName, sizeof(data->tableName)); data->maxEntries = bitbuf.ReadWord(); - data->numEntries = bitbuf.ReadUBitLong(math::log2(data->maxEntries) + 1); + + const size_t numEncodeBits = math::log2(data->maxEntries); + data->numEntries = bitbuf.ReadUBitLong(numEncodeBits + 1); if (context.protocol > 23) { data->dataLengthInBits = bitbuf.ReadVarInt32(); @@ -46,6 +135,28 @@ namespace NetHandlers } data->data.reset(new uint8_t[math::BitsToBytes(data->dataLengthInBits)]); bitbuf.ReadBits(data->data.get(), data->dataLengthInBits); + +#ifdef WIP_STRINGTABLE + if (data->compressedData) + { + bf_read bitbuf2(data->data.get(), data->dataLengthInBits); + const uint32_t decompressedNumBytes = bitbuf2.ReadUBitLong(32); + const uint32_t compressedNumBytes = bitbuf2.ReadUBitLong(32); + std::unique_ptr compressedData(new uint8[compressedNumBytes]); + std::unique_ptr uncompressedData(new uint8[decompressedNumBytes]); + bitbuf2.ReadBytes(compressedData.get(), compressedNumBytes); + + uint32_t numWritten = COM_BufferToBufferDecompress(uncompressedData.get(), decompressedNumBytes, compressedData.get(), compressedNumBytes); + bitbuf2 = bf_read(uncompressedData.get(), decompressedNumBytes); + StringTable_BitRead(bitbuf2, context, data); + } + else + { + bf_read bitbuf2(data->data.get(), data->dataLengthInBits); + StringTable_BitRead(bitbuf2, context, data); + } +#endif // WIP_STRINGTABLE + return !bitbuf.IsOverflowed(); } diff --git a/external/sourcesdk/common.cpp b/external/sourcesdk/common.cpp new file mode 100644 index 0000000..4a8b165 --- /dev/null +++ b/external/sourcesdk/common.cpp @@ -0,0 +1,72 @@ + +#include "sourcesdk/common.h" +#include "snappy/snappy.h" +#include + +static const uint32 INVALID = static_cast(-1); +static const uint32 LZSS_ID = BigLong(('L' << 24) | ('Z' << 16) | ('S' << 8) | ('S')); +static const uint32 SNAPPY_ID = BigLong(('S' << 24) | ('N' << 16) | ('A' << 8) | ('P')); + +inline uint32_t U8ToU32(const uint8* from) +{ + union U8ToU32 + { + uint8 bytes[4]; + uint32 dword; + }; + return reinterpret_cast(from)->dword; +} + +uint32 COM_BufferToBufferDecompress(uint8* dest, uint32 destLen, const uint8* src, uint32 srcLen) +{ + const uint32 uncompressedLen = COM_GetUncompressedSize(src, srcLen); + if (uncompressedLen == INVALID) + { + if (srcLen <= destLen) + { + memcpy(dest, src, srcLen); + } + } + else if (uncompressedLen <= destLen) + { + const uint32 id = U8ToU32(src); + if (id == LZSS_ID) + { + assert(false); + } + else if (id == SNAPPY_ID) + { + snappy::RawUncompress(reinterpret_cast(src + 4), srcLen - 4, reinterpret_cast(dest)); + } + } + return (uncompressedLen != INVALID) ? uncompressedLen : 0; +} + +uint32 COM_GetUncompressedSize(const uint8* data, uint32 numBytes) +{ + size_t uncompressedLength = INVALID; + if (numBytes > 4) + { + const uint32 magic = U8ToU32(data); + if (numBytes >= 8 && magic == LZSS_ID) + { + return U8ToU32(data + 4); + } + + if (magic == SNAPPY_ID) + { + snappy::GetUncompressedLength(reinterpret_cast(data + 4), numBytes - 4, &uncompressedLength); + } + } + return static_cast(uncompressedLength); +} + +uint32 COM_GetIdealDestinationCompressionBufferSize_Snappy(uint32 srcLen) +{ + return snappy::MaxCompressedLength(srcLen) + sizeof(uint32); +} + +/*uint32 COM_CompressBuffer_Snappy(const uint8* src, uint32 srcLen, uint8* dest, uint32 destLen) +{ + return COM_GetIdealDestinationCompressionBufferSize_Snappy(srcLen); +}*/ diff --git a/external/sourcesdk/include/sourcesdk/common.h b/external/sourcesdk/include/sourcesdk/common.h new file mode 100644 index 0000000..9fa1820 --- /dev/null +++ b/external/sourcesdk/include/sourcesdk/common.h @@ -0,0 +1,8 @@ + +#include "valve_support.h" + +uint32 COM_BufferToBufferDecompress(uint8* dest, uint32 destLen, const uint8* src, uint32 srcLen); +uint32 COM_GetUncompressedSize(const uint8* data, uint32 numBytes); + +uint32 COM_GetIdealDestinationCompressionBufferSize_Snappy(uint32 srcLen); +uint32 COM_CompressBuffer_Snappy(const uint8* src, uint32 srcLen, uint8* dest, uint32 destLen); diff --git a/external/sourcesdk/include/sourcesdk/valve_support.h b/external/sourcesdk/include/sourcesdk/valve_support.h index 5755a18..ad3359b 100644 --- a/external/sourcesdk/include/sourcesdk/valve_support.h +++ b/external/sourcesdk/include/sourcesdk/valve_support.h @@ -59,17 +59,6 @@ inline bool is_little_endian() #define NORMAL_DENOMINATOR ( (1<<(NORMAL_FRACTIONAL_BITS)) - 1 ) #define NORMAL_RESOLUTION (1.0/(NORMAL_DENOMINATOR)) -template -inline T DWordSwapC( T dw ) -{ - uint32 temp; - temp = *((uint32 *)&dw) >> 24; - temp |= ((*((uint32 *)&dw) & 0x00FF0000) >> 8); - temp |= ((*((uint32 *)&dw) & 0x0000FF00) << 8); - temp |= ((*((uint32 *)&dw) & 0x000000FF) << 24); - return *((T*)&temp); -} - #if defined( _MSC_VER ) && !defined( PLATFORM_WINDOWS_PC64 ) #define DWordSwap DWordSwapAsm #pragma warning(push) @@ -85,7 +74,7 @@ inline T DWordSwapC( T dw ) } #pragma warning(pop) #else - #define DWordSwap DWordSwapC + #error write the 64 bit swaps #endif inline unsigned long LoadLittleDWord(const unsigned long *base, unsigned int dwordIndex) @@ -114,6 +103,11 @@ inline void LittleFloat(float* pOut, float* pIn) } } +inline long BigLong(long val) +{ + return is_little_endian() ? DWordSwap(val) : val; +} + #define BITS_PER_INT 32 inline int GetBitForBitnum( int bitNum )