Added demo writer interface, restructure

This commit is contained in:
Jordan Cristiano 2015-05-12 20:39:56 -04:00
parent 9834880bc5
commit 78de8690ab
14 changed files with 564 additions and 1514 deletions

View File

@ -1,90 +1,100 @@
#include "demofile.h"
#include "demofilebitbuf.h"
#include "netmessages.h"
#include <assert.h>
#include <vector>
#include "idemowriter.h"
#include "demoreader.h"
#include <cstdio>
#include <string>
void ParsePacket(const std::vector<unsigned char>& 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<unsigned char>& sequenceData)
enum class FileType
{
unsigned char cmd;
int32 tick;
int32 sequenceInfo1;
int32 sequenceInfo2;
democmdinfo_t cmdInfo;
std::vector<unsigned char> 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 <in>.dem/json <out>.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;
}

View File

@ -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 <stdio.h>
#include <cstring>
#include <assert.h>
#include "demofile.h"
#include "demotypes.h"
#include <cassert>
DemoSequenceReader::DemoSequenceReader(const std::vector<unsigned char>& 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<const int32*>(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<unsigned char>& 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<const int32*>(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<const int32*>(sequenceData + currentReadOffset);
currentReadOffset += sizeof(int32);
nSeqNrOut = *reinterpret_cast<const int32*>(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<const int32*>(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<unsigned char>& 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<const int32*>(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);
}

View File

@ -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 <vector>
#include <cstdint>
#include <cstdio>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
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<unsigned char>& sequenceData);
int32 ReadRawData(char *buffer, int32 length);
bool ReadRawData(std::vector<unsigned char>& 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<unsigned char>& buf, const int32 maxReadSize = MAX_READ_SIZE);
private:
const std::vector<unsigned char>& 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<unsigned char>& GetSignOnData() const;
const std::vector<unsigned char>& 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<unsigned char> m_signOnData;
std::vector<unsigned char> 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<unsigned char>& 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<unsigned char>& CDemoFile::GetDemoData() const
{
return m_fileBuffer;
}
#endif // DEMOFILE_H
private:
FILE* m_demoFp;
};

View File

@ -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 <assert.h>
#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<uint64>(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 );
}

View File

@ -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 <math.h>
#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<int32>(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<int64>(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

10
demboyz/demoreader.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
class IDemoWriter;
namespace DemoReader
{
void ProcessDem(void* inputFp, IDemoWriter* writer);
void ProcessJson(void* inputFp, IDemoWriter* writer);
}

180
demboyz/demotypes.h Normal file
View File

@ -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 <inttypes.h>
#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 ];
};

28
demboyz/demowriter.cpp Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include "idemowriter.h"
#include <cstdio>
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<FILE*>(outputFp));
}
DemoWriter::DemoWriter(FILE* outputFp)
{
}

72
demboyz/demreader.cpp Normal file
View File

@ -0,0 +1,72 @@
#include "demoreader.h"
#include "idemowriter.h"
#include "demofile.h"
/*void ParsePacket(const std::vector<unsigned char>& 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<unsigned char>& sequenceData)
{
CommandPacket packet;
std::vector<uint8_t> 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<FILE*>(inputFp));
//auto demoHeader = demoFile.GetDemoHeader();
//ParseDemoSequence(demoFile.GetSignOnData());
//ParseDemoSequence(demoFile.GetDemoData());
}

48
demboyz/idemowriter.h Normal file
View File

@ -0,0 +1,48 @@
#pragma once
#include <cstdint>
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;
}
};

6
demboyz/jsonreader.cpp Normal file
View File

@ -0,0 +1,6 @@
#include "demoreader.h"
void DemoReader::ProcessJson(void* inputFp, IDemoWriter* writer)
{
}

24
demboyz/jsonwriter.cpp Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include "idemowriter.h"
#include <cstdio>
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<FILE*>(outputFp));
}

View File

@ -2,6 +2,7 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include <cassert>
#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)

View File

@ -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")