diff --git a/.gitignore b/.gitignore index b8bd026..34a99cb 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ *.exe *.out *.app + +# Generated Project Files +premake/*/ diff --git a/demboyz/demboyz.cpp b/demboyz/demboyz.cpp new file mode 100644 index 0000000..c415d47 --- /dev/null +++ b/demboyz/demboyz.cpp @@ -0,0 +1,20 @@ + +#include "demofile.h" + +int main(const int argc, const char* argv[]) +{ + if (argc < 2) + { + return -1; + } + + CDemoFile demoFile; + if (!demoFile.Open(argv[1])) + { + return -1; + } + + demoFile.Close(); + + return 0; +} diff --git a/demboyz/demofile.cpp b/demboyz/demofile.cpp new file mode 100644 index 0000000..166ddcb --- /dev/null +++ b/demboyz/demofile.cpp @@ -0,0 +1,192 @@ +//====== 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" + +CDemoFile::CDemoFile() +{ +} + +CDemoFile::~CDemoFile() +{ + Close(); +} + +void CDemoFile::ReadSequenceInfo( int32 &nSeqNrIn, int32 &nSeqNrOut ) +{ + if ( !m_fileBuffer.size() ) + return; + + nSeqNrIn = *( int32 * )( &m_fileBuffer[ m_fileBufferPos ] ); + m_fileBufferPos += sizeof( int32 ); + nSeqNrOut = *( int32 * )( &m_fileBuffer[ m_fileBufferPos ] ); + m_fileBufferPos += sizeof( int32 ); +} + +void CDemoFile::ReadCmdInfo( democmdinfo_t& info ) +{ + if ( !m_fileBuffer.size() ) + return; + + memcpy( &info, &m_fileBuffer[ m_fileBufferPos ], sizeof( democmdinfo_t ) ); + m_fileBufferPos += sizeof( democmdinfo_t ); +} + +void CDemoFile::ReadCmdHeader( unsigned char& cmd, int32& tick, unsigned char& playerSlot ) +{ + if ( !m_fileBuffer.size() ) + return; + + // Read the command + cmd = *( unsigned char * )( &m_fileBuffer[ m_fileBufferPos ] ); + m_fileBufferPos += sizeof( unsigned char ); + + if ( cmd <=0 ) + { + fprintf( stderr, "CDemoFile::ReadCmdHeader: Missing end tag in demo file.\n"); + cmd = dem_stop; + return; + } + + assert( cmd >= 1 && cmd <= dem_lastcmd ); + + // Read the timestamp + tick = *( int32 * )( &m_fileBuffer[ m_fileBufferPos ] ); + m_fileBufferPos += sizeof( int32 ); + + // read playerslot + playerSlot = *( unsigned char * )( &m_fileBuffer[ m_fileBufferPos ] ); + m_fileBufferPos += sizeof( unsigned char ); +} + +int32 CDemoFile::ReadUserCmd( char *buffer, int32 &size ) +{ + if ( !m_fileBuffer.size() ) + return 0; + + int32 outgoing_sequence = *( int32 * )( &m_fileBuffer[ m_fileBufferPos ] ); + m_fileBufferPos += sizeof( int32 ); + + size = ReadRawData( buffer, size ); + + return outgoing_sequence; +} + +int32 CDemoFile::ReadRawData( char *buffer, int32 length ) +{ + if ( !m_fileBuffer.size() ) + return 0; + + // read length of data block + int32 size = *( int32 * )( &m_fileBuffer[ m_fileBufferPos ] ); + m_fileBufferPos += sizeof( int32 ); + + if ( buffer && (length < size) ) + { + fprintf( stderr, "CDemoFile::ReadRawData: buffer overflow (%i).\n", size ); + return -1; + } + + if ( buffer ) + { + // read data into buffer + memcpy( buffer, &m_fileBuffer[ m_fileBufferPos ], size ); + m_fileBufferPos += size; + } + else + { + // just skip it + m_fileBufferPos += size; + } + + return size; +} + +bool CDemoFile::Open( const char *name ) +{ + 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", m_szFileName.c_str() ); + 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; + } + + 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; + } + + m_fileBufferPos = 0; + m_szFileName = name; + return true; +} + +void CDemoFile::Close() +{ + m_szFileName.clear(); + + m_fileBufferPos = 0; + m_fileBuffer.clear(); +} diff --git a/demboyz/demofile.h b/demboyz/demofile.h new file mode 100644 index 0000000..eb5202e --- /dev/null +++ b/demboyz/demofile.h @@ -0,0 +1,251 @@ +//====== 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 + +#define __STDC_FORMAT_MACROS +#include + +#define DEMO_HEADER_ID "HL2DEMO" +#define DEMO_PROTOCOL 4 + +#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 2 + +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 CDemoFile +{ +public: + CDemoFile(); + virtual ~CDemoFile(); + + bool Open( const char *name ); + void Close(); + + int32 ReadRawData( char *buffer, int32 length ); + + void ReadSequenceInfo( int32 &nSeqNrIn, int32 &nSeqNrOutAck ); + + void ReadCmdInfo( democmdinfo_t& info ); + + void ReadCmdHeader( unsigned char &cmd, int32 &tick, unsigned char& playerSlot ); + + int32 ReadUserCmd( char *buffer, int32 &size ); + + demoheader_t *ReadDemoHeader(); + +public: + demoheader_t m_DemoHeader; //general demo info + + std::string m_szFileName; + + size_t m_fileBufferPos; + std::string m_fileBuffer; +}; + +#endif // DEMOFILE_H diff --git a/demboyz/demofilebitbuf.cpp b/demboyz/demofilebitbuf.cpp new file mode 100644 index 0000000..9e8ac2a --- /dev/null +++ b/demboyz/demofilebitbuf.cpp @@ -0,0 +1,691 @@ +//====== 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 new file mode 100644 index 0000000..14b46fd --- /dev/null +++ b/demboyz/demofilebitbuf.h @@ -0,0 +1,240 @@ +//====== 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() ); } +}; + +#ifndef MIN +#define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) +#endif + +#endif \ No newline at end of file diff --git a/premake/demboyz.lua b/premake/demboyz.lua new file mode 100644 index 0000000..b5c23ef --- /dev/null +++ b/premake/demboyz.lua @@ -0,0 +1,28 @@ + +solution "demboyz" + basedir ".." + location (_ACTION) + startproject "demboyz" + configurations { "Debug", "Release" } + platforms "x32" + + vpaths + { + ["Header Files"] = { "../**.h" }, + ["Source Files"] = { "../**.cpp" } + } + + project "demboyz" + kind "ConsoleApp" + language "C++" + files { "../demboyz/**.h", "../demboyz/**.cpp" } + + configuration "Debug" + targetdir (_ACTION .. "/build/Debug") + defines { "DEBUG" } + flags { "Symbols" } + + configuration "Release" + targetdir (_ACTION .. "/build/Release") + defines { "NDEBUG" } + optimize "Full" diff --git a/premake/premake5.exe b/premake/premake5.exe new file mode 100644 index 0000000..0cee83d Binary files /dev/null and b/premake/premake5.exe differ diff --git a/premake/vs2013.bat b/premake/vs2013.bat new file mode 100644 index 0000000..8dae9d3 --- /dev/null +++ b/premake/vs2013.bat @@ -0,0 +1,4 @@ + +premake5 --file=demboyz.lua vs2013 + +pause