From 78de8690abbdfc591a2049bf78eb03caeb0588eb Mon Sep 17 00:00:00 2001 From: Jordan Cristiano Date: Tue, 12 May 2015 20:39:56 -0400 Subject: [PATCH] Added demo writer interface, restructure --- demboyz/demboyz.cpp | 140 +++--- demboyz/demofile.cpp | 303 ++++--------- demboyz/demofile.h | 290 ++---------- demboyz/demofilebitbuf.cpp | 691 ----------------------------- demboyz/demofilebitbuf.h | 263 ----------- demboyz/demoreader.h | 10 + demboyz/demotypes.h | 180 ++++++++ demboyz/demowriter.cpp | 28 ++ demboyz/demreader.cpp | 72 +++ demboyz/idemowriter.h | 48 ++ demboyz/jsonreader.cpp | 6 + demboyz/jsonwriter.cpp | 24 + external/sourcesdk/valve_support.h | 3 +- premake/demboyz.lua | 20 +- 14 files changed, 564 insertions(+), 1514 deletions(-) delete mode 100644 demboyz/demofilebitbuf.cpp delete mode 100644 demboyz/demofilebitbuf.h create mode 100644 demboyz/demoreader.h create mode 100644 demboyz/demotypes.h create mode 100644 demboyz/demowriter.cpp create mode 100644 demboyz/demreader.cpp create mode 100644 demboyz/idemowriter.h create mode 100644 demboyz/jsonreader.cpp create mode 100644 demboyz/jsonwriter.cpp diff --git a/demboyz/demboyz.cpp b/demboyz/demboyz.cpp index 0e9638a..1e6e0bc 100644 --- a/demboyz/demboyz.cpp +++ b/demboyz/demboyz.cpp @@ -1,90 +1,100 @@ -#include "demofile.h" -#include "demofilebitbuf.h" -#include "netmessages.h" -#include -#include +#include "idemowriter.h" +#include "demoreader.h" +#include +#include -void ParsePacket(const std::vector& packet) +std::string GetExtension(const std::string& filename) { - assert(packet.size() <= NET_MAX_PAYLOAD); - CBitRead bitbuf(packet.data(), packet.size()); - while (bitbuf.GetNumBitsLeft() >= NETMSG_TYPE_BITS) + size_t index = filename.find_last_of("."); + if (index != std::string::npos) { - uint32 typeId = bitbuf.ReadUBitLong(NETMSG_TYPE_BITS); - printf("%i\n", typeId); - ProcessNetMsg(typeId, bitbuf); + return filename.substr(index + 1); } + return std::string(); } -void ParseDemoSequence(const std::vector& sequenceData) +enum class FileType { - unsigned char cmd; - int32 tick; - int32 sequenceInfo1; - int32 sequenceInfo2; - democmdinfo_t cmdInfo; - std::vector buffer; + None, + Dem, + Json +}; - DemoSequenceReader reader(sequenceData); - for (;;) +FileType GetFileType(const std::string& filename) +{ + std::string ext = GetExtension(filename); + if (ext == "dem") { - reader.ReadCmdHeader(cmd, tick); - switch (cmd) - { - case dem_signon: - case dem_packet: - reader.ReadCmdInfo(cmdInfo); - reader.ReadSequenceInfo(sequenceInfo1, sequenceInfo2); - assert(sequenceInfo1 == sequenceInfo2); - reader.ReadRawData(buffer); - ParsePacket(buffer); - break; - case dem_synctick: - // nothing - break; - case dem_consolecmd: - reader.ReadRawData(nullptr, 1024); - break; - case dem_usercmd: - reader.ReadUserCmd(buffer, 256); - break; - case dem_datatables: - // TODO: datatables - reader.ReadRawData(nullptr, 64*1024); - break; - case dem_stop: - // TODO assert frame and tick numbers - break; - case dem_customdata: - reader.ReadRawData(nullptr, 0); - break; - case dem_stringtables: - reader.ReadRawData(nullptr, 0); - break; - default: - assert(false); - break; - } + return FileType::Dem; } + if (ext == "json") + { + return FileType::Json; + } + return FileType::None; } int main(const int argc, const char* argv[]) { - if (argc < 2) + if (argc != 3) { + fprintf(stderr, "Usage: %s .dem/json .dem/json\n", argv[0]); return -1; } - CDemoFile demoFile; - if (!demoFile.Open(argv[1])) + std::string inputFile(argv[1]); + std::string outputFile(argv[2]); + + FileType inputType = GetFileType(inputFile); + FileType outputType = GetFileType(outputFile); + if (inputType == FileType::None) { + fprintf(stderr, "Error: Bad type for input file\n"); + return -1; + } + if (outputType == FileType::None) + { + fprintf(stderr, "Error: Bad type for output file\n"); return -1; } - auto demoHeader = demoFile.GetDemoHeader(); - ParseDemoSequence(demoFile.GetSignOnData()); - ParseDemoSequence(demoFile.GetDemoData()); - demoFile.Close(); + FILE* inputFp = fopen(inputFile.c_str(), "rb"); + if (!inputFp) + { + fprintf(stderr, "Error: Could not open input file\n"); + return -1; + } + + FILE* outputFp = fopen(outputFile.c_str(), "wb"); + if (!outputFp) + { + fprintf(stderr, "Error: Could not open input file\n"); + fclose(inputFp); + return -1; + } + + IDemoWriter* writer; + if (outputType == FileType::Dem) + { + writer = IDemoWriter::CreateDemoWriter(outputFp); + } + else + { + writer = IDemoWriter::CreateJsonWriter(outputFp); + } + + if (inputType == FileType::Dem) + { + DemoReader::ProcessDem(inputFp, writer); + } + else + { + DemoReader::ProcessJson(inputFp, writer); + } + fclose(inputFp); + fclose(outputFp); + + IDemoWriter::FreeDemoWriter(writer); return 0; } diff --git a/demboyz/demofile.cpp b/demboyz/demofile.cpp index bbc9649..da0d1c3 100644 --- a/demboyz/demofile.cpp +++ b/demboyz/demofile.cpp @@ -1,260 +1,121 @@ -//====== Copyright (c) 2014, Valve Corporation, All rights reserved. ========// -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -//===========================================================================// -#include -#include -#include #include "demofile.h" +#include "demotypes.h" +#include -DemoSequenceReader::DemoSequenceReader(const std::vector& sequenceData): - m_sequenceData(sequenceData), - m_dataReadOffset(0) +// DemoFileReader + +DemoFileReader::DemoFileReader(FILE* fp): + m_demoFp(fp) { } -int32 DemoSequenceReader::ReadRawData(char *buffer, int32 length) +int32_t DemoFileReader::ReadRawData(uint8_t* buffer, int32_t length) { - if (m_sequenceData.empty()) - { - return 0; - } + FILE* fp = m_demoFp; - const unsigned char* sequenceData = m_sequenceData.data(); - size_t currentReadOffset = m_dataReadOffset; - - // read length of data block - const int32 size = *reinterpret_cast(sequenceData + currentReadOffset); - currentReadOffset += sizeof(int32); + int32_t size; + fread(&size, sizeof(int32_t), 1, fp); if (buffer && (length < size)) { - fprintf(stderr, "CDemoFile::ReadRawData: buffer overflow (%i).\n", size); return -1; } if (buffer) { - // read data into buffer - memcpy(buffer, sequenceData + currentReadOffset, size); - } - currentReadOffset += size; - - m_dataReadOffset = currentReadOffset; - return size; -} - -bool DemoSequenceReader::ReadRawData(std::vector& buf, - const int32 maxReadSize /*= MAX_READ_SIZE*/) -{ - if (m_sequenceData.empty()) - { - return false; - } - - const unsigned char* sequenceData = m_sequenceData.data(); - size_t currentReadOffset = m_dataReadOffset; - - // read length of data block - const int32 size = *reinterpret_cast(sequenceData + currentReadOffset); - currentReadOffset += sizeof(int32); - - if (size < 0) - { - fprintf(stderr, "DemoSequenceReader::ReadRawData: invalid size (%i).\n", size); - return false; - } - if (maxReadSize < 0 || size > maxReadSize) - { - fprintf(stderr, "DemoSequenceReader::ReadRawData: invalid size (%i) with max (%i).\n", size, maxReadSize); - return false; - } - - buf.resize(size); - - // read data into buffer - memcpy(buf.data(), sequenceData + currentReadOffset, size); - currentReadOffset += size; - - m_dataReadOffset = currentReadOffset; - return true; -} - -void DemoSequenceReader::ReadSequenceInfo(int32 &nSeqNrIn, int32 &nSeqNrOut) -{ - if (m_sequenceData.empty()) - { - return; - } - - const unsigned char* sequenceData = m_sequenceData.data(); - size_t currentReadOffset = m_dataReadOffset; - - nSeqNrIn = *reinterpret_cast(sequenceData + currentReadOffset); - currentReadOffset += sizeof(int32); - nSeqNrOut = *reinterpret_cast(sequenceData + currentReadOffset); - currentReadOffset += sizeof(int32); - - m_dataReadOffset = currentReadOffset; -} - -void DemoSequenceReader::ReadCmdInfo(democmdinfo_t& info) -{ - if (m_sequenceData.empty()) - { - return; - } - - size_t currentReadOffset = m_dataReadOffset; - - memcpy(&info, m_sequenceData.data() + currentReadOffset, sizeof(democmdinfo_t)); - currentReadOffset += sizeof(democmdinfo_t); - - m_dataReadOffset = currentReadOffset; -} - -void DemoSequenceReader::ReadCmdHeader(unsigned char& cmd, int32& tick) -{ - if (m_sequenceData.empty()) - { - return; - } - - const unsigned char* sequenceData = m_sequenceData.data(); - size_t currentReadOffset = m_dataReadOffset; - - // Read the command - cmd = sequenceData[currentReadOffset]; - currentReadOffset += sizeof(unsigned char); - - if (cmd > 0) - { - assert(cmd <= dem_lastcmd); - - // Read the timestamp - tick = *reinterpret_cast(sequenceData + currentReadOffset); - currentReadOffset += sizeof(int32); + fread(buffer, 1, size, fp); + } + else + { + fseek(fp, size, SEEK_CUR); + } + return size; +} + +void DemoFileReader::ReadSequenceInfo(int32_t& seqNum1, int32_t& seqNum2) +{ + FILE* fp = m_demoFp; + fread(&seqNum1, sizeof(int32_t), 1, fp); + fread(&seqNum2, sizeof(int32_t), 1, fp); +} + +void DemoFileReader::ReadCmdInfo(democmdinfo_t& info) +{ + fread(&info, sizeof(democmdinfo_t), 1, m_demoFp); +} + +void DemoFileReader::ReadCmdHeader(unsigned char& cmd, int32_t& tick) +{ + FILE* fp = m_demoFp; + fread(&cmd, sizeof(unsigned char), 1, fp); + fread(&tick, sizeof(int32_t), 1, fp); + if (cmd >= 0) + { + assert(cmd <= dem_lastcmd); } else { - fprintf(stderr, "CDemoFile::ReadCmdHeader: Missing end tag in demo file.\n"); cmd = dem_stop; } - m_dataReadOffset = currentReadOffset; } -int32 DemoSequenceReader::ReadUserCmd(std::vector& buf, - const int32 maxReadSize /*= MAX_READ_SIZE*/) +int32_t DemoFileReader::ReadUserCmd(uint8_t* buffer, int32_t length) { - if (m_sequenceData.empty()) + int32_t sequenceNum; + fread(&sequenceNum, sizeof(int32_t), 1, m_demoFp); + if (ReadRawData(buffer, length) < 0) { - return 0; + return -1; } - - const int32 outgoing_sequence = *reinterpret_cast(m_sequenceData.data() + m_dataReadOffset); - m_dataReadOffset += sizeof(int32); - - if (!ReadRawData(buf, maxReadSize)) - { - return 0; - } - return outgoing_sequence; + return sequenceNum; } -CDemoFile::CDemoFile(): - m_DemoHeader() +// DemoFileWriter + +DemoFileWriter::DemoFileWriter(FILE* fp) : + m_demoFp(fp) { } -CDemoFile::~CDemoFile() +void DemoFileWriter::WriteDemoHeader(const demoheader_t& header) { - Close(); + fwrite(&header, sizeof(demoheader_t), 1, m_demoFp); } -bool CDemoFile::Open( const char *name ) +void DemoFileReader::ReadDemoHeader(demoheader_t& header) { - Close(); - - FILE *fp = NULL; - fp = fopen( name, "rb" ); - if( fp ) - { - size_t Length; - - fseek( fp, 0, SEEK_END ); - Length = ftell( fp ); - fseek( fp, 0, SEEK_SET ); - - if( Length < sizeof( m_DemoHeader ) ) - { - fprintf( stderr, "CDemoFile::Open: file too small. %s.\n", name ); - fclose( fp ); - return false; - } - - fread( &m_DemoHeader, 1, sizeof( m_DemoHeader ), fp ); - Length -= sizeof( m_DemoHeader ); - - if ( strcmp ( m_DemoHeader.demofilestamp, DEMO_HEADER_ID ) ) - { - fprintf(stderr, "CDemoFile::Open: %s has invalid demo header ID.\n", name); - fclose( fp ); - return false; - } - - if ( m_DemoHeader.demoprotocol != DEMO_PROTOCOL ) - { - fprintf( stderr, "CDemoFile::Open: demo file protocol %i invalid, expected version is %i \n", m_DemoHeader.demoprotocol, DEMO_PROTOCOL ); - fclose( fp ); - return false; - } - - const int32 signOnLength = m_DemoHeader.signonlength; - if (signOnLength > 0) - { - m_signOnData.resize(signOnLength); - fread(&m_signOnData[0], 1, signOnLength, fp); - Length -= signOnLength; - } - - m_fileBuffer.resize( Length ); - fread( &m_fileBuffer[ 0 ], 1, Length, fp ); - - fclose( fp ); - fp = NULL; - } - - if ( !m_fileBuffer.size() ) - { - fprintf( stderr, "CDemoFile::Open: couldn't open file %s.\n", name ); - Close(); - return false; - } - return true; + fread(&header, sizeof(demoheader_t), 1, m_demoFp); } -void CDemoFile::Close() +void DemoFileWriter::WriteRawData(const uint8_t* buffer, int32_t length) { - m_signOnData.clear(); - m_fileBuffer.clear(); + FILE* fp = m_demoFp; + fwrite(&length, sizeof(int32_t), 1, fp); + fwrite(buffer, length, 1, fp); +} + +void DemoFileWriter::WriteSequenceInfo(int32_t seqNum1, int32_t seqNum2) +{ + FILE* fp = m_demoFp; + fwrite(&seqNum1, sizeof(int32_t), 1, fp); + fwrite(&seqNum2, sizeof(int32_t), 1, fp); +} + +void DemoFileWriter::WriteCmdInfo(const democmdinfo_t& info) +{ + fwrite(&info, sizeof(democmdinfo_t), 1, m_demoFp); +} + +void DemoFileWriter::WriteCmdHeader(unsigned char cmd, int32_t tick) +{ + FILE* fp = m_demoFp; + fwrite(&cmd, sizeof(unsigned char), 1, fp); + fwrite(&tick, sizeof(int32_t), 1, fp); +} + +void DemoFileWriter::WriteUserCmd(int32_t sequenceNum, const uint8_t* buffer, int32_t length) +{ + fwrite(&sequenceNum, sizeof(int32_t), 1, m_demoFp); + WriteRawData(buffer, length); } diff --git a/demboyz/demofile.h b/demboyz/demofile.h index 393248d..7ed6bf9 100644 --- a/demboyz/demofile.h +++ b/demboyz/demofile.h @@ -1,280 +1,40 @@ -//====== Copyright (c) 2014, Valve Corporation, All rights reserved. ========// -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -//===========================================================================// -#ifndef DEMOFILE_H -#define DEMOFILE_H -#ifdef _WIN32 #pragma once -#endif -#include +#include +#include -#define __STDC_FORMAT_MACROS -#include +struct demoheader_t; +struct democmdinfo_t; -#define DEMO_HEADER_ID "HL2DEMO" -#define DEMO_PROTOCOL 3 - -#if !defined( MAX_OSPATH ) -#define MAX_OSPATH 260 // max length of a filesystem pathname -#endif - -typedef int32_t int32; -typedef uint32_t uint32; -typedef int64_t int64; -typedef uint64_t uint64; -typedef unsigned long CRC32_t; - -// Demo messages -enum -{ - // it's a startup message, process as fast as possible - dem_signon = 1, - // it's a normal network packet that we stored off - dem_packet, - // sync client clock to demo tick - dem_synctick, - // console command - dem_consolecmd, - // user input command - dem_usercmd, - // network data tables - dem_datatables, - // end of time. - dem_stop, - // a blob of binary data understood by a callback function - dem_customdata, - - dem_stringtables, - - // Last command - dem_lastcmd = dem_stringtables -}; - -struct demoheader_t -{ - char demofilestamp[ 8 ]; // Should be HL2DEMO - int32 demoprotocol; // Should be DEMO_PROTOCOL - int32 networkprotocol; // Should be PROTOCOL_VERSION - char servername[ MAX_OSPATH ]; // Name of server - char clientname[ MAX_OSPATH ]; // Name of client who recorded the game - char mapname[ MAX_OSPATH ]; // Name of map - char gamedirectory[ MAX_OSPATH ]; // Name of game directory (com_gamedir) - float playback_time; // Time of track - int32 playback_ticks; // # of ticks in track - int32 playback_frames; // # of frames in track - int32 signonlength; // length of sigondata in bytes -}; - -#define FDEMO_NORMAL 0 -#define FDEMO_USE_ORIGIN2 ( 1 << 0 ) -#define FDEMO_USE_ANGLES2 ( 1 << 1 ) -#define FDEMO_NOINTERP ( 1 << 2 ) // don't interpolate between this an last view - -#define MAX_SPLITSCREEN_CLIENTS 1 - -struct QAngle -{ - float x, y, z; - void Init( void ) - { - x = y = z = 0.0f; - } - void Init( float _x, float _y, float _z ) - { - x = _x; - y = _y; - z = _z; - } -}; -struct Vector -{ - float x, y, z; - void Init( void ) - { - x = y = z = 0.0f; - } - void Init( float _x, float _y, float _z ) - { - x = _x; - y = _y; - z = _z; - } -}; - -struct democmdinfo_t -{ - democmdinfo_t( void ) - { - } - - struct Split_t - { - Split_t( void ) - { - flags = FDEMO_NORMAL; - viewOrigin.Init(); - viewAngles.Init(); - localViewAngles.Init(); - - // Resampled origin/angles - viewOrigin2.Init(); - viewAngles2.Init(); - localViewAngles2.Init(); - } - - Split_t& operator=( const Split_t& src ) - { - if ( this == &src ) - return *this; - - flags = src.flags; - viewOrigin = src.viewOrigin; - viewAngles = src.viewAngles; - localViewAngles = src.localViewAngles; - viewOrigin2 = src.viewOrigin2; - viewAngles2 = src.viewAngles2; - localViewAngles2 = src.localViewAngles2; - - return *this; - } - - const Vector& GetViewOrigin( void ) - { - if ( flags & FDEMO_USE_ORIGIN2 ) - { - return viewOrigin2; - } - return viewOrigin; - } - - const QAngle& GetViewAngles( void ) - { - if ( flags & FDEMO_USE_ANGLES2 ) - { - return viewAngles2; - } - return viewAngles; - } - const QAngle& GetLocalViewAngles( void ) - { - if ( flags & FDEMO_USE_ANGLES2 ) - { - return localViewAngles2; - } - return localViewAngles; - } - - void Reset( void ) - { - flags = 0; - viewOrigin2 = viewOrigin; - viewAngles2 = viewAngles; - localViewAngles2 = localViewAngles; - } - - int32 flags; - - // original origin/viewangles - Vector viewOrigin; - QAngle viewAngles; - QAngle localViewAngles; - - // Resampled origin/viewangles - Vector viewOrigin2; - QAngle viewAngles2; - QAngle localViewAngles2; - }; - - void Reset( void ) - { - for ( int i = 0; i < MAX_SPLITSCREEN_CLIENTS; ++i ) - { - u[ i ].Reset(); - } - } - - Split_t u[ MAX_SPLITSCREEN_CLIENTS ]; -}; - -class DemoSequenceReader -{ - static const int32 MAX_READ_SIZE = 1024 * 1024; -public: - DemoSequenceReader(const std::vector& sequenceData); - - int32 ReadRawData(char *buffer, int32 length); - - bool ReadRawData(std::vector& buf, const int32 maxReadSize = MAX_READ_SIZE); - - void ReadSequenceInfo(int32 &nSeqNrIn, int32 &nSeqNrOutAck); - - void ReadCmdInfo(democmdinfo_t& info); - - void ReadCmdHeader(unsigned char &cmd, int32 &tick); - - int32 ReadUserCmd(std::vector& buf, const int32 maxReadSize = MAX_READ_SIZE); - -private: - const std::vector& m_sequenceData; - size_t m_dataReadOffset; -}; - -class CDemoFile +class DemoFileReader { public: - CDemoFile(); - ~CDemoFile(); + DemoFileReader(FILE* fp); - bool Open( const char *name ); - void Close(); - - const demoheader_t *GetDemoHeader() const; - - const std::vector& GetSignOnData() const; - const std::vector& GetDemoData() const; + void ReadDemoHeader(demoheader_t& header); + int32_t ReadRawData(uint8_t* buffer, int32_t length); + void ReadSequenceInfo(int32_t& seqNum1, int32_t& seqNum2); + void ReadCmdInfo(democmdinfo_t& info); + void ReadCmdHeader(unsigned char& cmd, int32_t& tick); + int32_t ReadUserCmd(uint8_t* buffer, int32_t length); private: - demoheader_t m_DemoHeader; //general demo info - - std::vector m_signOnData; - std::vector m_fileBuffer; + FILE* m_demoFp; }; -inline const demoheader_t *CDemoFile::GetDemoHeader() const +class DemoFileWriter { - return &m_DemoHeader; -} +public: + DemoFileWriter(FILE* fp); -inline const std::vector& CDemoFile::GetSignOnData() const -{ - return m_signOnData; -} + void WriteDemoHeader(const demoheader_t& header); + void WriteRawData(const uint8_t* buffer, int32_t length); + void WriteSequenceInfo(int32_t seqNum1, int32_t seqNum2); + void WriteCmdInfo(const democmdinfo_t& info); + void WriteCmdHeader(unsigned char cmd, int32_t tick); + void WriteUserCmd(int32_t sequenceNum, const uint8_t* buffer, int32_t length); -inline const std::vector& CDemoFile::GetDemoData() const -{ - return m_fileBuffer; -} - -#endif // DEMOFILE_H +private: + FILE* m_demoFp; +}; diff --git a/demboyz/demofilebitbuf.cpp b/demboyz/demofilebitbuf.cpp deleted file mode 100644 index 9e8ac2a..0000000 --- a/demboyz/demofilebitbuf.cpp +++ /dev/null @@ -1,691 +0,0 @@ -//====== Copyright (c) 2014, Valve Corporation, All rights reserved. ========// -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -//===========================================================================// - -#include -#include "demofilebitbuf.h" - -const uint32 CBitRead::s_nMaskTable[33] = { - 0, - ( 1 << 1 ) - 1, - ( 1 << 2 ) - 1, - ( 1 << 3 ) - 1, - ( 1 << 4 ) - 1, - ( 1 << 5 ) - 1, - ( 1 << 6 ) - 1, - ( 1 << 7 ) - 1, - ( 1 << 8 ) - 1, - ( 1 << 9 ) - 1, - ( 1 << 10 ) - 1, - ( 1 << 11 ) - 1, - ( 1 << 12 ) - 1, - ( 1 << 13 ) - 1, - ( 1 << 14 ) - 1, - ( 1 << 15 ) - 1, - ( 1 << 16 ) - 1, - ( 1 << 17 ) - 1, - ( 1 << 18 ) - 1, - ( 1 << 19 ) - 1, - ( 1 << 20 ) - 1, - ( 1 << 21 ) - 1, - ( 1 << 22 ) - 1, - ( 1 << 23 ) - 1, - ( 1 << 24 ) - 1, - ( 1 << 25 ) - 1, - ( 1 << 26 ) - 1, - ( 1 << 27 ) - 1, - ( 1 << 28 ) - 1, - ( 1 << 29 ) - 1, - ( 1 << 30 ) - 1, - 0x7fffffff, - 0xffffffff, -}; - -int CBitRead::GetNumBitsRead( void ) const -{ - if ( ! m_pData ) // pesky null ptr bitbufs. these happen. - return 0; - - int nCurOfs = ( ( int( m_pDataIn ) - int( m_pData ) ) / 4 ) - 1; - nCurOfs *= 32; - nCurOfs += ( 32 - m_nBitsAvail ); - int nAdjust = 8 * ( m_nDataBytes & 3 ); - return MIN( nCurOfs + nAdjust, m_nDataBits ); -} - -int CBitRead::GetNumBytesRead( void ) const -{ - return ( ( GetNumBitsRead() + 7 ) >> 3 ); -} - -void CBitRead::GrabNextDWord( bool bOverFlowImmediately ) -{ - if ( m_pDataIn == m_pBufferEnd ) - { - m_nBitsAvail = 1; // so that next read will run out of words - m_nInBufWord = 0; - m_pDataIn++; // so seek count increments like old - if ( bOverFlowImmediately ) - { - SetOverflowFlag(); - } - } - else - if ( m_pDataIn > m_pBufferEnd ) - { - SetOverflowFlag(); - m_nInBufWord = 0; - } - else - { - assert( reinterpret_cast< int >( m_pDataIn ) + 3 < reinterpret_cast< int >( m_pBufferEnd ) ); - m_nInBufWord = *( m_pDataIn++ ); - } -} - -void CBitRead::FetchNext( void ) -{ - m_nBitsAvail = 32; - GrabNextDWord( false ); -} - -int CBitRead::ReadOneBit( void ) -{ - int nRet = m_nInBufWord & 1; - if ( --m_nBitsAvail == 0 ) - { - FetchNext(); - } - else - { - m_nInBufWord >>= 1; - } - return nRet; -} - -unsigned int CBitRead::ReadUBitLong( int numbits ) -{ - if ( m_nBitsAvail >= numbits ) - { - unsigned int nRet = m_nInBufWord & s_nMaskTable[ numbits ]; - m_nBitsAvail -= numbits; - if ( m_nBitsAvail ) - { - m_nInBufWord >>= numbits; - } - else - { - FetchNext(); - } - return nRet; - } - else - { - // need to merge words - unsigned int nRet = m_nInBufWord; - numbits -= m_nBitsAvail; - GrabNextDWord( true ); - if ( m_bOverflow ) - return 0; - nRet |= ( ( m_nInBufWord & s_nMaskTable[ numbits ] ) << m_nBitsAvail ); - m_nBitsAvail = 32 - numbits; - m_nInBufWord >>= numbits; - return nRet; - } -} - -int CBitRead::ReadSBitLong( int numbits ) -{ - int nRet = ReadUBitLong( numbits ); - // sign extend - return ( nRet << ( 32 - numbits ) ) >> ( 32 - numbits ); -} - -#ifdef _WIN32 -#pragma warning(push) -#pragma warning(disable : 4715) // disable warning on not all cases - // returning a value. throwing default: - // in measurably reduces perf in bit - // packing benchmark -#endif -unsigned int CBitRead::ReadUBitVar( void ) -{ - unsigned int ret = ReadUBitLong( 6 ); - switch( ret & ( 16 | 32 ) ) - { - case 16: - ret = ( ret & 15 ) | ( ReadUBitLong( 4 ) << 4 ); - assert( ret >= 16); - break; - - case 32: - ret = ( ret & 15 ) | ( ReadUBitLong( 8 ) << 4 ); - assert( ret >= 256); - break; - case 48: - ret = ( ret & 15 ) | ( ReadUBitLong( 32 - 4 ) << 4 ); - assert( ret >= 4096 ); - break; - } - return ret; -} -#ifdef _WIN32 -#pragma warning(pop) -#endif - -int CBitRead::ReadChar( void ) -{ - return ReadSBitLong( sizeof( char ) << 3 ); -} - -int CBitRead::ReadByte( void ) -{ - return ReadUBitLong( sizeof( unsigned char ) << 3 ); -} - -int CBitRead::ReadShort( void ) -{ - return ReadSBitLong( sizeof( short ) << 3 ); -} - -int CBitRead::ReadWord( void ) -{ - return ReadUBitLong( sizeof( unsigned short ) << 3 ); -} - -bool CBitRead::Seek( int nPosition ) -{ - bool bSucc = true; - if ( nPosition < 0 || nPosition > m_nDataBits ) - { - SetOverflowFlag(); - bSucc = false; - nPosition = m_nDataBits; - } - int nHead = m_nDataBytes & 3; // non-multiple-of-4 bytes at head of buffer. We put the "round off" - // at the head to make reading and detecting the end efficient. - - int nByteOfs = nPosition / 8; - if ( ( m_nDataBytes < 4 ) || ( nHead && ( nByteOfs < nHead ) ) ) - { - // partial first dword - unsigned char const *pPartial = ( unsigned char const *) m_pData; - if ( m_pData ) - { - m_nInBufWord = *( pPartial++ ); - if ( nHead > 1 ) - { - m_nInBufWord |= ( *pPartial++ ) << 8; - } - if ( nHead > 2 ) - { - m_nInBufWord |= ( *pPartial++ ) << 16; - } - } - m_pDataIn = ( uint32 const * ) pPartial; - m_nInBufWord >>= ( nPosition & 31 ); - m_nBitsAvail = ( nHead << 3 ) - ( nPosition & 31 ); - } - else - { - int nAdjPosition = nPosition - ( nHead << 3 ); - m_pDataIn = reinterpret_cast< uint32 const * > ( reinterpret_cast< unsigned char const * >( m_pData ) + ( ( nAdjPosition / 32 ) << 2 ) + nHead ); - if ( m_pData ) - { - m_nBitsAvail = 32; - GrabNextDWord(); - } - else - { - m_nInBufWord = 0; - m_nBitsAvail = 1; - } - m_nInBufWord >>= ( nAdjPosition & 31 ); - m_nBitsAvail = MIN( m_nBitsAvail, 32 - ( nAdjPosition & 31 ) ); // in case grabnextdword overflowed - } - return bSucc; -} - - -void CBitRead::StartReading( const void *pData, int nBytes, int iStartBit, int nBits ) -{ -// Make sure it's dword aligned and padded. - assert( ( ( unsigned long )pData & 3 ) == 0 ); - m_pData = ( uint32 * ) pData; - m_pDataIn = m_pData; - m_nDataBytes = nBytes; - - if ( nBits == -1 ) - { - m_nDataBits = nBytes << 3; - } - else - { - assert( nBits <= nBytes * 8 ); - m_nDataBits = nBits; - } - m_bOverflow = false; - m_pBufferEnd = reinterpret_cast< uint32 const * > ( reinterpret_cast< unsigned char const * >( m_pData ) + nBytes ); - if ( m_pData ) - { - Seek( iStartBit ); - } -} - -bool CBitRead::ReadString( char *pStr, int maxLen, bool bLine, int *pOutNumChars ) -{ - assert( maxLen != 0 ); - - bool bTooSmall = false; - int iChar = 0; - while(1) - { - char val = ReadChar(); - if ( val == 0 ) - break; - else if ( bLine && val == '\n' ) - break; - - if ( iChar < ( maxLen - 1 ) ) - { - pStr[ iChar ] = val; - ++iChar; - } - else - { - bTooSmall = true; - } - } - - // Make sure it's null-terminated. - assert( iChar < maxLen ); - pStr[ iChar ] = 0; - - if ( pOutNumChars ) - { - *pOutNumChars = iChar; - } - - return !IsOverflowed() && !bTooSmall; -} - -// Read 1-5 bytes in order to extract a 32-bit unsigned value from the -// stream. 7 data bits are extracted from each byte with the 8th bit used -// to indicate whether the loop should continue. -// This allows variable size numbers to be stored with tolerable -// efficiency. Numbers sizes that can be stored for various numbers of -// encoded bits are: -// 8-bits: 0-127 -// 16-bits: 128-16383 -// 24-bits: 16384-2097151 -// 32-bits: 2097152-268435455 -// 40-bits: 268435456-0xFFFFFFFF -uint32 CBitRead::ReadVarInt32() -{ - uint32 result = 0; - int count = 0; - uint32 b; - - do - { - if ( count == bitbuf::kMaxVarint32Bytes ) - { - return result; - } - b = ReadUBitLong( 8 ); - result |= ( b & 0x7F ) << ( 7 * count ); - ++count; - } while ( b & 0x80 ); - - return result; -} - -uint64 CBitRead::ReadVarInt64() -{ - uint64 result = 0; - int count = 0; - uint64 b; - - do - { - if ( count == bitbuf::kMaxVarintBytes ) - { - return result; - } - b = ReadUBitLong( 8 ); - result |= static_cast(b & 0x7F) << (7 * count); - ++count; - } while (b & 0x80); - - return result; -} - -void CBitRead::ReadBits( void *pOutData, int nBits ) -{ - unsigned char *pOut = ( unsigned char* )pOutData; - int nBitsLeft = nBits; - - - // align output to dword boundary - while( ( ( unsigned long )pOut & 3 ) != 0 && nBitsLeft >= 8 ) - { - *pOut = ( unsigned char )ReadUBitLong( 8 ); - ++pOut; - nBitsLeft -= 8; - } - - // read dwords - while ( nBitsLeft >= 32 ) - { - *( ( unsigned long* )pOut ) = ReadUBitLong( 32 ); - pOut += sizeof( unsigned long ); - nBitsLeft -= 32; - } - - // read remaining bytes - while ( nBitsLeft >= 8 ) - { - *pOut = ReadUBitLong( 8 ); - ++pOut; - nBitsLeft -= 8; - } - - // read remaining bits - if ( nBitsLeft ) - { - *pOut = ReadUBitLong( nBitsLeft ); - } - -} - -bool CBitRead::ReadBytes( void *pOut, int nBytes ) -{ - ReadBits( pOut, nBytes << 3 ); - return !IsOverflowed(); -} - -#define BITS_PER_INT 32 -inline int GetBitForBitnum( int bitNum ) -{ - static int bitsForBitnum[] = - { - ( 1 << 0 ), - ( 1 << 1 ), - ( 1 << 2 ), - ( 1 << 3 ), - ( 1 << 4 ), - ( 1 << 5 ), - ( 1 << 6 ), - ( 1 << 7 ), - ( 1 << 8 ), - ( 1 << 9 ), - ( 1 << 10 ), - ( 1 << 11 ), - ( 1 << 12 ), - ( 1 << 13 ), - ( 1 << 14 ), - ( 1 << 15 ), - ( 1 << 16 ), - ( 1 << 17 ), - ( 1 << 18 ), - ( 1 << 19 ), - ( 1 << 20 ), - ( 1 << 21 ), - ( 1 << 22 ), - ( 1 << 23 ), - ( 1 << 24 ), - ( 1 << 25 ), - ( 1 << 26 ), - ( 1 << 27 ), - ( 1 << 28 ), - ( 1 << 29 ), - ( 1 << 30 ), - ( 1 << 31 ), - }; - - return bitsForBitnum[ (bitNum) & (BITS_PER_INT-1) ]; -} - -float CBitRead::ReadBitAngle( int numbits ) -{ - float shift = (float)( GetBitForBitnum(numbits) ); - - int i = ReadUBitLong( numbits ); - float fReturn = (float)i * (360.0f / shift); - - return fReturn; -} - -// Basic Coordinate Routines (these contain bit-field size AND fixed point scaling constants) -float CBitRead::ReadBitCoord (void) -{ - int intval=0,fractval=0,signbit=0; - float value = 0.0; - - - // Read the required integer and fraction flags - intval = ReadOneBit(); - fractval = ReadOneBit(); - - // If we got either parse them, otherwise it's a zero. - if ( intval || fractval ) - { - // Read the sign bit - signbit = ReadOneBit(); - - // If there's an integer, read it in - if ( intval ) - { - // Adjust the integers from [0..MAX_COORD_VALUE-1] to [1..MAX_COORD_VALUE] - intval = ReadUBitLong( COORD_INTEGER_BITS ) + 1; - } - - // If there's a fraction, read it in - if ( fractval ) - { - fractval = ReadUBitLong( COORD_FRACTIONAL_BITS ); - } - - // Calculate the correct floating point value - value = intval + ((float)fractval * COORD_RESOLUTION); - - // Fixup the sign if negative. - if ( signbit ) - value = -value; - } - - return value; -} - -float CBitRead::ReadBitCoordMP( EBitCoordType coordType ) -{ - bool bIntegral = ( coordType == kCW_Integral ); - bool bLowPrecision = ( coordType == kCW_LowPrecision ); - - int intval=0,fractval=0,signbit=0; - float value = 0.0; - - bool bInBounds = ReadOneBit() ? true : false; - - if ( bIntegral ) - { - // Read the required integer and fraction flags - intval = ReadOneBit(); - // If we got either parse them, otherwise it's a zero. - if ( intval ) - { - // Read the sign bit - signbit = ReadOneBit(); - - // If there's an integer, read it in - // Adjust the integers from [0..MAX_COORD_VALUE-1] to [1..MAX_COORD_VALUE] - if ( bInBounds ) - { - value = ( float )( ReadUBitLong( COORD_INTEGER_BITS_MP ) + 1 ); - } - else - { - value = ( float )( ReadUBitLong( COORD_INTEGER_BITS ) + 1 ); - } - } - } - else - { - // Read the required integer and fraction flags - intval = ReadOneBit(); - - // Read the sign bit - signbit = ReadOneBit(); - - // If we got either parse them, otherwise it's a zero. - if ( intval ) - { - if ( bInBounds ) - { - intval = ReadUBitLong( COORD_INTEGER_BITS_MP ) + 1; - } - else - { - intval = ReadUBitLong( COORD_INTEGER_BITS ) + 1; - } - } - - // If there's a fraction, read it in - fractval = ReadUBitLong( bLowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS ); - - // Calculate the correct floating point value - value = intval + ((float)fractval * ( bLowPrecision ? COORD_RESOLUTION_LOWPRECISION : COORD_RESOLUTION ) ); - } - - // Fixup the sign if negative. - if ( signbit ) - value = -value; - - return value; -} - -float CBitRead::ReadBitCellCoord( int bits, EBitCoordType coordType ) -{ - bool bIntegral = ( coordType == kCW_Integral ); - bool bLowPrecision = ( coordType == kCW_LowPrecision ); - - int intval=0,fractval=0; - float value = 0.0; - - if ( bIntegral ) - { - value = ( float )( ReadUBitLong( bits ) ); - } - else - { - intval = ReadUBitLong( bits ); - - // If there's a fraction, read it in - fractval = ReadUBitLong( bLowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS ); - - // Calculate the correct floating point value - value = intval + ((float)fractval * ( bLowPrecision ? COORD_RESOLUTION_LOWPRECISION : COORD_RESOLUTION ) ); - } - - return value; -} - -void CBitRead::ReadBitVec3Coord( Vector& fa ) -{ - int xflag, yflag, zflag; - - // This vector must be initialized! Otherwise, If any of the flags aren't set, - // the corresponding component will not be read and will be stack garbage. - fa.Init( 0, 0, 0 ); - - xflag = ReadOneBit(); - yflag = ReadOneBit(); - zflag = ReadOneBit(); - - if ( xflag ) - fa.x = ReadBitCoord(); - if ( yflag ) - fa.y = ReadBitCoord(); - if ( zflag ) - fa.z = ReadBitCoord(); -} - -float CBitRead::ReadBitNormal (void) -{ - // Read the sign bit - int signbit = ReadOneBit(); - - // Read the fractional part - unsigned int fractval = ReadUBitLong( NORMAL_FRACTIONAL_BITS ); - - // Calculate the correct floating point value - float value = (float)fractval * NORMAL_RESOLUTION; - - // Fixup the sign if negative. - if ( signbit ) - value = -value; - - return value; -} - -void CBitRead::ReadBitVec3Normal( Vector& fa ) -{ - int xflag = ReadOneBit(); - int yflag = ReadOneBit(); - - if (xflag) - fa.x = ReadBitNormal(); - else - fa.x = 0.0f; - - if (yflag) - fa.y = ReadBitNormal(); - else - fa.y = 0.0f; - - // The first two imply the third (but not its sign) - int znegative = ReadOneBit(); - - float fafafbfb = fa.x * fa.x + fa.y * fa.y; - if (fafafbfb < 1.0f) - fa.z = sqrt( 1.0f - fafafbfb ); - else - fa.z = 0.0f; - - if (znegative) - fa.z = -fa.z; -} - -void CBitRead::ReadBitAngles( QAngle& fa ) -{ - Vector tmp; - ReadBitVec3Coord( tmp ); - fa.Init( tmp.x, tmp.y, tmp.z ); -} - -float CBitRead::ReadBitFloat( void ) -{ - uint32 nvalue = ReadUBitLong( 32 ); - return *( ( float * ) &nvalue ); -} - diff --git a/demboyz/demofilebitbuf.h b/demboyz/demofilebitbuf.h deleted file mode 100644 index 14749b5..0000000 --- a/demboyz/demofilebitbuf.h +++ /dev/null @@ -1,263 +0,0 @@ -//====== Copyright (c) 2014, Valve Corporation, All rights reserved. ========// -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -//===========================================================================// - -#ifndef DEMOFILEBITBUF_H -#define DEMOFILEBITBUF_H - -#include -#include "demofile.h" - -// OVERALL Coordinate Size Limits used in COMMON.C MSG_*BitCoord() Routines (and someday the HUD) -#define COORD_INTEGER_BITS 14 -#define COORD_FRACTIONAL_BITS 5 -#define COORD_DENOMINATOR (1<<(COORD_FRACTIONAL_BITS)) -#define COORD_RESOLUTION (1.0f/(COORD_DENOMINATOR)) - -// Special threshold for networking multiplayer origins -#define COORD_INTEGER_BITS_MP 11 -#define COORD_FRACTIONAL_BITS_MP_LOWPRECISION 3 -#define COORD_DENOMINATOR_LOWPRECISION (1<<(COORD_FRACTIONAL_BITS_MP_LOWPRECISION)) -#define COORD_RESOLUTION_LOWPRECISION (1.0f/(COORD_DENOMINATOR_LOWPRECISION)) - -#define NORMAL_FRACTIONAL_BITS 11 -#define NORMAL_DENOMINATOR ( (1<<(NORMAL_FRACTIONAL_BITS)) - 1 ) -#define NORMAL_RESOLUTION (1.0f/(NORMAL_DENOMINATOR)) - -enum EBitCoordType -{ - kCW_None, - kCW_LowPrecision, - kCW_Integral -}; - -//----------------------------------------------------------------------------- -// namespaced helpers -//----------------------------------------------------------------------------- -namespace bitbuf -{ - // ZigZag Transform: Encodes signed integers so that they can be - // effectively used with varint encoding. - // - // varint operates on unsigned integers, encoding smaller numbers into - // fewer bytes. If you try to use it on a signed integer, it will treat - // this number as a very large unsigned integer, which means that even - // small signed numbers like -1 will take the maximum number of bytes - // (10) to encode. ZigZagEncode() maps signed integers to unsigned - // in such a way that those with a small absolute value will have smaller - // encoded values, making them appropriate for encoding using varint. - // - // int32 -> uint32 - // ------------------------- - // 0 -> 0 - // -1 -> 1 - // 1 -> 2 - // -2 -> 3 - // ... -> ... - // 2147483647 -> 4294967294 - // -2147483648 -> 4294967295 - // - // >> encode >> - // << decode << - - inline uint32 ZigZagEncode32(int32 n) - { - // Note: the right-shift must be arithmetic - return(n << 1) ^ (n >> 31); - } - - inline int32 ZigZagDecode32(uint32 n) - { - return(n >> 1) ^ -static_cast(n & 1); - } - - inline uint64 ZigZagEncode64(int64 n) - { - // Note: the right-shift must be arithmetic - return(n << 1) ^ (n >> 63); - } - - inline int64 ZigZagDecode64(uint64 n) - { - return(n >> 1) ^ -static_cast(n & 1); - } - - const int kMaxVarintBytes = 10; - const int kMaxVarint32Bytes = 5; -} - -class CBitRead -{ - uint32 m_nInBufWord; - int m_nBitsAvail; - uint32 const *m_pDataIn; - uint32 const *m_pBufferEnd; - uint32 const *m_pData; - - bool m_bOverflow; - int m_nDataBits; - size_t m_nDataBytes; - - static const uint32 s_nMaskTable[ 33 ]; // 0 1 3 7 15 .. - -public: - CBitRead( const void *pData, int nBytes, int nBits = -1 ) - { - m_bOverflow = false; - m_nDataBits = -1; - m_nDataBytes = 0; - StartReading( pData, nBytes, 0, nBits ); - } - - CBitRead( void ) - { - m_bOverflow = false; - m_nDataBits = -1; - m_nDataBytes = 0; - } - - void SetOverflowFlag( void ) - { - m_bOverflow = true; - } - - bool IsOverflowed( void ) const - { - return m_bOverflow; - } - - int Tell( void ) const - { - return GetNumBitsRead(); - } - - size_t TotalBytesAvailable( void ) const - { - return m_nDataBytes; - } - - int GetNumBitsLeft( void ) const - { - return m_nDataBits - Tell(); - } - - int GetNumBytesLeft( void ) const - { - return GetNumBitsLeft() >> 3; - } - - bool Seek( int nPosition ); - - bool SeekRelative( int nOffset ) - { - return Seek( GetNumBitsRead() + nOffset ); - } - - unsigned char const * GetBasePointer() - { - return reinterpret_cast< unsigned char const *>( m_pData ); - } - - void StartReading( const void *pData, int nBytes, int iStartBit = 0, int nBits = -1 ); - - int GetNumBitsRead( void ) const; - int GetNumBytesRead( void ) const; - - void GrabNextDWord( bool bOverFlowImmediately = false ); - void FetchNext( void ); - unsigned int ReadUBitLong( int numbits ); - int ReadSBitLong( int numbits ); - unsigned int ReadUBitVar( void ); - unsigned int PeekUBitLong( int numbits ); - bool ReadBytes( void *pOut, int nBytes ); - - // Returns 0 or 1. - int ReadOneBit( void ); - int ReadLong( void ); - int ReadChar( void ); - int ReadByte( void ); - int ReadShort( void ); - int ReadWord( void ); - float ReadFloat( void ); - void ReadBits( void *pOut, int nBits ); - - float ReadBitCoord(); - float ReadBitCoordMP( EBitCoordType coordType ); - float ReadBitCellCoord( int bits, EBitCoordType coordType ); - float ReadBitNormal(); - void ReadBitVec3Coord( Vector& fa ); - void ReadBitVec3Normal( Vector& fa ); - void ReadBitAngles( QAngle& fa ); - float ReadBitAngle( int numbits ); - float ReadBitFloat( void ); - - // Returns false if bufLen isn't large enough to hold the - // string in the buffer. - // - // Always reads to the end of the string (so you can read the - // next piece of data waiting). - // - // If bLine is true, it stops when it reaches a '\n' or a null-terminator. - // - // pStr is always null-terminated (unless bufLen is 0). - // - // pOutNumChars is set to the number of characters left in pStr when the routine is - // complete (this will never exceed bufLen-1). - // - bool ReadString( char *pStr, int bufLen, bool bLine=false, int *pOutNumChars = NULL ); - - // reads a varint encoded integer - uint32 ReadVarInt32(); - uint64 ReadVarInt64(); - int32 ReadSignedVarInt32() { return bitbuf::ZigZagDecode32( ReadVarInt32() ); } - int64 ReadSignedVarInt64() { return bitbuf::ZigZagDecode64( ReadVarInt64() ); } -}; - -inline unsigned int CBitRead::PeekUBitLong(int numbits) -{ - int nSaveBA = m_nBitsAvail; - int nSaveW = m_nInBufWord; - uint32 const *pSaveP = m_pDataIn; - unsigned int nRet = ReadUBitLong(numbits); - m_nBitsAvail = nSaveBA; - m_nInBufWord = nSaveW; - m_pDataIn = pSaveP; - return nRet; -} - -inline int CBitRead::ReadLong(void) -{ - return (int)ReadUBitLong(sizeof(long) << 3); -} - -inline float CBitRead::ReadFloat(void) -{ - uint32 nUval = ReadUBitLong(sizeof(long) << 3); - return *((float *)&nUval); -} - -#ifndef MIN -#define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) -#endif - -#endif \ No newline at end of file diff --git a/demboyz/demoreader.h b/demboyz/demoreader.h new file mode 100644 index 0000000..069aaaf --- /dev/null +++ b/demboyz/demoreader.h @@ -0,0 +1,10 @@ + +#pragma once + +class IDemoWriter; + +namespace DemoReader +{ + void ProcessDem(void* inputFp, IDemoWriter* writer); + void ProcessJson(void* inputFp, IDemoWriter* writer); +} diff --git a/demboyz/demotypes.h b/demboyz/demotypes.h new file mode 100644 index 0000000..e7472aa --- /dev/null +++ b/demboyz/demotypes.h @@ -0,0 +1,180 @@ +//====== Copyright (c) 2014, Valve Corporation, All rights reserved. ========// +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +//===========================================================================// + +#pragma once + +#define __STDC_FORMAT_MACROS +#include + +#include "vector.h" + +#define DEMO_HEADER_ID "HL2DEMO" +#define DEMO_PROTOCOL 3 + +#if !defined( MAX_OSPATH ) +#define MAX_OSPATH 260 // max length of a filesystem pathname +#endif + +// Demo messages +enum +{ + // it's a startup message, process as fast as possible + dem_signon = 1, + // it's a normal network packet that we stored off + dem_packet, + // sync client clock to demo tick + dem_synctick, + // console command + dem_consolecmd, + // user input command + dem_usercmd, + // network data tables + dem_datatables, + // end of time. + dem_stop, + // a blob of binary data understood by a callback function + dem_customdata, + + dem_stringtables, + + // Last command + dem_lastcmd = dem_stringtables +}; + +struct demoheader_t +{ + char demofilestamp[ 8 ]; // Should be HL2DEMO + int32_t demoprotocol; // Should be DEMO_PROTOCOL + int32_t networkprotocol; // Should be PROTOCOL_VERSION + char servername[ MAX_OSPATH ]; // Name of server + char clientname[ MAX_OSPATH ]; // Name of client who recorded the game + char mapname[ MAX_OSPATH ]; // Name of map + char gamedirectory[ MAX_OSPATH ]; // Name of game directory (com_gamedir) + float playback_time; // Time of track + int32_t playback_ticks; // # of ticks in track + int32_t playback_frames; // # of frames in track + int32_t signonlength; // length of sigondata in bytes +}; + +#define FDEMO_NORMAL 0 +#define FDEMO_USE_ORIGIN2 ( 1 << 0 ) +#define FDEMO_USE_ANGLES2 ( 1 << 1 ) +#define FDEMO_NOINTERP ( 1 << 2 ) // don't interpolate between this an last view + +#define MAX_SPLITSCREEN_CLIENTS 1 + +struct democmdinfo_t +{ + democmdinfo_t( void ) + { + } + + struct Split_t + { + Split_t( void ) + { + flags = FDEMO_NORMAL; + viewOrigin.Init(); + viewAngles.Init(); + localViewAngles.Init(); + + // Resampled origin/angles + viewOrigin2.Init(); + viewAngles2.Init(); + localViewAngles2.Init(); + } + + Split_t& operator=( const Split_t& src ) + { + if ( this == &src ) + return *this; + + flags = src.flags; + viewOrigin = src.viewOrigin; + viewAngles = src.viewAngles; + localViewAngles = src.localViewAngles; + viewOrigin2 = src.viewOrigin2; + viewAngles2 = src.viewAngles2; + localViewAngles2 = src.localViewAngles2; + + return *this; + } + + const Vector& GetViewOrigin( void ) + { + if ( flags & FDEMO_USE_ORIGIN2 ) + { + return viewOrigin2; + } + return viewOrigin; + } + + const QAngle& GetViewAngles( void ) + { + if ( flags & FDEMO_USE_ANGLES2 ) + { + return viewAngles2; + } + return viewAngles; + } + const QAngle& GetLocalViewAngles( void ) + { + if ( flags & FDEMO_USE_ANGLES2 ) + { + return localViewAngles2; + } + return localViewAngles; + } + + void Reset( void ) + { + flags = 0; + viewOrigin2 = viewOrigin; + viewAngles2 = viewAngles; + localViewAngles2 = localViewAngles; + } + + int32_t flags; + + // original origin/viewangles + Vector viewOrigin; + QAngle viewAngles; + QAngle localViewAngles; + + // Resampled origin/viewangles + Vector viewOrigin2; + QAngle viewAngles2; + QAngle localViewAngles2; + }; + + void Reset( void ) + { + for ( int i = 0; i < MAX_SPLITSCREEN_CLIENTS; ++i ) + { + u[ i ].Reset(); + } + } + + Split_t u[ MAX_SPLITSCREEN_CLIENTS ]; +}; diff --git a/demboyz/demowriter.cpp b/demboyz/demowriter.cpp new file mode 100644 index 0000000..1e601f1 --- /dev/null +++ b/demboyz/demowriter.cpp @@ -0,0 +1,28 @@ + +#pragma once + +#include "idemowriter.h" +#include + +class DemoWriter: public IDemoWriter +{ +public: + DemoWriter(FILE* outputFp); + + virtual void StartWriting(demoheader_t& header) override final; + virtual void EndWriting() override final; + + virtual void StartCommandPacket(CommandPacket& packet) override final; + virtual void EndCommandPacket() override final; + + virtual void WriteNetPacket(NetPacket& packet) override final; +}; + +IDemoWriter* IDemoWriter::CreateDemoWriter(void* outputFp) +{ + return new DemoWriter(reinterpret_cast(outputFp)); +} + +DemoWriter::DemoWriter(FILE* outputFp) +{ +} diff --git a/demboyz/demreader.cpp b/demboyz/demreader.cpp new file mode 100644 index 0000000..935fe5a --- /dev/null +++ b/demboyz/demreader.cpp @@ -0,0 +1,72 @@ + +#include "demoreader.h" +#include "idemowriter.h" +#include "demofile.h" + +/*void ParsePacket(const std::vector& packet) +{ + assert(packet.size() <= NET_MAX_PAYLOAD); + bf_read bitbuf(packet.data(), packet.size()); + while (bitbuf.GetNumBitsLeft() >= NETMSG_TYPE_BITS) + { + uint32 typeId = bitbuf.ReadUBitLong(NETMSG_TYPE_BITS); + printf("%i\n", typeId); + //ProcessNetMsg(typeId, bitbuf); + } +} + +void ParseDemoSequence(const std::vector& sequenceData) +{ + CommandPacket packet; + std::vector buffer; + + DemoSequenceReader reader(sequenceData); + for (; reader.ReadCmdHeader(packet.cmd, packet.tick);) + { + switch (packet.cmd) + { + case dem_signon: + case dem_packet: + reader.ReadCmdInfo(packet.cmdInfo); + reader.ReadSequenceInfo(packet.sequenceInfo1, packet.sequenceInfo2); + assert(packet.sequenceInfo1 == packet.sequenceInfo2); + reader.ReadRawData(buffer); + ParsePacket(buffer); + break; + case dem_synctick: + // nothing + break; + case dem_consolecmd: + reader.ReadRawData(nullptr, 1024); + break; + case dem_usercmd: + reader.ReadUserCmd(buffer, 256); + break; + case dem_datatables: + // TODO: datatables + reader.ReadRawData(nullptr, 64*1024); + break; + case dem_stop: + // TODO assert frame and tick numbers + break; + case dem_customdata: + reader.ReadRawData(nullptr, 0); + break; + case dem_stringtables: + reader.ReadRawData(nullptr, 0); + break; + default: + assert(false); + break; + } + } +}*/ + +void DemoReader::ProcessDem(void* inputFp, IDemoWriter* writer) +{ + DemoFileReader demoFile(reinterpret_cast(inputFp)); + + //auto demoHeader = demoFile.GetDemoHeader(); + //ParseDemoSequence(demoFile.GetSignOnData()); + //ParseDemoSequence(demoFile.GetDemoData()); +} diff --git a/demboyz/idemowriter.h b/demboyz/idemowriter.h new file mode 100644 index 0000000..414c8fa --- /dev/null +++ b/demboyz/idemowriter.h @@ -0,0 +1,48 @@ + +#pragma once + +#include + +struct democmdinfo_t; +struct demoheader_t; +struct CommandPacket; +struct NetPacket; + +struct CommandPacket +{ + unsigned char cmd; + int32_t tick; + + democmdinfo_t* cmdInfo; + int32_t sequenceInfo1; + int32_t sequenceInfo2; +}; + +struct NetPacket +{ + uint32_t type; + void* data; +}; + +class IDemoWriter +{ +public: + virtual ~IDemoWriter() {} + + virtual void StartWriting(demoheader_t& header) = 0; + virtual void EndWriting() = 0; + + virtual void StartCommandPacket(CommandPacket& packet) = 0; + virtual void EndCommandPacket() = 0; + + virtual void WriteNetPacket(NetPacket& packet) = 0; + +public: + static IDemoWriter* CreateJsonWriter(void* outputFp); + static IDemoWriter* CreateDemoWriter(void* outputFp); + + static void FreeDemoWriter(IDemoWriter* writer) + { + delete writer; + } +}; diff --git a/demboyz/jsonreader.cpp b/demboyz/jsonreader.cpp new file mode 100644 index 0000000..95e5450 --- /dev/null +++ b/demboyz/jsonreader.cpp @@ -0,0 +1,6 @@ + +#include "demoreader.h" + +void DemoReader::ProcessJson(void* inputFp, IDemoWriter* writer) +{ +} diff --git a/demboyz/jsonwriter.cpp b/demboyz/jsonwriter.cpp new file mode 100644 index 0000000..4923293 --- /dev/null +++ b/demboyz/jsonwriter.cpp @@ -0,0 +1,24 @@ + +#pragma once + +#include "idemowriter.h" +#include + +class JsonWriter: public IDemoWriter +{ +public: + JsonWriter(FILE* outputFp); + + virtual void StartWriting(demoheader_t& header) override final; + virtual void EndWriting() override final; + + virtual void StartCommandPacket(CommandPacket& packet) override final; + virtual void EndCommandPacket() override final; + + virtual void WriteNetPacket(NetPacket& packet) override final; +}; + +IDemoWriter* IDemoWriter::CreateJsonWriter(void* outputFp) +{ + return new JsonWriter(reinterpret_cast(outputFp)); +} diff --git a/external/sourcesdk/valve_support.h b/external/sourcesdk/valve_support.h index 1df7209..ecaddcc 100644 --- a/external/sourcesdk/valve_support.h +++ b/external/sourcesdk/valve_support.h @@ -2,6 +2,7 @@ #pragma once #include +#include #include #include "vector.h" @@ -25,8 +26,6 @@ using byte = char; #define PLATFORM_WINDOWS_PC64 1 #endif -#define NULL nullptr - #define Assert(x) assert(x) #define AssertMsg(x, ...) assert(x) #define AssertMsg2(x, ...) assert(x) diff --git a/premake/demboyz.lua b/premake/demboyz.lua index b5c23ef..1376f21 100644 --- a/premake/demboyz.lua +++ b/premake/demboyz.lua @@ -6,16 +6,22 @@ solution "demboyz" configurations { "Debug", "Release" } platforms "x32" - vpaths - { - ["Header Files"] = { "../**.h" }, - ["Source Files"] = { "../**.cpp" } - } - project "demboyz" kind "ConsoleApp" language "C++" - files { "../demboyz/**.h", "../demboyz/**.cpp" } + files + { + "../demboyz/*.h", + "../demboyz/*.cpp", + "../demboyz/netmessages/*.h", + "../demboyz/netmessages/*.cpp", + "../external/sourcesdk/*.h", + "../external/sourcesdk/*.cpp" + } + includedirs + { + "../external/sourcesdk" + } configuration "Debug" targetdir (_ACTION .. "/build/Debug")