Created
September 26, 2025 15:02
-
-
Save jamesu/f31cef752da7dc38f9d119ee77265489 to your computer and use it in GitHub Desktop.
Code to dump packet info from onverse connections
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//#pragma GCC visibility push(hidden) | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <math.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <sys/poll.h> | |
#include <arpa/inet.h> | |
#include <netdb.h> | |
#include <netinet/in.h> | |
#include <vector> | |
// BitStream | |
// g++ -m32 -fPIC -dynamiclib -shared replacement_lib.c -o replacement.dylib | |
#undef M_PI | |
#undef M_SQRT2 | |
#define M_PI 3.14159265358979323846 | |
#define M_SQRT2 1.41421356237309504880 | |
#define M_2PI (3.1415926535897932384626433 * 2.0) | |
#define M_SQRTHALF 0.7071067811865475244008443 | |
#define M_PI_F 3.14159265358979323846f | |
#define M_SQRT2_F 1.41421356237309504880f | |
#define M_2PI_F (3.1415926535897932384626433f * 2.0f) | |
#define M_SQRTHALF_F 0.7071067811865475244008443f | |
#define BIT(x) (1 << (x)) ///< Returns value with bit x set (2^x) | |
#define DECLARE_OVERLOADED_READ(type) \ | |
bool read(type* out_read) { \ | |
return read(sizeof(type), out_read); \ | |
} | |
#define DECLARE_OVERLOADED_WRITE(type) \ | |
bool write(type in_write) { \ | |
return write(sizeof(type), &in_write); \ | |
} | |
#define DECLARE_ENDIAN_OVERLOADED_READ(type) \ | |
bool read(type* out_read) { \ | |
type temp; \ | |
bool success = read(sizeof(type), &temp); \ | |
*out_read = (temp); \ | |
return success; \ | |
} | |
#define DECLARE_ENDIAN_OVERLOADED_WRITE(type) \ | |
bool write(type in_write) { \ | |
type temp = (in_write); \ | |
return write(sizeof(type), &temp); \ | |
} | |
typedef unsigned int U32; | |
typedef signed int S32; | |
typedef unsigned char U8; | |
typedef unsigned short U16; | |
typedef short S16; | |
typedef char S8; | |
typedef float F32; | |
class OVNetAddress; | |
enum EventConstants | |
{ | |
MaxPacketDataSize = 1500, ///< Maximum allowed size of a packet. | |
MaxConsoleLineSize = 512 ///< Maximum allowed size of a console line. | |
}; | |
void AssertFatal(bool cond, const char *str) | |
{ | |
if (!cond) | |
{ | |
//printf("%s\n", str); | |
} | |
} | |
inline F32 mSqrt(const F32 val) | |
{ | |
return (F32) sqrt(val); | |
} | |
inline F32 mFabs(const F32 val) | |
{ | |
return (F32) fabs(val); | |
} | |
inline F32 mCos(const F32 angle) | |
{ | |
return (F32) cos(angle); | |
} | |
inline F32 mSin(const F32 angle) | |
{ | |
return (F32) sin(angle); | |
} | |
inline F32 mAsin(const F32 val) | |
{ | |
return (F32) asin(val); | |
} | |
inline F32 mAtan(const F32 x, const F32 y) | |
{ | |
return (F32) atan2(x, y); | |
} | |
inline U32 getNextPow2(U32 io_num); | |
/// Determines if the given U32 is some 2^n | |
/// @returns true if in_num is a power of two, otherwise false | |
inline bool isPow2(const U32 in_num) | |
{ | |
return (in_num == getNextPow2(in_num)); | |
} | |
// note: impl from T2D | |
inline U32 getNextPow2(U32 io_num) | |
{ | |
S32 oneCount = 0; | |
S32 shiftCount = -1; | |
while (io_num) { | |
if(io_num & 1) | |
oneCount++; | |
shiftCount++; | |
io_num >>= 1; | |
} | |
if(oneCount > 1) | |
shiftCount++; | |
return U32(1 << shiftCount); | |
} | |
// note: impl from T2D | |
inline U32 getBinLog2(U32 io_num) | |
{ | |
AssertFatal(io_num != 0 && isPow2(io_num) == true, | |
"Error, this only works on powers of 2 > 0"); | |
S32 shiftCount = 0; | |
while (io_num) { | |
shiftCount++; | |
io_num >>= 1; | |
} | |
return U32(shiftCount - 1); | |
} | |
/// Returns the lesser of the two parameters: a & b. | |
inline U32 getMin(U32 a, U32 b) | |
{ | |
return a>b ? b : a; | |
} | |
/// Returns the lesser of the two parameters: a & b. | |
inline U16 getMin(U16 a, U16 b) | |
{ | |
return a>b ? b : a; | |
} | |
/// Returns the lesser of the two parameters: a & b. | |
inline U8 getMin(U8 a, U8 b) | |
{ | |
return a>b ? b : a; | |
} | |
/// Returns the lesser of the two parameters: a & b. | |
inline S32 getMin(S32 a, S32 b) | |
{ | |
return a>b ? b : a; | |
} | |
/// Returns the lesser of the two parameters: a & b. | |
inline S16 getMin(S16 a, S16 b) | |
{ | |
return a>b ? b : a; | |
} | |
/// Returns the lesser of the two parameters: a & b. | |
inline S8 getMin(S8 a, S8 b) | |
{ | |
return a>b ? b : a; | |
} | |
/// Returns the lesser of the two parameters: a & b. | |
inline float getMin(float a, float b) | |
{ | |
return a>b ? b : a; | |
} | |
/// Returns the lesser of the two parameters: a & b. | |
inline double getMin(double a, double b) | |
{ | |
return a>b ? b : a; | |
} | |
/// Returns the greater of the two parameters: a & b. | |
inline U32 getMax(U32 a, U32 b) | |
{ | |
return a>b ? a : b; | |
} | |
/// Returns the greater of the two parameters: a & b. | |
inline U16 getMax(U16 a, U16 b) | |
{ | |
return a>b ? a : b; | |
} | |
/// Returns the greater of the two parameters: a & b. | |
inline U8 getMax(U8 a, U8 b) | |
{ | |
return a>b ? a : b; | |
} | |
/// Returns the greater of the two parameters: a & b. | |
inline S32 getMax(S32 a, S32 b) | |
{ | |
return a>b ? a : b; | |
} | |
/// Returns the greater of the two parameters: a & b. | |
inline S16 getMax(S16 a, S16 b) | |
{ | |
return a>b ? a : b; | |
} | |
/// Returns the greater of the two parameters: a & b. | |
inline S8 getMax(S8 a, S8 b) | |
{ | |
return a>b ? a : b; | |
} | |
/// Returns the greater of the two parameters: a & b. | |
inline float getMax(float a, float b) | |
{ | |
return a>b ? a : b; | |
} | |
/// Returns the greater of the two parameters: a & b. | |
inline double getMax(double a, double b) | |
{ | |
return a>b ? a : b; | |
} | |
inline S32 mClamp(S32 val, S32 low, S32 high) | |
{ | |
return getMax(getMin(val, high), low); | |
} | |
inline F32 mClampF(F32 val, F32 low, F32 high) | |
{ | |
return (F32) getMax(getMin(val, high), low); | |
} | |
/// A convenience class to manipulate a set of bits. | |
/// | |
/// Notice that bits are accessed directly, ie, by passing | |
/// a variable with the relevant bit set or not, instead of | |
/// passing the index of the relevant bit. | |
class BitSet32 | |
{ | |
private: | |
/// Internal representation of bitset. | |
U32 mbits; | |
public: | |
BitSet32() { mbits = 0; } | |
BitSet32(const BitSet32& in_rCopy) { mbits = in_rCopy.mbits; } | |
BitSet32(const U32 in_mask) { mbits = in_mask; } | |
operator U32() const { return mbits; } | |
U32 getMask() const { return mbits; } | |
/// Set all bits to true. | |
void set() { mbits = 0xFFFFFFFFUL; } | |
/// Set the specified bit(s) to true. | |
void set(const U32 m) { mbits |= m; } | |
/// Masked-set the bitset; ie, using s as the mask and then setting the masked bits | |
/// to b. | |
void set(BitSet32 s, bool b) { mbits = (mbits&~(s.mbits))|(b?s.mbits:0); } | |
/// Clear all bits. | |
void clear() { mbits = 0; } | |
/// Clear the specified bit(s). | |
void clear(const U32 m) { mbits &= ~m; } | |
/// Toggle the specified bit(s). | |
void toggle(const U32 m) { mbits ^= m; } | |
/// Are any of the specified bit(s) set? | |
bool test(const U32 m) const { return (mbits & m) != 0; } | |
/// Are ALL the specified bit(s) set? | |
bool testStrict(const U32 m) const { return (mbits & m) == m; } | |
/// @name Operator Overloads | |
/// @{ | |
BitSet32& operator =(const U32 m) { mbits = m; return *this; } | |
BitSet32& operator|=(const U32 m) { mbits |= m; return *this; } | |
BitSet32& operator&=(const U32 m) { mbits &= m; return *this; } | |
BitSet32& operator^=(const U32 m) { mbits ^= m; return *this; } | |
BitSet32 operator|(const U32 m) const { return BitSet32(mbits | m); } | |
BitSet32 operator&(const U32 m) const { return BitSet32(mbits & m); } | |
BitSet32 operator^(const U32 m) const { return BitSet32(mbits ^ m); } | |
/// @} | |
}; | |
class Point3F; | |
class HuffmanProcessor; | |
struct Point3F | |
{ | |
F32 x,y,z; | |
Point3F() : x(0), y(0), z(0) {;} | |
Point3F(F32 _x, F32 _y, F32 _z) : x(_x), y(_y), z(_z) { ; } | |
inline Point3F operator-(const Point3F& in) const | |
{ | |
return Point3F(x - in.x, y - in.y, z - in.z); | |
} | |
inline Point3F operator-=(const Point3F& in) | |
{ | |
x -= in.x; | |
y -= in.y; | |
z -= in.z; | |
return *this; | |
} | |
inline F32 len() | |
{ | |
return mSqrt(x*x + y*y + z*z); | |
} | |
}; | |
class Stream | |
{ | |
// Public structs and enumerations... | |
public: | |
/// Status constantants for the stream | |
enum Status { | |
Ok = 0, ///< Ok! | |
IOError, ///< Read or Write error | |
EOS, ///< End of Stream reached (mostly for reads) | |
IllegalCall, ///< An unsupported operation used. Always w/ accompanied by AssertWarn | |
Closed, ///< Tried to operate on a closed stream (or detached filter) | |
UnknownError ///< Catchall | |
}; | |
enum Capability { | |
StreamWrite = BIT(0), ///< Can this stream write? | |
StreamRead = BIT(1), ///< Can this stream read? | |
StreamPosition = BIT(2) ///< Can this stream position? | |
}; | |
// Accessible only through inline accessors | |
private: | |
Status m_streamStatus; | |
public: | |
virtual bool _read (const U32 size,void* d) = 0; | |
virtual bool _write(const U32 size,const void* d) = 0; | |
// Overloaded write and read ops.. | |
public: | |
bool read(const U32 in_numBytes, void* out_pBuffer) { | |
return _read(in_numBytes, out_pBuffer); | |
} | |
bool write(const U32 in_numBytes, const void* in_pBuffer) { | |
return _write(in_numBytes, in_pBuffer); | |
} | |
DECLARE_OVERLOADED_WRITE(S8) | |
DECLARE_OVERLOADED_WRITE(U8) | |
DECLARE_ENDIAN_OVERLOADED_WRITE(S16) | |
DECLARE_ENDIAN_OVERLOADED_WRITE(S32) | |
DECLARE_ENDIAN_OVERLOADED_WRITE(U16) | |
DECLARE_ENDIAN_OVERLOADED_WRITE(U32) | |
DECLARE_ENDIAN_OVERLOADED_WRITE(F32) | |
DECLARE_OVERLOADED_READ(S8) | |
DECLARE_OVERLOADED_READ(U8) | |
DECLARE_ENDIAN_OVERLOADED_READ(S16) | |
DECLARE_ENDIAN_OVERLOADED_READ(S32) | |
DECLARE_ENDIAN_OVERLOADED_READ(U16) | |
DECLARE_ENDIAN_OVERLOADED_READ(U32) | |
DECLARE_ENDIAN_OVERLOADED_READ(F32) | |
}; | |
// should be 52 bytes | |
class BitStream : public Stream | |
{ | |
public: | |
U8 *dataPtr; | |
S32 bitNum; | |
S32 bufSize; | |
bool error; | |
S32 maxReadBitNum; | |
S32 maxWriteBitNum; | |
char *stringBuffer; | |
bool mCompressRelative; | |
Point3F mCompressPoint; | |
friend class HuffmanProcessor; | |
public: | |
static BitStream *getPacketStream(U32 writeSize = 0); | |
static void sendPacketStream(const OVNetAddress *addr); | |
void setBuffer(void *bufPtr, S32 bufSize, S32 maxSize = 0); | |
U8* getBuffer() { return dataPtr; } | |
U8* getBytePtr(); | |
U32 getReadByteSize(); | |
S32 getCurPos() const; | |
void setCurPos(const U32); | |
BitStream(void *bufPtr, S32 bufSize, S32 maxWriteSize = -1) { setBuffer(bufPtr, bufSize,maxWriteSize); stringBuffer = NULL; } | |
void clear(); | |
void setStringBuffer(char buffer[256]); | |
void writeInt(S32 value, S32 bitCount); | |
S32 readInt(S32 bitCount); | |
/// Use this method to write out values in a concise but ass backwards way... | |
/// Good for values you expect to be frequently zero, often small. Worst case | |
/// this will bloat values by nearly 20% (5 extra bits!) Best case you'll get | |
/// one bit (if it's zero). | |
/// | |
/// This is not so much for efficiency's sake, as to make life painful for | |
/// people that want to reverse engineer our network or file formats. | |
void writeCussedU32(U32 val) | |
{ | |
// Is it zero? | |
if(writeFlag(val == 0)) | |
return; | |
if(writeFlag(val <= 0xF)) // 4 bit | |
writeRangedU32(val, 0, 0xF); | |
else if(writeFlag(val <= 0xFF)) // 8 bit | |
writeRangedU32(val, 0, 0xFF); | |
else if(writeFlag(val <= 0xFFFF)) // 16 bit | |
writeRangedU32(val, 0, 0xFFFF); | |
else if(writeFlag(val <= 0xFFFFFF)) // 24 bit | |
writeRangedU32(val, 0, 0xFFFFFF); | |
else | |
writeRangedU32(val, 0, 0xFFFFFFFF); | |
} | |
U32 readCussedU32() | |
{ | |
if(readFlag()) | |
return 0; | |
if(readFlag()) | |
return readRangedU32(0, 0xF); | |
else if(readFlag()) | |
return readRangedU32(0, 0xFF); | |
else if(readFlag()) | |
return readRangedU32(0, 0xFFFF); | |
else if(readFlag()) | |
return readRangedU32(0, 0xFFFFFF); | |
else | |
return readRangedU32(0, 0xFFFFFFFF); | |
} | |
void writeSignedInt(S32 value, S32 bitCount); | |
S32 readSignedInt(S32 bitCount); | |
void writeRangedU32(U32 value, U32 rangeStart, U32 rangeEnd); | |
U32 readRangedU32(U32 rangeStart, U32 rangeEnd); | |
/// Writes a clamped signed integer to the stream using | |
/// an optimal amount of bits for the range. | |
void writeRangedS32( S32 value, S32 min, S32 max ); | |
/// Reads a ranged signed integer written with writeRangedS32. | |
S32 readRangedS32( S32 min, S32 max ); | |
// read and write floats... floats are 0 to 1 inclusive, signed floats are -1 to 1 inclusive | |
F32 readFloat(S32 bitCount); | |
F32 readSignedFloat(S32 bitCount); | |
void writeFloat(F32 f, S32 bitCount); | |
void writeSignedFloat(F32 f, S32 bitCount); | |
/// Writes a clamped floating point value to the | |
/// stream with the desired bits of precision. | |
void writeRangedF32( F32 value, F32 min, F32 max, U32 numBits ); | |
/// Reads a ranged floating point value written with writeRangedF32. | |
F32 readRangedF32( F32 min, F32 max, U32 numBits ); | |
void writeClassId(U32 classId, U32 classType, U32 classGroup); | |
S32 readClassId(U32 classType, U32 classGroup); // returns -1 if the class type is out of range | |
// writes a normalized vector | |
void writeNormalVector(const Point3F& vec, S32 bitCount); | |
void readNormalVector(Point3F *vec, S32 bitCount); | |
void clearCompressionPoint(); | |
void setCompressionPoint(const Point3F& p); | |
// Matching calls to these compression methods must, of course, | |
// have matching scale values. | |
void writeCompressedPoint(const Point3F& p,F32 scale = 0.01f); | |
void readCompressedPoint(Point3F* p,F32 scale = 0.01f); | |
// Uses the above method to reduce the precision of a normal vector so the server can | |
// determine exactly what is on the client. (Pre-dumbing the vector before sending | |
// to the client can result in precision errors...) | |
static Point3F dumbDownNormal(const Point3F& vec, S32 bitCount); | |
// writes a normalized vector using alternate method | |
void writeNormalVector(const Point3F& vec, S32 angleBitCount, S32 zBitCount); | |
void readNormalVector(Point3F *vec, S32 angleBitCount, S32 zBitCount); | |
void readVector(Point3F * vec, F32 minMag, F32 maxMag, S32 magBits, S32 angleBits, S32 zBits); | |
void writeVector(Point3F vec, F32 minMag, F32 maxMag, S32 magBits, S32 angleBits, S32 zBits); | |
virtual void writeBits(S32 bitCount, const void *bitPtr); | |
virtual void readBits(S32 bitCount, void *bitPtr); | |
virtual bool writeFlag(bool val); | |
virtual bool readFlag(); | |
void setBit(S32 bitCount, bool set); | |
bool testBit(S32 bitCount); | |
bool isFull() { return bitNum > (bufSize << 3); } | |
bool isValid() { return !error; } | |
bool _read (const U32 size,void* d); | |
bool _write(const U32 size,const void* d); | |
void readString(char stringBuf[256]); | |
void writeString(const char *stringBuf, S32 maxLen=255); | |
bool hasCapability(const Capability) const { return true; } | |
U32 getPosition() const; | |
bool setPosition(const U32 in_newPosition); | |
U32 getStreamSize(); | |
static void copyState(BitStream* in, BitStream* out) | |
{ | |
out->dataPtr = in->dataPtr; | |
out->bitNum = in->bitNum; | |
out->bufSize = in->bufSize; | |
out->error = in->error; | |
out->maxReadBitNum = in->maxReadBitNum; | |
out->maxWriteBitNum = in->maxWriteBitNum; | |
out->stringBuffer = in->stringBuffer; | |
out->mCompressRelative = in->mCompressRelative; | |
out->mCompressPoint = in->mCompressPoint; | |
} | |
}; | |
//------------------------------------------------------------------------------ | |
//-------------------------------------- INLINES | |
// | |
inline S32 BitStream::getCurPos() const | |
{ | |
return bitNum; | |
} | |
inline void BitStream::setCurPos(const U32 in_position) | |
{ | |
AssertFatal(in_position < (U32)(bufSize << 3), "Out of range bitposition"); | |
bitNum = S32(in_position); | |
} | |
inline bool BitStream::readFlag() | |
{ | |
if(bitNum > maxReadBitNum) | |
{ | |
error = true; | |
AssertFatal(false, "Out of range read"); | |
return false; | |
} | |
S32 mask = 1 << (bitNum & 0x7); | |
bool ret = (*(dataPtr + (bitNum >> 3)) & mask) != 0; | |
bitNum++; | |
return ret; | |
} | |
inline void BitStream::writeRangedU32(U32 value, U32 rangeStart, U32 rangeEnd) | |
{ | |
AssertFatal(value >= rangeStart && value <= rangeEnd, "Out of bounds value!"); | |
AssertFatal(rangeEnd >= rangeStart, "error, end of range less than start"); | |
U32 rangeSize = rangeEnd - rangeStart + 1; | |
U32 rangeBits = getBinLog2(getNextPow2(rangeSize)); | |
writeInt(S32(value - rangeStart), S32(rangeBits)); | |
} | |
inline U32 BitStream::readRangedU32(U32 rangeStart, U32 rangeEnd) | |
{ | |
AssertFatal(rangeEnd >= rangeStart, "error, end of range less than start"); | |
U32 rangeSize = rangeEnd - rangeStart + 1; | |
U32 rangeBits = getBinLog2(getNextPow2(rangeSize)); | |
U32 val = U32(readInt(S32(rangeBits))); | |
return val + rangeStart; | |
} | |
inline void BitStream::writeRangedS32( S32 value, S32 min, S32 max ) | |
{ | |
value = mClamp( value, min, max ); | |
writeRangedU32( ( value - min ), 0, ( max - min ) ); | |
} | |
inline S32 BitStream::readRangedS32( S32 min, S32 max ) | |
{ | |
return readRangedU32( 0, ( max - min ) ) + min; | |
} | |
inline void BitStream::writeRangedF32( F32 value, F32 min, F32 max, U32 numBits ) | |
{ | |
value = ( mClampF( value, min, max ) - min ) / ( max - min ); | |
writeInt( (S32)(value * ( (1 << numBits) - 1 )), numBits ); | |
} | |
inline F32 BitStream::readRangedF32( F32 min, F32 max, U32 numBits ) | |
{ | |
F32 value = (F32)readInt( numBits ); | |
value /= F32( ( 1 << numBits ) - 1 ); | |
return min + value * ( max - min ); | |
} | |
static BitStream gPacketStream(NULL, 0); | |
static U8 gPacketBuffer[MaxPacketDataSize]; | |
// bitstream utility functions | |
void BitStream::setStringBuffer(char buffer[256]) | |
{ | |
stringBuffer = buffer; | |
} | |
typedef BitStream* (*fpGetPacketStream)(void); | |
BitStream *BitStream::getPacketStream(U32 writeSize) | |
{ | |
// hook into orig ver | |
// @ 0x000adc02 | |
const fpGetPacketStream _func = (fpGetPacketStream)0x53780; | |
return _func(); | |
} | |
typedef const char* (*fpSendPacketStream)(const OVNetAddress*); | |
void BitStream::sendPacketStream(const OVNetAddress *addr) | |
{ | |
// hook into orig ver | |
const fpSendPacketStream _func = (fpSendPacketStream)0x53aa0; | |
_func(addr); | |
} | |
// FIXMEFIXMEFIXME MATH | |
inline bool IsEqual(F32 a, F32 b) { return a == b; } | |
class HuffmanProcessor | |
{ | |
static const U32 csm_charFreqs[256]; | |
bool m_tablesBuilt; | |
void buildTables(); | |
struct HuffNode { | |
U32 pop; | |
S16 index0; | |
S16 index1; | |
}; | |
struct HuffLeaf { | |
U32 pop; | |
U8 numBits; | |
U8 symbol; | |
U32 code; // no code should be longer than 32 bits. | |
}; | |
// We have to be a bit careful with these, mSince they are pointers... | |
struct HuffWrap { | |
HuffNode* pNode; | |
HuffLeaf* pLeaf; | |
public: | |
HuffWrap() : pNode(NULL), pLeaf(NULL) { } | |
void set(HuffLeaf* in_leaf) { pNode = NULL; pLeaf = in_leaf; } | |
void set(HuffNode* in_node) { pLeaf = NULL; pNode = in_node; } | |
U32 getPop() { if (pNode) return pNode->pop; else return pLeaf->pop; } | |
}; | |
std::vector<HuffNode> m_huffNodes; | |
std::vector<HuffLeaf> m_huffLeaves; | |
S16 determineIndex(HuffWrap&); | |
void generateCodes(BitStream&, S32, S32); | |
public: | |
HuffmanProcessor() : m_tablesBuilt(false) { } | |
static HuffmanProcessor g_huffProcessor; | |
bool readHuffBuffer(BitStream* pStream, char* out_pBuffer); | |
bool writeHuffBuffer(BitStream* pStream, const char* out_pBuffer, S32 maxLen); | |
}; | |
HuffmanProcessor HuffmanProcessor::g_huffProcessor; | |
void BitStream::setBuffer(void *bufPtr, S32 size, S32 maxSize) | |
{ | |
dataPtr = (U8 *) bufPtr; | |
bitNum = 0; | |
bufSize = size; | |
maxReadBitNum = size << 3; | |
if(maxSize < 0) | |
maxSize = size; | |
maxWriteBitNum = maxSize << 3; | |
error = false; | |
mCompressRelative = false; | |
} | |
U32 BitStream::getPosition() const | |
{ | |
return (bitNum + 7) >> 3; | |
} | |
bool BitStream::setPosition(const U32 pos) | |
{ | |
bitNum = pos << 3; | |
return (true); | |
} | |
U32 BitStream::getStreamSize() | |
{ | |
return bufSize; | |
} | |
U8 *BitStream::getBytePtr() | |
{ | |
return dataPtr + getPosition(); | |
} | |
U32 BitStream::getReadByteSize() | |
{ | |
return (maxReadBitNum >> 3) - getPosition(); | |
} | |
void BitStream::clear() | |
{ | |
memset(dataPtr, 0, bufSize); | |
} | |
void BitStream::writeClassId(U32 classId, U32 classType, U32 classGroup) | |
{ | |
//AssertFatal(classType < NetClassTypesCount, "Out of range class type."); | |
//AssertFatal(classId < AbstractClassRep::NetClassCount[classGroup][classType], "Out of range class id."); | |
//writeInt(classId, AbstractClassRep::NetClassBitSize[classGroup][classType]); | |
} | |
//----------------------------------------------------------------------------- | |
enum NetClassTypes { | |
NetClassTypeObject = 0, | |
NetClassTypeDataBlock, | |
NetClassTypeEvent, | |
NetClassTypesCount, | |
}; | |
//----------------------------------------------------------------------------- | |
enum NetClassGroups { | |
NetClassGroupGame = 0, | |
NetClassGroupCommunity, | |
NetClassGroup3, | |
NetClassGroup4, | |
NetClassGroupsCount, | |
}; | |
U32 __NetClassBitSize[NetClassGroupsCount][NetClassTypesCount]; | |
S32 BitStream::readClassId(U32 classType, U32 classGroup) | |
{ | |
//AssertFatal(classType < NetClassTypesCount, "Out of range class type."); | |
printf("Type %u group %u\n", classType, classGroup); | |
S32 ret = readInt(__NetClassBitSize[classGroup][classType]); | |
//printf("Read packet bits: %u\n", __NetClassBitSize[classGroup][classType]); | |
return ret; | |
} | |
void BitStream::writeBits(S32 bitCount, const void *bitPtr) | |
{ | |
if(!bitCount) | |
return; | |
if(bitCount + bitNum > maxWriteBitNum) | |
{ | |
error = true; | |
AssertFatal(false, "Out of range write"); | |
return; | |
} | |
// [tom, 8/17/2006] This is probably a lot lamer then it needs to be. However, | |
// at least it doesnt clobber data or overrun the buffer like the old code did. | |
const U8 *ptr = (U8 *)bitPtr; | |
for(S32 srcBitNum = 0;srcBitNum < bitCount;srcBitNum++) | |
{ | |
if((*(ptr + (srcBitNum >> 3)) & (1 << (srcBitNum & 0x7))) != 0) | |
*(dataPtr + (bitNum >> 3)) |= (1 << (bitNum & 0x7)); | |
else | |
*(dataPtr + (bitNum >> 3)) &= ~(1 << (bitNum & 0x7)); | |
bitNum++; | |
} | |
} | |
void BitStream::setBit(S32 bitCount, bool set) | |
{ | |
if(set) | |
*(dataPtr + (bitCount >> 3)) |= (1 << (bitCount & 0x7)); | |
else | |
*(dataPtr + (bitCount >> 3)) &= ~(1 << (bitCount & 0x7)); | |
} | |
bool BitStream::testBit(S32 bitCount) | |
{ | |
return (*(dataPtr + (bitCount >> 3)) & (1 << (bitCount & 0x7))) != 0; | |
} | |
bool BitStream::writeFlag(bool val) | |
{ | |
if(bitNum + 1 > maxWriteBitNum) | |
{ | |
error = true; | |
AssertFatal(false, "Out of range write"); | |
return false; | |
} | |
if(val) | |
*(dataPtr + (bitNum >> 3)) |= (1 << (bitNum & 0x7)); | |
else | |
*(dataPtr + (bitNum >> 3)) &= ~(1 << (bitNum & 0x7)); | |
bitNum++; | |
return (val); | |
} | |
void BitStream::readBits(S32 bitCount, void *bitPtr) | |
{ | |
if(!bitCount) | |
return; | |
if(bitCount + bitNum > maxReadBitNum) | |
{ | |
error = true; | |
AssertFatal(false, "Out of range read"); | |
//AssertWarn(false, "Out of range read"); | |
return; | |
} | |
U8 *stPtr = dataPtr + (bitNum >> 3); | |
S32 byteCount = (bitCount + 7) >> 3; | |
U8 *ptr = (U8 *) bitPtr; | |
S32 downShift = bitNum & 0x7; | |
S32 upShift = 8 - downShift; | |
U8 curB = *stPtr; | |
while(byteCount--) | |
{ | |
U8 nextB = *++stPtr; | |
*ptr++ = (curB >> downShift) | (nextB << upShift); | |
curB = nextB; | |
} | |
bitNum += bitCount; | |
} | |
bool BitStream::_read(U32 size, void *dataPtr) | |
{ | |
readBits(size << 3, dataPtr); | |
return true; | |
} | |
bool BitStream::_write(U32 size, const void *dataPtr) | |
{ | |
writeBits(size << 3, dataPtr); | |
return true; | |
} | |
S32 BitStream::readInt(S32 bitCount) | |
{ | |
S32 ret = 0; | |
readBits(bitCount, &ret); | |
ret = (ret); | |
if(bitCount == 32) | |
return ret; | |
else | |
ret &= (1 << bitCount) - 1; | |
return ret; | |
} | |
void BitStream::writeInt(S32 val, S32 bitCount) | |
{ | |
val = (val); | |
writeBits(bitCount, &val); | |
} | |
void BitStream::writeFloat(F32 f, S32 bitCount) | |
{ | |
writeInt((S32)(f * ((1 << bitCount) - 1)), bitCount); | |
} | |
F32 BitStream::readFloat(S32 bitCount) | |
{ | |
return readInt(bitCount) / F32((1 << bitCount) - 1); | |
} | |
void BitStream::writeSignedFloat(F32 f, S32 bitCount) | |
{ | |
writeInt((S32)(((f + 1) * .5) * ((1 << bitCount) - 1)), bitCount); | |
} | |
F32 BitStream::readSignedFloat(S32 bitCount) | |
{ | |
return readInt(bitCount) * 2 / F32((1 << bitCount) - 1) - 1.0f; | |
} | |
void BitStream::writeSignedInt(S32 value, S32 bitCount) | |
{ | |
if(writeFlag(value < 0)) | |
writeInt(-value, bitCount - 1); | |
else | |
writeInt(value, bitCount - 1); | |
} | |
S32 BitStream::readSignedInt(S32 bitCount) | |
{ | |
if(readFlag()) | |
return -readInt(bitCount - 1); | |
else | |
return readInt(bitCount - 1); | |
} | |
void BitStream::writeNormalVector(const Point3F& vec, S32 bitCount) | |
{ | |
F32 phi = mAtan(vec.x, vec.y) / (F32)M_PI; | |
F32 theta = mAtan(vec.z, mSqrt(vec.x*vec.x + vec.y*vec.y)) / ((F32)M_PI/2.0f); | |
writeSignedFloat(phi, bitCount+1); | |
writeSignedFloat(theta, bitCount); | |
} | |
void BitStream::readNormalVector(Point3F *vec, S32 bitCount) | |
{ | |
F32 phi = readSignedFloat(bitCount+1) * (U32)M_PI; | |
F32 theta = readSignedFloat(bitCount) * ((F32)M_PI/2.0f); | |
vec->x = mSin(phi)*mCos(theta); | |
vec->y = mCos(phi)*mCos(theta); | |
vec->z = mSin(theta); | |
} | |
Point3F BitStream::dumbDownNormal(const Point3F& vec, S32 bitCount) | |
{ | |
U8 buffer[128]; | |
BitStream temp(buffer, 128); | |
temp.writeNormalVector(vec, bitCount); | |
temp.setCurPos(0); | |
Point3F ret; | |
temp.readNormalVector(&ret, bitCount); | |
return ret; | |
} | |
void BitStream::writeNormalVector(const Point3F& vec, S32 angleBitCount, S32 zBitCount) | |
{ | |
writeSignedFloat( vec.z, zBitCount ); | |
// don't need to write x and y if they are both zero, which we can assess | |
// by checking for |z| == 1 | |
if(!IsEqual(mFabs(vec.z), 1.0f)) | |
{ | |
writeSignedFloat( mAtan(vec.x,vec.y) / (F32)M_2PI, angleBitCount ); | |
} | |
else | |
{ | |
// angle won't matter... | |
writeSignedFloat(0.0f,angleBitCount); | |
} | |
} | |
void BitStream::readNormalVector(Point3F * vec, S32 angleBitCount, S32 zBitCount) | |
{ | |
vec->z = readSignedFloat(zBitCount); | |
F32 angle = (F32)M_2PI * readSignedFloat(angleBitCount); | |
F32 mult = mSqrt(1.0f - vec->z * vec->z); | |
vec->x = mult * mSin(angle); | |
vec->y = mult * mCos(angle); | |
} | |
//---------------------------------------------------------------------------- | |
void BitStream::clearCompressionPoint() | |
{ | |
mCompressRelative = false; | |
} | |
void BitStream::setCompressionPoint(const Point3F& p) | |
{ | |
mCompressRelative = true; | |
mCompressPoint = p; | |
} | |
static U32 gBitCounts[4] = { | |
16, 18, 20, 32 | |
}; | |
void BitStream::writeCompressedPoint(const Point3F& p,F32 scale) | |
{ | |
// Same # of bits for all axis | |
Point3F vec; | |
F32 invScale = 1 / scale; | |
U32 type; | |
if(mCompressRelative) | |
{ | |
vec = p - mCompressPoint; | |
F32 dist = vec.len() * invScale; | |
if(dist < (1 << 15)) | |
type = 0; | |
else if(dist < (1 << 17)) | |
type = 1; | |
else if(dist < (1 << 19)) | |
type = 2; | |
else | |
type = 3; | |
} | |
else | |
type = 3; | |
writeInt(type, 2); | |
if (type != 3) | |
{ | |
type = gBitCounts[type]; | |
writeSignedInt(S32(vec.x * invScale),type); | |
writeSignedInt(S32(vec.y * invScale),type); | |
writeSignedInt(S32(vec.z * invScale),type); | |
} | |
else | |
{ | |
write(p.x); | |
write(p.y); | |
write(p.z); | |
} | |
} | |
void BitStream::readCompressedPoint(Point3F* p,F32 scale) | |
{ | |
// Same # of bits for all axis | |
U32 type = readInt(2); | |
if(type == 3) | |
{ | |
read(&p->x); | |
read(&p->y); | |
read(&p->z); | |
} | |
else | |
{ | |
type = gBitCounts[type]; | |
p->x = (F32)readSignedInt(type); | |
p->y = (F32)readSignedInt(type); | |
p->z = (F32)readSignedInt(type); | |
p->x = mCompressPoint.x + p->x * scale; | |
p->y = mCompressPoint.y + p->y * scale; | |
p->z = mCompressPoint.z + p->z * scale; | |
} | |
} | |
//------------------------------------------------------------------------------ | |
void BitStream::readString(char buf[256]) | |
{ | |
if(stringBuffer) | |
{ | |
if(readFlag()) | |
{ | |
if (error) | |
return; | |
S32 offset = readInt(8); | |
//printf("offset %i\n", offset); | |
HuffmanProcessor::g_huffProcessor.readHuffBuffer(this, stringBuffer + offset); | |
stringBuffer[offset + 255] = '\0'; | |
if (!error) strcpy(buf, stringBuffer); | |
return; | |
} | |
} | |
HuffmanProcessor::g_huffProcessor.readHuffBuffer(this, buf); | |
stringBuffer[255] = '\0'; | |
if(stringBuffer && !error) | |
strcpy(stringBuffer, buf); | |
} | |
void BitStream::writeString(const char *string, S32 maxLen) | |
{ | |
if(!string) | |
string = ""; | |
if(stringBuffer) | |
{ | |
S32 j; | |
for(j = 0; j < maxLen && stringBuffer[j] == string[j] && string[j];j++) | |
; | |
strncpy(stringBuffer, string, maxLen); | |
stringBuffer[maxLen] = 0; | |
if(writeFlag(j > 2)) | |
{ | |
writeInt(j, 8); | |
HuffmanProcessor::g_huffProcessor.writeHuffBuffer(this, string + j, maxLen - j); | |
return; | |
} | |
} | |
HuffmanProcessor::g_huffProcessor.writeHuffBuffer(this, string, maxLen); | |
} | |
void HuffmanProcessor::buildTables() | |
{ | |
AssertFatal(m_tablesBuilt == false, "Cannot build tables twice!"); | |
m_tablesBuilt = true; | |
S32 i; | |
// First, construct the array of wraps... | |
// | |
m_huffLeaves.resize(256); | |
m_huffNodes.reserve(256); | |
m_huffNodes.push_back(HuffNode()); | |
for (i = 0; i < 256; i++) { | |
HuffLeaf& rLeaf = m_huffLeaves[i]; | |
rLeaf.pop = csm_charFreqs[i] + 1; | |
rLeaf.symbol = U8(i); | |
memset(&rLeaf.code, 0, sizeof(rLeaf.code)); | |
rLeaf.numBits = 0; | |
} | |
S32 currWraps = 256; | |
HuffWrap* pWrap = new HuffWrap[256]; | |
for (i = 0; i < 256; i++) { | |
pWrap[i].set(&m_huffLeaves[i]); | |
} | |
while (currWraps != 1) { | |
U32 min1 = 0xfffffffe, min2 = 0xffffffff; | |
S32 index1 = -1, index2 = -1; | |
for (i = 0; i < currWraps; i++) { | |
if (pWrap[i].getPop() < min1) { | |
min2 = min1; | |
index2 = index1; | |
min1 = pWrap[i].getPop(); | |
index1 = i; | |
} else if (pWrap[i].getPop() < min2) { | |
min2 = pWrap[i].getPop(); | |
index2 = i; | |
} | |
} | |
AssertFatal(index1 != -1 && index2 != -1 && index1 != index2, "hrph"); | |
// Create a node for this... | |
m_huffNodes.push_back(HuffNode()); | |
HuffNode& rNode = m_huffNodes.back(); | |
rNode.pop = pWrap[index1].getPop() + pWrap[index2].getPop(); | |
rNode.index0 = determineIndex(pWrap[index1]); | |
rNode.index1 = determineIndex(pWrap[index2]); | |
S32 mergeIndex = index1 > index2 ? index2 : index1; | |
S32 nukeIndex = index1 > index2 ? index1 : index2; | |
pWrap[mergeIndex].set(&rNode); | |
if (index2 != (currWraps - 1)) { | |
pWrap[nukeIndex] = pWrap[currWraps - 1]; | |
} | |
currWraps--; | |
} | |
AssertFatal(currWraps == 1, "wrong wraps?"); | |
AssertFatal(pWrap[0].pNode != NULL && pWrap[0].pLeaf == NULL, "Wrong wrap type!"); | |
// Ok, now we have one wrap, which is a node. we need to make sure that this | |
// is the first node in the node list. | |
m_huffNodes[0] = *(pWrap[0].pNode); | |
delete [] pWrap; | |
U32 code = 0; | |
BitStream bs(&code, 4); | |
generateCodes(bs, 0, 0); | |
} | |
void HuffmanProcessor::generateCodes(BitStream& rBS, S32 index, S32 depth) | |
{ | |
if (index < 0) { | |
// leaf node, copy the code in, and back out... | |
HuffLeaf& rLeaf = m_huffLeaves[-(index + 1)]; | |
memcpy(&rLeaf.code, rBS.dataPtr, sizeof(rLeaf.code)); | |
rLeaf.numBits = depth; | |
} else { | |
HuffNode& rNode = m_huffNodes[index]; | |
S32 pos = rBS.getCurPos(); | |
rBS.writeFlag(false); | |
generateCodes(rBS, rNode.index0, depth + 1); | |
rBS.setCurPos(pos); | |
rBS.writeFlag(true); | |
generateCodes(rBS, rNode.index1, depth + 1); | |
rBS.setCurPos(pos); | |
} | |
} | |
S16 HuffmanProcessor::determineIndex(HuffWrap& rWrap) | |
{ | |
if (rWrap.pLeaf != NULL) { | |
AssertFatal(rWrap.pNode == NULL, "Got a non-NULL pNode in a HuffWrap with a non-NULL leaf."); | |
return -((rWrap.pLeaf - m_huffLeaves.data()) + 1); | |
} else { | |
AssertFatal(rWrap.pNode != NULL, "Got a NULL pNode in a HuffWrap with a NULL leaf."); | |
return rWrap.pNode - m_huffNodes.data(); | |
} | |
} | |
bool HuffmanProcessor::readHuffBuffer(BitStream* pStream, char* out_pBuffer) | |
{ | |
if (m_tablesBuilt == false) | |
buildTables(); | |
if (pStream->readFlag()) { | |
if (pStream->error) | |
return false; | |
S32 len = pStream->readInt(8); | |
for (S32 i = 0; i < len; i++) { | |
if (pStream->error) | |
return false; | |
S32 index = 0; | |
while (true) { | |
if (index >= 0) { | |
if (pStream->readFlag() == true) { | |
index = m_huffNodes[index].index1; | |
} else { | |
index = m_huffNodes[index].index0; | |
} | |
} else { | |
out_pBuffer[i] = m_huffLeaves[-(index+1)].symbol; | |
break; | |
} | |
} | |
} | |
out_pBuffer[len] = '\0'; | |
return true; | |
} else { | |
// Uncompressed string... | |
U32 len = pStream->readInt(8); | |
if (pStream->error) | |
return false; | |
pStream->read(len, out_pBuffer); | |
out_pBuffer[len] = '\0'; | |
return true; | |
} | |
} | |
bool HuffmanProcessor::writeHuffBuffer(BitStream* pStream, const char* out_pBuffer, S32 maxLen) | |
{ | |
if (out_pBuffer == NULL) { | |
pStream->writeFlag(false); | |
pStream->writeInt(0, 8); | |
return true; | |
} | |
if (m_tablesBuilt == false) | |
buildTables(); | |
S32 len = out_pBuffer ? strlen(out_pBuffer) : 0; | |
//AssertWarn(len <= 255, "String TOO long for writeString"); | |
//AssertWarn(len <= 255, out_pBuffer); | |
if (len > maxLen) | |
len = maxLen; | |
S32 numBits = 0; | |
S32 i; | |
for (i = 0; i < len; i++) | |
numBits += m_huffLeaves[(unsigned char)out_pBuffer[i]].numBits; | |
if (numBits >= (len * 8)) { | |
pStream->writeFlag(false); | |
pStream->writeInt(len, 8); | |
pStream->write(len, out_pBuffer); | |
} else { | |
pStream->writeFlag(true); | |
pStream->writeInt(len, 8); | |
for (i = 0; i < len; i++) { | |
HuffLeaf& rLeaf = m_huffLeaves[((unsigned char)out_pBuffer[i])]; | |
pStream->writeBits(rLeaf.numBits, &rLeaf.code); | |
} | |
} | |
return true; | |
} | |
const U32 HuffmanProcessor::csm_charFreqs[256] = { | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
329 , | |
21 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
2809 , | |
68 , | |
0 , | |
27 , | |
0 , | |
58 , | |
3 , | |
62 , | |
4 , | |
7 , | |
0 , | |
0 , | |
15 , | |
65 , | |
554 , | |
3 , | |
394 , | |
404 , | |
189 , | |
117 , | |
30 , | |
51 , | |
27 , | |
15 , | |
34 , | |
32 , | |
80 , | |
1 , | |
142 , | |
3 , | |
142 , | |
39 , | |
0 , | |
144 , | |
125 , | |
44 , | |
122 , | |
275 , | |
70 , | |
135 , | |
61 , | |
127 , | |
8 , | |
12 , | |
113 , | |
246 , | |
122 , | |
36 , | |
185 , | |
1 , | |
149 , | |
309 , | |
335 , | |
12 , | |
11 , | |
14 , | |
54 , | |
151 , | |
0 , | |
0 , | |
2 , | |
0 , | |
0 , | |
211 , | |
0 , | |
2090 , | |
344 , | |
736 , | |
993 , | |
2872 , | |
701 , | |
605 , | |
646 , | |
1552 , | |
328 , | |
305 , | |
1240 , | |
735 , | |
1533 , | |
1713 , | |
562 , | |
3 , | |
1775 , | |
1149 , | |
1469 , | |
979 , | |
407 , | |
553 , | |
59 , | |
279 , | |
31 , | |
0 , | |
0 , | |
0 , | |
68 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 , | |
0 | |
}; | |
struct OVNetAddress { | |
int type; ///< Type of address (IPAddress currently) | |
/// Acceptable NetAddress types. | |
enum { | |
IPAddress, | |
IPXAddress | |
}; | |
U8 netNum[4]; ///< For IP: sin_addr<br> | |
/// For IPX: sa_netnum | |
U16 port; ///< For IP: sin_port<br> | |
/// For IPX: sa_socket | |
}; | |
#include <iostream> // std::cin, std::cout | |
#include <fstream> // std::filebuf | |
const char* packetNames[] = { | |
"Data", | |
"Ping", | |
"Ack" | |
}; | |
enum PacketTypes | |
{ | |
DataPacket, | |
PingPacket, | |
AckPacket, | |
InvalidPacketType, | |
}; | |
typedef bool (*fpReadUpdatePacketType)(BitStream&); | |
struct PacketReadClass | |
{ | |
U32 index; | |
U32 typeID; | |
const char* name; | |
fpReadUpdatePacketType readFunc; | |
}; | |
bool doReadGeneralTransmitField(BitStream &s) | |
{ | |
char buf1[256]; | |
char buf2[256]; | |
U32 num = 0; | |
int num2 = 0; | |
s.readString(buf1); | |
num = s.readRangedU32(0, 0x9fff); | |
printf("STR=%s, int=%u int2=%i\n", buf1, num, num2); | |
return true; | |
} | |
bool doReadClientTransmitPersistField(BitStream &s) | |
{ | |
char buf1[256]; | |
char buf2[256]; | |
buf1[0] = '\0'; | |
buf2[0] = '\0'; | |
U32 num = 0; | |
int num2 = 0; | |
s.readString(buf1); | |
num = s.readRangedU32(0, 0x8000); | |
s.read(&num2); | |
printf("STR=%s, int=%u int2=%i\n", buf1, num, num2); | |
return true; | |
} | |
const U32 GhostIdBitSize = 0xf; | |
const U32 GhostIndexBitSize = 0x4; | |
bool doReadConnectionMessageEvent(BitStream &s) | |
{ | |
U32 seq; | |
U32 msg; | |
U32 gc; | |
s.read(&seq); | |
msg = s.readInt(3); | |
s.readInt(GhostIdBitSize + 1); | |
return true; | |
} | |
PacketReadClass sReadClasses[] = { | |
13, NetClassTypeEvent, "GeneralTransmitField", &doReadGeneralTransmitField, | |
7, NetClassTypeEvent, "ClientTransmitPersistField", &doReadClientTransmitPersistField, | |
6, NetClassTypeObject, "ClientEditAIPlayerObject", NULL, | |
26, NetClassTypeEvent, "UpdateClientAIPlayerPersistField", NULL, | |
52, NetClassTypeObject, "MountablePlayer", NULL, | |
21, NetClassTypeDataBlock, "MountablePlayerData", NULL, | |
18, NetClassTypeObject, "ClientEditLightning", NULL, | |
38, NetClassTypeObject, "GameScriptObject", NULL, | |
12, NetClassTypeObject, "ClientEditGameScriptObject", NULL, | |
27, NetClassTypeEvent, "UpdateClientGameScriptPersistField", NULL, | |
39, NetClassTypeObject, "GiftItem", NULL, | |
28, NetClassTypeDataBlock, "PowerupData", NULL, | |
5, NetClassTypeObject, "CameraBlocker", NULL, | |
59, NetClassTypeObject, "PlayerBlocker", NULL, | |
31, NetClassTypeObject, "ClientEditVehicleBlocker", NULL, | |
9, NetClassTypeObject, "ClientEditCameraBlocker", NULL, | |
20, NetClassTypeObject, "ClientEditPlayerBlocker", NULL, | |
8, NetClassTypeObject, "ClientEditCamera", NULL, | |
22, NetClassTypeObject, "ClientEditPrecipitation", NULL, | |
40, NetClassTypeObject, "GuidedProjectile", NULL, | |
73, NetClassTypeObject, "ShoulderPet", NULL, | |
35, NetClassTypeObject, "ShoulderPetData", NULL, | |
0, NetClassTypeObject, "AIPet", NULL, | |
0, NetClassTypeDataBlock, "AIPetData", NULL, | |
36, NetClassTypeObject, "GameArea", NULL, | |
11, NetClassTypeObject, "ClientEditGameArea", NULL, | |
40, NetClassTypeDataBlock, "ToolImageData", NULL, | |
30, NetClassTypeObject, "ClientEditTrigger", NULL, | |
79, NetClassTypeObject, "StoreArea", NULL, | |
69, NetClassTypeObject, "PurchaseWheeledVehicle", NULL, | |
66, NetClassTypeObject, "PurchaseMountablePlayer", NULL, | |
67, NetClassTypeObject, "PurchaseShoulderPet", NULL, | |
63, NetClassTypeObject, "PurchaseAIPet", NULL, | |
68, NetClassTypeObject, "PurchaseToolObject", NULL, | |
64, NetClassTypeObject, "PurchaseClothingObject", NULL, | |
65, NetClassTypeObject, "PurchaseInteractiveObject", NULL, | |
25, NetClassTypeObject, "ClientEditStoreArea", NULL, | |
14, NetClassTypeObject, "ClientEditHomePoint", NULL, | |
13, NetClassTypeObject, "ClientEditHomeArea", NULL, | |
42, NetClassTypeObject, "HomePoint", NULL, | |
15, NetClassTypeDataBlock, "HomePointData", NULL, | |
41, NetClassTypeObject, "HomeArea", NULL, | |
28, NetClassTypeEvent, "UpdateClientInteractivePersistField", NULL, | |
15, NetClassTypeObject, "ClientEditInteractiveObject", NULL, | |
7, NetClassTypeObject, "ClientEditInteractiveObjectData", NULL, | |
44, NetClassTypeObject, "InteractiveObject", NULL, | |
17, NetClassTypeDataBlock, "InteractiveObjectData", NULL, | |
29, NetClassTypeEvent, "UpdatePersistField", NULL, | |
27, NetClassTypeObject, "ClientEditSunLight", NULL, | |
19, NetClassTypeObject, "ClientEditParticleEmitterNode", NULL, | |
17, NetClassTypeObject, "ClientEditLightObject", NULL, | |
7, NetClassTypeObject, "ClientEditAudioEmitter", NULL, | |
60, NetClassTypeObject, "PoiPoint", NULL, | |
21, NetClassTypeObject, "ClientEditPoiPoint", NULL, | |
29, NetClassTypeObject, "ClientEditTerrainBlock", NULL, | |
3, NetClassTypeEvent, "ClientTerrainAckStart", NULL, | |
5, NetClassTypeEvent, "ClientTerrainFilesChanged", NULL, | |
6, NetClassTypeEvent, "ClientTerrainTransactionData", NULL, | |
4, NetClassTypeEvent, "ClientTerrainBeginTransaction", NULL, | |
8, NetClassTypeDataBlock, "ClothingData", NULL, | |
2, NetClassTypeEvent, "ClientObjectTransformChanged", NULL, | |
32, NetClassTypeObject, "ClientEditWaterBlock", NULL, | |
28, NetClassTypeObject, "ClientEditTSStatic", NULL, | |
26, NetClassTypeObject, "ClientEditSun", NULL, | |
24, NetClassTypeObject, "ClientEditSky", NULL, | |
23, NetClassTypeObject, "ClientEditShapeReplicator", NULL, | |
16, NetClassTypeObject, "ClientEditInteriorInstance", NULL, | |
10, NetClassTypeObject, "ClientEditFoliageReplicator", NULL, | |
91, NetClassTypeObject, "fxRenderObject", NULL, | |
99, NetClassTypeObject, "volumeLight", NULL, | |
97, NetClassTypeObject, "sgMissionLightingFilter", NULL, | |
48, NetClassTypeDataBlock, "sgMissionLightingFilterData", NULL, | |
98, NetClassTypeObject, "sgUniversalStaticLight", NULL, | |
49, NetClassTypeDataBlock, "sgUniversalStaticLightData", NULL, | |
96, NetClassTypeObject, "sgLightObject", NULL, | |
47, NetClassTypeDataBlock, "sgLightObjectData", NULL, | |
95, NetClassTypeObject, "sgDecalProjector", NULL, | |
53, NetClassTypeObject, "ParticleEmitter", NULL, | |
22, NetClassTypeDataBlock, "ParticleData", NULL, | |
23, NetClassTypeDataBlock, "ParticleEmitterData", NULL, | |
70, NetClassTypeObject, "RigidShape", NULL, | |
31, NetClassTypeDataBlock, "RigidShapeData", NULL, | |
39, NetClassTypeDataBlock, "TSShapeConstructor", NULL, | |
85, NetClassTypeObject, "WaterBlock", NULL, | |
82, NetClassTypeObject, "TerrainBlock", NULL, | |
80, NetClassTypeObject, "Sun", NULL, | |
75, NetClassTypeObject, "Sky", NULL, | |
49, NetClassTypeObject, "Marker", NULL, | |
17, NetClassTypeEvent, "PathManagerEvent", NULL, | |
14, NetClassTypeEvent, "GhostAlwaysObjectEvent", NULL, | |
11, NetClassTypeEvent, "FileNoneExistEvent", NULL, | |
19, NetClassTypeEvent, "RequestResendFileChunkEvent", NULL, | |
0, NetClassTypeEvent, "AckFileChunkEvent", NULL, | |
9, NetClassTypeEvent, "FileChunkEvent", NULL, | |
10, NetClassTypeEvent, "FileDownloadRequestEvent", NULL, | |
12, NetClassTypeEvent, "FileVerifyCheckSum", NULL, | |
8, NetClassTypeEvent, "ConnectionMessageEvent", &doReadConnectionMessageEvent, | |
10, NetClassTypeDataBlock, "DecalData", NULL, | |
16, NetClassTypeEvent, "NetStringEvent", NULL, | |
26, NetClassTypeDataBlock,"PathedInteriorData", NULL, | |
56, NetClassTypeDataBlock,"PathedInterior", NULL, | |
46, NetClassTypeObject, "InteriorMap", NULL, | |
45, NetClassTypeObject, "InteriorInstance", NULL, | |
88, NetClassTypeObject, "WheeledVehicle", NULL, | |
43, NetClassTypeDataBlock, "WheeledVehicleData", NULL, | |
44, NetClassTypeDataBlock, "WheeledVehicleSpring", NULL, | |
45, NetClassTypeDataBlock, "WheeledVehicleTire", NULL, | |
84, NetClassTypeObject, "VehicleBlocker", NULL, | |
43, NetClassTypeObject, "HoverVehicle", NULL, | |
16, NetClassTypeDataBlock,"HoverVehicleData", NULL, | |
35, NetClassTypeObject, "FlyingVehicle", NULL, | |
13, NetClassTypeDataBlock,"FlyingVehicleData", NULL, | |
81, NetClassTypeObject, "TSStatic", NULL, | |
83, NetClassTypeObject, "Trigger", NULL, | |
41, NetClassTypeDataBlock,"TriggerData", NULL, | |
78, NetClassTypeObject, "StaticShape", NULL, | |
38, NetClassTypeDataBlock, "StaticShapeData", NULL, | |
34, NetClassTypeDataBlock, "ShapeBaseImageData", NULL, | |
72, NetClassTypeObject, "ShapeBase", NULL, | |
33, NetClassTypeDataBlock, "ShapeBaseData", NULL, | |
71, NetClassTypeObject, "ScopeAlwaysShape", NULL, | |
32, NetClassTypeDataBlock, "ScopeAlwaysShapeData", NULL, | |
62, NetClassTypeObject, "Projectile", NULL, | |
30, NetClassTypeDataBlock, "ProjectileData", NULL, | |
58, NetClassTypeObject, "Player", NULL, | |
27, NetClassTypeDataBlock, "PlayerData", NULL, | |
57, NetClassTypeObject, "PhysicalZone", NULL, | |
55, NetClassTypeObject, "PathCamera", NULL, | |
25, NetClassTypeDataBlock, "PathCameraData", NULL, | |
74, NetClassTypeObject, "SimpleNetObject", NULL, | |
25, NetClassTypeEvent, "SimpleMessageEvent", NULL, | |
18, NetClassTypeEvent, "RemoteCommandEvent", NULL, | |
76, NetClassTypeObject, "SpawnSphere", NULL, | |
86, NetClassTypeObject, "WayPoint", NULL, | |
51, NetClassTypeObject, "MissionMarker", NULL, | |
20, NetClassTypeDataBlock, "MissionMarkerData", NULL, | |
50, NetClassTypeObject, "MissionArea", NULL, | |
47, NetClassTypeObject, "Item", NULL, | |
18, NetClassTypeDataBlock, "ItemData", NULL, | |
1, NetClassTypeEvent, "ClientFurnitureTransformChanged", NULL, | |
20, NetClassTypeEvent, "SetMissionCRCEvent", NULL, | |
22, NetClassTypeEvent, "Sim3DAudioEvent", NULL, | |
21, NetClassTypeEvent, "Sim2DAudioEvent", NULL, | |
23, NetClassTypeEvent, "SimDataBlockAckEvent", NULL, | |
24, NetClassTypeEvent, "SimDataBlockEvent", NULL, | |
37, NetClassTypeObject, "GameBase", NULL, | |
14, NetClassTypeDataBlock, "GameBaseData", NULL, | |
87, NetClassTypeObject, "WeatherLightning", NULL, | |
42, NetClassTypeDataBlock, "WeatherLightningData", NULL, | |
30, NetClassTypeEvent, "WeatherLightningStrikeEvent", NULL, | |
77, NetClassTypeObject, "Splash", NULL, | |
37, NetClassTypeDataBlock, "SplashData", NULL, | |
29, NetClassTypeDataBlock, "PrecipitationData", NULL, | |
61, NetClassTypeObject, "Precipitation", NULL, | |
54, NetClassTypeObject, "ParticleEmitterNode", NULL, | |
24, NetClassTypeDataBlock, "ParticleEmitterNodeData", NULL, | |
15, NetClassTypeEvent, "LightningStrikeEvent", NULL, | |
48, NetClassTypeObject, "Lightning", NULL, | |
19, NetClassTypeDataBlock, "LightningData", NULL, | |
94, NetClassTypeObject, "fxSunLight", NULL, | |
92, NetClassTypeObject, "fxShapeReplicatedStatic", NULL, | |
93, NetClassTypeObject, "fxShapeReplicator", NULL, | |
90, NetClassTypeObject, "fxLight", NULL, | |
46, NetClassTypeDataBlock, "fxLightData", NULL, | |
89, NetClassTypeObject, "fxFoliageReplicator", NULL, | |
11, NetClassTypeDataBlock, "ExplosionData", NULL, | |
12, NetClassTypeDataBlock, "FireballAtmosphereData", NULL, | |
34, NetClassTypeObject, "FireballAtmosphere", NULL, | |
33, NetClassTypeObject, "Debris", NULL, | |
9, NetClassTypeDataBlock, "DebrisData", NULL, | |
4, NetClassTypeObject, "Camera", NULL, | |
6, NetClassTypeDataBlock, "CameraData", NULL, | |
3, NetClassTypeObject, "AudioEmitter", NULL, | |
2, NetClassTypeObject, "AIWheeledVehicle", NULL, | |
1, NetClassTypeObject, "AIPlayer", NULL, | |
1, NetClassTypeDataBlock, "AIPlayerData", NULL, | |
36, NetClassTypeDataBlock, "SimDataBlock", NULL, | |
4, NetClassTypeDataBlock, "AudioProfile", NULL, | |
2, NetClassTypeDataBlock, "AudioDescription", NULL, | |
5, NetClassTypeDataBlock, "AudioSampleEnvironment", NULL, | |
3, NetClassTypeDataBlock, "AudioEnvironment", NULL | |
}; | |
PacketReadClass* findClassInfo(U32 idx, U32 typeID) | |
{ | |
for (int i=0; i<sizeof(sReadClasses) / sizeof(sReadClasses[0]); i++) | |
{ | |
if (sReadClasses[i].index == idx && sReadClasses[i].typeID == typeID) | |
return sReadClasses + i; | |
} | |
return NULL; | |
} | |
bool eventReadPacket(BitStream &s) | |
{ | |
S32 prevSeq = -2; | |
bool unguaranteedPhase = true; | |
while (1) | |
{ | |
bool bit = s.readFlag(); | |
if(unguaranteedPhase && !bit) | |
{ | |
unguaranteedPhase = false; | |
printf("NOW READING GUARANTEED EVENTS\n"); | |
bit = s.readFlag(); | |
} | |
if(!unguaranteedPhase && !bit) | |
{ | |
printf("END READ EVENT\n"); | |
break; | |
} | |
printf("Reading event...\n"); | |
S32 seq = -1; | |
if(!unguaranteedPhase) // get the sequence | |
{ | |
if(s.readFlag()) | |
seq = (prevSeq + 1) & 0x7f; | |
else | |
seq = s.readInt(7); | |
prevSeq = seq; | |
} | |
S32 classId = s.readClassId(NetClassTypeEvent, NetClassGroupGame); | |
if(classId == -1) | |
{ | |
printf("Invalid event packet.\n"); | |
return false; | |
} | |
PacketReadClass *info = findClassInfo(classId, NetClassTypeEvent); | |
if(!info) | |
{ | |
printf("Invalid packet classID=%u.\n", classId); | |
return false; | |
} | |
if (!info->readFunc) | |
{ | |
printf("TODO: %s\n", info->name); | |
return false; | |
} | |
if (!info->readFunc(s)) | |
{ | |
printf("Packet read error class=%u\n", classId); | |
return false; | |
} | |
} | |
return true; | |
} | |
bool ghostReadPacket(BitStream &s) | |
{ | |
//char stringBuf[256*10]; | |
//stringBuf[0] = 0; | |
//s.setStringBuffer(stringBuf); | |
bool hasEvent = s.readFlag(); | |
if (hasEvent) | |
{ | |
S32 idSize = s.readInt(GhostIndexBitSize); | |
printf("Has ghosts, idSize=%i\n", idSize); | |
while (s.readFlag()) | |
{ | |
S32 idx = s.readInt(idSize); | |
printf("Ghost %i update\n", idx); | |
S32 bitPos = s.getCurPos(); | |
U32 count = 0; | |
while (s.getReadByteSize() != 0) | |
{ | |
s.error = false; | |
s.setCurPos(bitPos + count); | |
//s.setStringBuffer(stringBuf); | |
char str[256*10]; | |
memset(str, '\0', sizeof(str)); | |
//memset(stringBuf, '\0', sizeof(stringBuf)); | |
str[0] = '\0'; | |
s.readString(str); | |
//if (str[0] == '~' && str[1] == '/') | |
if (strlen(str) != 0) | |
{ | |
printf("STRING %s\n", str); | |
} | |
count++; | |
} | |
if (s.readFlag()) | |
{ | |
printf("^^ BEING DELETED\n"); | |
} | |
else | |
{ | |
printf("TODO: see if this is a new object?\n"); | |
} | |
return false; | |
} | |
} | |
return true; | |
} | |
int main(int argc, char **argv) | |
{ | |
std::filebuf fb; | |
char buffer[1024*1024]; | |
uint32_t sz = 0; | |
U32 lastSequenceNumber = 0xFFFFFF; | |
memset(__NetClassBitSize, '\0', sizeof(__NetClassBitSize)); | |
__NetClassBitSize[NetClassGroupGame][NetClassTypeObject] = 7; | |
__NetClassBitSize[NetClassGroupGame][NetClassTypeDataBlock] = 6; | |
__NetClassBitSize[NetClassGroupGame][NetClassTypeEvent] = 5; | |
for (int i=1; i<argc; i++) | |
{ | |
printf("%s ", argv[i]); | |
} | |
printf("\n"); | |
for (int i=1; i<argc; i++) | |
{ | |
printf("Open %s\n", argv[i]); | |
if (fb.open (argv[i], std::ios::in)) | |
{ | |
std::istream is(&fb); | |
is.seekg (0, is.end); | |
sz = is.tellg(); | |
is.seekg (0, is.beg); | |
is.read(&buffer[0], sz); | |
fb.close(); | |
} | |
printf("Now for bitstream (%u)...\n", sz); | |
BitStream s(&buffer[0], sz); | |
printf("BITSIZE = %u\n", s.getReadByteSize()); | |
{ | |
if (!s.readFlag()) // get rid of the game info packet bit | |
{ | |
printf("NON-PROTOCOL PACKET\n"); | |
continue; | |
} | |
U32 pkConnectSeqBit = s.readInt(1); | |
U32 pkSequenceNumber = s.readInt(9); | |
U32 pkHighestAck = s.readInt(9); | |
U32 pkPacketType = s.readInt(2); | |
S32 pkAckByteCount = s.readInt(3); | |
const char* packetName = pkPacketType < InvalidPacketType ? packetNames[pkPacketType] : "INVALID"; | |
printf("SEQB %u SEQN %u HACK %u PT %s BC %u\n", pkConnectSeqBit, pkSequenceNumber, pkHighestAck, packetName, pkAckByteCount); | |
U32 ackMask = s.readInt(pkAckByteCount*8); | |
if (pkPacketType == PingPacket) | |
{ | |
continue; | |
} | |
else if (pkPacketType == DataPacket) | |
{ | |
if (lastSequenceNumber == 0xFFFFFF) | |
{ | |
lastSequenceNumber = pkSequenceNumber; | |
} | |
else if (lastSequenceNumber != pkSequenceNumber) | |
{ | |
printf("Sequence changed...\n"); | |
if (s.readFlag()) | |
{ | |
printf("Rate changed\n"); | |
U32 curDelay = s.readInt(10); | |
U32 packetSize = s.readInt(10); | |
} | |
if (s.readFlag()) | |
{ | |
printf("max changed\n"); | |
U32 omaxDelay = s.readInt(10); | |
S32 omaxSize = s.readInt(10); | |
} | |
// GameConnection::readPacket comes first | |
char stringBuf[256]; | |
stringBuf[0] = 0; | |
s.setStringBuffer(stringBuf); | |
s.clearCompressionPoint(); | |
U32 lastMoveAck = s.readInt(32); | |
printf("lastMoveAck %u\n", lastMoveAck); | |
if (s.readFlag()) | |
{ | |
if (s.readFlag()) | |
{ | |
F32 flash = s.readFloat(7); | |
} | |
if (s.readFlag()) | |
{ | |
F32 whiteOut = s.readFloat(7) * 1.5; | |
} | |
printf("Flash/whiteout change\n"); | |
} | |
else | |
{ | |
printf("no flash or whiteout\n"); | |
} | |
if (s.readFlag()) | |
{ | |
if (s.readFlag()) // control object dirty | |
{ | |
S32 gIdx = s.readInt(GhostIdBitSize); | |
printf("CONTROL OBJECT CHANGED TO %i, NEED TO READ PACKET\n", gIdx); | |
// TODO: read packet | |
continue; | |
} | |
else | |
{ | |
Point3F pos; | |
s.read(&pos.x); | |
s.read(&pos.y); | |
s.read(&pos.z); | |
s.setCompressionPoint(pos); | |
printf("Compression point %f,%f,%f\n", pos.x, pos.y, pos.z); | |
} | |
} | |
else | |
{ | |
printf("no control changes\n"); | |
} | |
if (s.readFlag()) | |
{ | |
S32 gIdx = s.readInt(GhostIdBitSize); | |
printf("CAMERA OBJECT CHANGED TO %i, NEED TO READ PACKET\n", gIdx); | |
// TODO: read packet | |
continue; | |
} | |
else | |
{ | |
printf("no camera changes\n"); | |
} | |
if (s.readFlag()) | |
{ | |
bool flag = s.readFlag(); | |
printf("Toggled first person to %s", flag ? "true" : "false"); | |
} | |
else | |
{ | |
printf("no FP changes\n"); | |
} | |
if (s.readFlag()) | |
{ | |
S32 fov = s.readInt(8); | |
printf("FOV change to %i", fov); | |
} | |
else | |
{ | |
printf("no FOV changes\n"); | |
} | |
printf("--\n"); | |
if (!eventReadPacket(s)) | |
{ | |
s.clearCompressionPoint(); | |
continue; | |
} | |
if (!ghostReadPacket(s)) | |
{ | |
s.clearCompressionPoint(); | |
continue; | |
} | |
} | |
} | |
else | |
{ | |
printf("WTF\n"); | |
break; | |
} | |
} | |
} | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment