aboutsummaryrefslogtreecommitdiffstats
path: root/LibOVR/Src/Net
diff options
context:
space:
mode:
Diffstat (limited to 'LibOVR/Src/Net')
-rw-r--r--LibOVR/Src/Net/OVR_BitStream.cpp1151
-rw-r--r--LibOVR/Src/Net/OVR_BitStream.h1744
-rw-r--r--LibOVR/Src/Net/OVR_MessageIDTypes.h39
-rw-r--r--LibOVR/Src/Net/OVR_NetworkPlugin.cpp70
-rw-r--r--LibOVR/Src/Net/OVR_NetworkPlugin.h60
-rw-r--r--LibOVR/Src/Net/OVR_NetworkTypes.h42
-rw-r--r--LibOVR/Src/Net/OVR_PacketizedTCPSocket.cpp215
-rw-r--r--LibOVR/Src/Net/OVR_PacketizedTCPSocket.h87
-rw-r--r--LibOVR/Src/Net/OVR_RPC1.cpp270
-rw-r--r--LibOVR/Src/Net/OVR_RPC1.h105
-rw-r--r--LibOVR/Src/Net/OVR_Session.cpp736
-rw-r--r--LibOVR/Src/Net/OVR_Session.h513
-rw-r--r--LibOVR/Src/Net/OVR_Socket.cpp94
-rw-r--r--LibOVR/Src/Net/OVR_Socket.h242
-rw-r--r--LibOVR/Src/Net/OVR_Unix_Socket.cpp600
-rw-r--r--LibOVR/Src/Net/OVR_Unix_Socket.h152
-rw-r--r--LibOVR/Src/Net/OVR_Win32_Socket.cpp608
-rw-r--r--LibOVR/Src/Net/OVR_Win32_Socket.h151
18 files changed, 6879 insertions, 0 deletions
diff --git a/LibOVR/Src/Net/OVR_BitStream.cpp b/LibOVR/Src/Net/OVR_BitStream.cpp
new file mode 100644
index 0000000..b565f22
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_BitStream.cpp
@@ -0,0 +1,1151 @@
+/************************************************************************************
+
+Filename : OVR_BitStream.cpp
+Content : A generic serialization toolkit for packing data to a binary stream.
+Created : June 10, 2014
+Authors : Kevin Jenkins
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "OVR_BitStream.h"
+
+#ifdef OVR_OS_WIN32
+#include <WinSock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+namespace OVR { namespace Net {
+
+
+//-----------------------------------------------------------------------------
+// BitStream
+
+BitStream::BitStream()
+{
+ numberOfBitsUsed = 0;
+ //numberOfBitsAllocated = 32 * 8;
+ numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8;
+ readOffset = 0;
+ //data = ( unsigned char* ) OVR_ALLOC( 32);
+ data = ( unsigned char* ) stackData;
+
+#ifdef _DEBUG
+ // OVR_ASSERT( data );
+#endif
+ //memset(data, 0, 32);
+ copyData = true;
+}
+
+BitStream::BitStream( const unsigned int initialBytesToAllocate )
+{
+ numberOfBitsUsed = 0;
+ readOffset = 0;
+ if (initialBytesToAllocate <= BITSTREAM_STACK_ALLOCATION_SIZE)
+ {
+ data = ( unsigned char* ) stackData;
+ numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8;
+ }
+ else
+ {
+ data = ( unsigned char* ) OVR_ALLOC( (size_t) initialBytesToAllocate);
+ numberOfBitsAllocated = initialBytesToAllocate << 3;
+ }
+#ifdef _DEBUG
+ OVR_ASSERT( data );
+#endif
+ // memset(data, 0, initialBytesToAllocate);
+ copyData = true;
+}
+
+BitStream::BitStream( char* _data, const unsigned int lengthInBytes, bool _copyData )
+{
+ numberOfBitsUsed = lengthInBytes << 3;
+ readOffset = 0;
+ copyData = _copyData;
+ numberOfBitsAllocated = lengthInBytes << 3;
+
+ if ( copyData )
+ {
+ if ( lengthInBytes > 0 )
+ {
+ if (lengthInBytes < BITSTREAM_STACK_ALLOCATION_SIZE)
+ {
+ data = ( unsigned char* ) stackData;
+ numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE << 3;
+ }
+ else
+ {
+ data = ( unsigned char* ) OVR_ALLOC( (size_t) lengthInBytes);
+ }
+#ifdef _DEBUG
+ OVR_ASSERT( data );
+#endif
+ memcpy( data, _data, (size_t) lengthInBytes );
+ }
+ else
+ data = 0;
+ }
+ else
+ data = ( unsigned char* ) _data;
+}
+
+// Use this if you pass a pointer copy to the constructor (_copyData==false) and want to overallocate to prevent reallocation
+void BitStream::SetNumberOfBitsAllocated( const BitSize_t lengthInBits )
+{
+#ifdef _DEBUG
+ OVR_ASSERT( lengthInBits >= ( BitSize_t ) numberOfBitsAllocated );
+#endif
+ numberOfBitsAllocated = lengthInBits;
+}
+
+BitStream::~BitStream()
+{
+ if ( copyData && numberOfBitsAllocated > (BITSTREAM_STACK_ALLOCATION_SIZE << 3))
+ OVR_FREE( data ); // Use realloc and free so we are more efficient than delete and new for resizing
+}
+
+void BitStream::Reset( void )
+{
+ // Note: Do NOT reallocate memory because BitStream is used
+ // in places to serialize/deserialize a buffer. Reallocation
+ // is a dangerous operation (may result in leaks).
+
+ if ( numberOfBitsUsed > 0 )
+ {
+ // memset(data, 0, BITS_TO_BYTES(numberOfBitsUsed));
+ }
+
+ // Don't free memory here for speed efficiency
+ //free(data); // Use realloc and free so we are more efficient than delete and new for resizing
+ numberOfBitsUsed = 0;
+
+ //numberOfBitsAllocated=8;
+ readOffset = 0;
+
+ //data=(unsigned char*)OVR_ALLOC(1, _FILE_AND_LINE_);
+ // if (numberOfBitsAllocated>0)
+ // memset(data, 0, BITS_TO_BYTES(numberOfBitsAllocated));
+}
+
+// Write an array or casted stream
+void BitStream::Write( const char* inputByteArray, const unsigned int numberOfBytes )
+{
+ if (numberOfBytes==0)
+ return;
+
+ // Optimization:
+ if ((numberOfBitsUsed & 7) == 0)
+ {
+ AddBitsAndReallocate( BYTES_TO_BITS(numberOfBytes) );
+ memcpy(data+BITS_TO_BYTES(numberOfBitsUsed), inputByteArray, (size_t) numberOfBytes);
+ numberOfBitsUsed+=BYTES_TO_BITS(numberOfBytes);
+ }
+ else
+ {
+ WriteBits( ( unsigned char* ) inputByteArray, numberOfBytes * 8, true );
+ }
+
+}
+void BitStream::Write( BitStream *bitStream)
+{
+ Write(bitStream, bitStream->GetNumberOfBitsUsed()-bitStream->GetReadOffset());
+}
+void BitStream::Write( BitStream *bitStream, BitSize_t numberOfBits )
+{
+ AddBitsAndReallocate( numberOfBits );
+ BitSize_t numberOfBitsMod8;
+
+ if ((bitStream->GetReadOffset()&7)==0 && (numberOfBitsUsed&7)==0)
+ {
+ int readOffsetBytes=bitStream->GetReadOffset()/8;
+ int numBytes=numberOfBits/8;
+ memcpy(data + (numberOfBitsUsed >> 3), bitStream->GetData()+readOffsetBytes, numBytes);
+ numberOfBits-=BYTES_TO_BITS(numBytes);
+ bitStream->SetReadOffset(BYTES_TO_BITS(numBytes+readOffsetBytes));
+ numberOfBitsUsed+=BYTES_TO_BITS(numBytes);
+ }
+
+ while (numberOfBits-->0 && bitStream->readOffset + 1 <= bitStream->numberOfBitsUsed)
+ {
+ numberOfBitsMod8 = numberOfBitsUsed & 7;
+ if ( numberOfBitsMod8 == 0 )
+ {
+ // New byte
+ if (bitStream->data[ bitStream->readOffset >> 3 ] & ( 0x80 >> ( bitStream->readOffset & 7 ) ) )
+ {
+ // Write 1
+ data[ numberOfBitsUsed >> 3 ] = 0x80;
+ }
+ else
+ {
+ // Write 0
+ data[ numberOfBitsUsed >> 3 ] = 0;
+ }
+
+ }
+ else
+ {
+ // Existing byte
+ if (bitStream->data[ bitStream->readOffset >> 3 ] & ( 0x80 >> ( bitStream->readOffset & 7 ) ) )
+ data[ numberOfBitsUsed >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1
+ // else 0, do nothing
+ }
+
+ bitStream->readOffset++;
+ numberOfBitsUsed++;
+ }
+}
+void BitStream::Write( BitStream &bitStream, BitSize_t numberOfBits )
+{
+ Write(&bitStream, numberOfBits);
+}
+void BitStream::Write( BitStream &bitStream )
+{
+ Write(&bitStream);
+}
+bool BitStream::Read( BitStream *bitStream, BitSize_t numberOfBits )
+{
+ if (GetNumberOfUnreadBits() < numberOfBits)
+ return false;
+ bitStream->Write(this, numberOfBits);
+ return true;
+}
+bool BitStream::Read( BitStream *bitStream )
+{
+ bitStream->Write(this);
+ return true;
+}
+bool BitStream::Read( BitStream &bitStream, BitSize_t numberOfBits )
+{
+ if (GetNumberOfUnreadBits() < numberOfBits)
+ return false;
+ bitStream.Write(this, numberOfBits);
+ return true;
+}
+bool BitStream::Read( BitStream &bitStream )
+{
+ bitStream.Write(this);
+ return true;
+}
+
+// Read an array or casted stream
+bool BitStream::Read( char* outByteArray, const unsigned int numberOfBytes )
+{
+ // Optimization:
+ if ((readOffset & 7) == 0)
+ {
+ if ( readOffset + ( numberOfBytes << 3 ) > numberOfBitsUsed )
+ return false;
+
+ // Write the data
+ memcpy( outByteArray, data + ( readOffset >> 3 ), (size_t) numberOfBytes );
+
+ readOffset += numberOfBytes << 3;
+ return true;
+ }
+ else
+ {
+ return ReadBits( ( unsigned char* ) outByteArray, numberOfBytes * 8 );
+ }
+}
+
+// Sets the read pointer back to the beginning of your data.
+void BitStream::ResetReadPointer( void )
+{
+ readOffset = 0;
+}
+
+// Sets the write pointer back to the beginning of your data.
+void BitStream::ResetWritePointer( void )
+{
+ numberOfBitsUsed = 0;
+}
+
+// Write a 0
+void BitStream::Write0( void )
+{
+ AddBitsAndReallocate( 1 );
+
+ // New bytes need to be zeroed
+ if ( ( numberOfBitsUsed & 7 ) == 0 )
+ data[ numberOfBitsUsed >> 3 ] = 0;
+
+ numberOfBitsUsed++;
+}
+
+// Write a 1
+void BitStream::Write1( void )
+{
+ AddBitsAndReallocate( 1 );
+
+ BitSize_t numberOfBitsMod8 = numberOfBitsUsed & 7;
+
+ if ( numberOfBitsMod8 == 0 )
+ data[ numberOfBitsUsed >> 3 ] = 0x80;
+ else
+ data[ numberOfBitsUsed >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1
+
+ numberOfBitsUsed++;
+}
+
+// Returns true if the next data read is a 1, false if it is a 0
+bool BitStream::ReadBit( void )
+{
+ bool result = ( data[ readOffset >> 3 ] & ( 0x80 >> ( readOffset & 7 ) ) ) !=0;
+ readOffset++;
+ return result;
+}
+
+// Align the bitstream to the byte boundary and then write the specified number of bits.
+// This is faster than WriteBits but wastes the bits to do the alignment and requires you to call
+// SetReadToByteAlignment at the corresponding read position
+void BitStream::WriteAlignedBytes( const unsigned char* inByteArray, const unsigned int numberOfBytesToWrite )
+{
+ AlignWriteToByteBoundary();
+ Write((const char*) inByteArray, numberOfBytesToWrite);
+}
+void BitStream::EndianSwapBytes( int byteOffset, int length )
+{
+ if (DoEndianSwap())
+ {
+ ReverseBytesInPlace(data+byteOffset, length);
+ }
+}
+/// Aligns the bitstream, writes inputLength, and writes input. Won't write beyond maxBytesToWrite
+void BitStream::WriteAlignedBytesSafe( const char *inByteArray, const unsigned int inputLength, const unsigned int maxBytesToWrite )
+{
+ if (inByteArray==0 || inputLength==0)
+ {
+ WriteCompressed((unsigned int)0);
+ return;
+ }
+ WriteCompressed(inputLength);
+ WriteAlignedBytes((const unsigned char*) inByteArray, inputLength < maxBytesToWrite ? inputLength : maxBytesToWrite);
+}
+
+// Read bits, starting at the next aligned bits. Note that the modulus 8 starting offset of the
+// sequence must be the same as was used with WriteBits. This will be a problem with packet coalescence
+// unless you byte align the coalesced packets.
+bool BitStream::ReadAlignedBytes( unsigned char* inOutByteArray, const unsigned int numberOfBytesToRead )
+{
+#ifdef _DEBUG
+ OVR_ASSERT( numberOfBytesToRead > 0 );
+#endif
+
+ if ( numberOfBytesToRead <= 0 )
+ return false;
+
+ // Byte align
+ AlignReadToByteBoundary();
+
+ if ( readOffset + ( numberOfBytesToRead << 3 ) > numberOfBitsUsed )
+ return false;
+
+ // Write the data
+ memcpy( inOutByteArray, data + ( readOffset >> 3 ), (size_t) numberOfBytesToRead );
+
+ readOffset += numberOfBytesToRead << 3;
+
+ return true;
+}
+bool BitStream::ReadAlignedBytesSafe( char *inOutByteArray, int &inputLength, const int maxBytesToRead )
+{
+ return ReadAlignedBytesSafe(inOutByteArray,(unsigned int&) inputLength,(unsigned int)maxBytesToRead);
+}
+bool BitStream::ReadAlignedBytesSafe( char *inOutByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead )
+{
+ if (ReadCompressed(inputLength)==false)
+ return false;
+ if (inputLength > maxBytesToRead)
+ inputLength=maxBytesToRead;
+ if (inputLength==0)
+ return true;
+ return ReadAlignedBytes((unsigned char*) inOutByteArray, inputLength);
+}
+bool BitStream::ReadAlignedBytesSafeAlloc( char **outByteArray, int &inputLength, const unsigned int maxBytesToRead )
+{
+ return ReadAlignedBytesSafeAlloc(outByteArray,(unsigned int&) inputLength, maxBytesToRead);
+}
+bool BitStream::ReadAlignedBytesSafeAlloc( char ** outByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead )
+{
+ OVR_FREE(*outByteArray);
+ *outByteArray=0;
+ if (ReadCompressed(inputLength)==false)
+ return false;
+ if (inputLength > maxBytesToRead)
+ inputLength=maxBytesToRead;
+ if (inputLength==0)
+ return true;
+ *outByteArray = (char*) OVR_ALLOC( (size_t) inputLength);
+ return ReadAlignedBytes((unsigned char*) *outByteArray, inputLength);
+}
+
+// Write numberToWrite bits from the input source
+void BitStream::WriteBits( const unsigned char* inByteArray, BitSize_t numberOfBitsToWrite, const bool rightAlignedBits )
+{
+// if (numberOfBitsToWrite<=0)
+// return;
+
+ AddBitsAndReallocate( numberOfBitsToWrite );
+
+ const BitSize_t numberOfBitsUsedMod8 = numberOfBitsUsed & 7;
+
+ // If currently aligned and numberOfBits is a multiple of 8, just memcpy for speed
+ if (numberOfBitsUsedMod8==0 && (numberOfBitsToWrite&7)==0)
+ {
+ memcpy( data + ( numberOfBitsUsed >> 3 ), inByteArray, numberOfBitsToWrite>>3);
+ numberOfBitsUsed+=numberOfBitsToWrite;
+ return;
+ }
+
+ unsigned char dataByte;
+ const unsigned char* inputPtr=inByteArray;
+
+ // Faster to put the while at the top surprisingly enough
+ while ( numberOfBitsToWrite > 0 )
+ //do
+ {
+ dataByte = *( inputPtr++ );
+
+ if ( numberOfBitsToWrite < 8 && rightAlignedBits ) // rightAlignedBits means in the case of a partial byte, the bits are aligned from the right (bit 0) rather than the left (as in the normal internal representation)
+ dataByte <<= 8 - numberOfBitsToWrite; // shift left to get the bits on the left, as in our internal representation
+
+ // Writing to a new byte each time
+ if ( numberOfBitsUsedMod8 == 0 )
+ * ( data + ( numberOfBitsUsed >> 3 ) ) = dataByte;
+ else
+ {
+ // Copy over the new data.
+ *( data + ( numberOfBitsUsed >> 3 ) ) |= dataByte >> ( numberOfBitsUsedMod8 ); // First half
+
+ if ( 8 - ( numberOfBitsUsedMod8 ) < 8 && 8 - ( numberOfBitsUsedMod8 ) < numberOfBitsToWrite ) // If we didn't write it all out in the first half (8 - (numberOfBitsUsed%8) is the number we wrote in the first half)
+ {
+ *( data + ( numberOfBitsUsed >> 3 ) + 1 ) = (unsigned char) ( dataByte << ( 8 - ( numberOfBitsUsedMod8 ) ) ); // Second half (overlaps byte boundary)
+ }
+ }
+
+ if ( numberOfBitsToWrite >= 8 )
+ {
+ numberOfBitsUsed += 8;
+ numberOfBitsToWrite -= 8;
+ }
+ else
+ {
+ numberOfBitsUsed += numberOfBitsToWrite;
+ numberOfBitsToWrite=0;
+ }
+ }
+ // } while(numberOfBitsToWrite>0);
+}
+
+// Set the stream to some initial data. For internal use
+void BitStream::SetData( unsigned char *inByteArray )
+{
+ data=inByteArray;
+ copyData=false;
+}
+
+// Assume the input source points to a native type, compress and write it
+void BitStream::WriteCompressed( const unsigned char* inByteArray,
+ const unsigned int size, const bool unsignedData )
+{
+ BitSize_t currentByte = ( size >> 3 ) - 1; // PCs
+
+ unsigned char byteMatch;
+
+ if ( unsignedData )
+ {
+ byteMatch = 0;
+ }
+
+ else
+ {
+ byteMatch = 0xFF;
+ }
+
+ // Write upper bytes with a single 1
+ // From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes
+ while ( currentByte > 0 )
+ {
+ if ( inByteArray[ currentByte ] == byteMatch ) // If high byte is byteMatch (0 of 0xff) then it would have the same value shifted
+ {
+ bool b = true;
+ Write( b );
+ }
+ else
+ {
+ // Write the remainder of the data after writing 0
+ bool b = false;
+ Write( b );
+
+ WriteBits( inByteArray, ( currentByte + 1 ) << 3, true );
+ // currentByte--;
+
+
+ return ;
+ }
+
+ currentByte--;
+ }
+
+ // If the upper half of the last byte is a 0 (positive) or 16 (negative) then write a 1 and the remaining 4 bits. Otherwise write a 0 and the 8 bites.
+ if ( ( unsignedData && ( ( *( inByteArray + currentByte ) ) & 0xF0 ) == 0x00 ) ||
+ ( unsignedData == false && ( ( *( inByteArray + currentByte ) ) & 0xF0 ) == 0xF0 ) )
+ {
+ bool b = true;
+ Write( b );
+ WriteBits( inByteArray + currentByte, 4, true );
+ }
+
+ else
+ {
+ bool b = false;
+ Write( b );
+ WriteBits( inByteArray + currentByte, 8, true );
+ }
+}
+
+// Read numberOfBitsToRead bits to the output source
+// alignBitsToRight should be set to true to convert internal bitstream data to userdata
+// It should be false if you used WriteBits with rightAlignedBits false
+bool BitStream::ReadBits( unsigned char *inOutByteArray, BitSize_t numberOfBitsToRead, const bool alignBitsToRight )
+{
+#ifdef _DEBUG
+ // OVR_ASSERT( numberOfBitsToRead > 0 );
+#endif
+ if (numberOfBitsToRead<=0)
+ return false;
+
+ if ( readOffset + numberOfBitsToRead > numberOfBitsUsed )
+ return false;
+
+
+ const BitSize_t readOffsetMod8 = readOffset & 7;
+
+ // If currently aligned and numberOfBits is a multiple of 8, just memcpy for speed
+ if (readOffsetMod8==0 && (numberOfBitsToRead&7)==0)
+ {
+ memcpy( inOutByteArray, data + ( readOffset >> 3 ), numberOfBitsToRead>>3);
+ readOffset+=numberOfBitsToRead;
+ return true;
+ }
+
+
+
+ BitSize_t offset = 0;
+
+ memset( inOutByteArray, 0, (size_t) BITS_TO_BYTES( numberOfBitsToRead ) );
+
+ while ( numberOfBitsToRead > 0 )
+ {
+ *( inOutByteArray + offset ) |= *( data + ( readOffset >> 3 ) ) << ( readOffsetMod8 ); // First half
+
+ if ( readOffsetMod8 > 0 && numberOfBitsToRead > 8 - ( readOffsetMod8 ) ) // If we have a second half, we didn't read enough bytes in the first half
+ *( inOutByteArray + offset ) |= *( data + ( readOffset >> 3 ) + 1 ) >> ( 8 - ( readOffsetMod8 ) ); // Second half (overlaps byte boundary)
+
+ if (numberOfBitsToRead>=8)
+ {
+ numberOfBitsToRead -= 8;
+ readOffset += 8;
+ offset++;
+ }
+ else
+ {
+ int neg = (int) numberOfBitsToRead - 8;
+
+ if ( neg < 0 ) // Reading a partial byte for the last byte, shift right so the data is aligned on the right
+ {
+
+ if ( alignBitsToRight )
+ * ( inOutByteArray + offset ) >>= -neg;
+
+ readOffset += 8 + neg;
+ }
+ else
+ readOffset += 8;
+
+ offset++;
+
+ numberOfBitsToRead=0;
+ }
+ }
+
+ return true;
+}
+
+// Assume the input source points to a compressed native type. Decompress and read it
+bool BitStream::ReadCompressed( unsigned char* inOutByteArray,
+ const unsigned int size, const bool unsignedData )
+{
+ unsigned int currentByte = ( size >> 3 ) - 1;
+
+
+ unsigned char byteMatch, halfByteMatch;
+
+ if ( unsignedData )
+ {
+ byteMatch = 0;
+ halfByteMatch = 0;
+ }
+
+ else
+ {
+ byteMatch = 0xFF;
+ halfByteMatch = 0xF0;
+ }
+
+ // Upper bytes are specified with a single 1 if they match byteMatch
+ // From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes
+ while ( currentByte > 0 )
+ {
+ // If we read a 1 then the data is byteMatch.
+
+ bool b;
+
+ if ( Read( b ) == false )
+ return false;
+
+ if ( b ) // Check that bit
+ {
+ inOutByteArray[ currentByte ] = byteMatch;
+ currentByte--;
+ }
+ else
+ {
+ // Read the rest of the bytes
+
+ if ( ReadBits( inOutByteArray, ( currentByte + 1 ) << 3 ) == false )
+ return false;
+
+ return true;
+ }
+ }
+
+ // All but the first bytes are byteMatch. If the upper half of the last byte is a 0 (positive) or 16 (negative) then what we read will be a 1 and the remaining 4 bits.
+ // Otherwise we read a 0 and the 8 bytes
+ //OVR_ASSERT(readOffset+1 <=numberOfBitsUsed); // If this assert is hit the stream wasn't long enough to read from
+ if ( readOffset + 1 > numberOfBitsUsed )
+ return false;
+
+ bool b=false;
+
+ if ( Read( b ) == false )
+ return false;
+
+ if ( b ) // Check that bit
+ {
+
+ if ( ReadBits( inOutByteArray + currentByte, 4 ) == false )
+ return false;
+
+ inOutByteArray[ currentByte ] |= halfByteMatch; // We have to set the high 4 bits since these are set to 0 by ReadBits
+ }
+ else
+ {
+ if ( ReadBits( inOutByteArray + currentByte, 8 ) == false )
+ return false;
+ }
+
+ return true;
+}
+
+// Reallocates (if necessary) in preparation of writing numberOfBitsToWrite
+void BitStream::AddBitsAndReallocate( const BitSize_t numberOfBitsToWrite )
+{
+ BitSize_t newNumberOfBitsAllocated = numberOfBitsToWrite + numberOfBitsUsed;
+
+ if ( numberOfBitsToWrite + numberOfBitsUsed > 0 && ( ( numberOfBitsAllocated - 1 ) >> 3 ) < ( ( newNumberOfBitsAllocated - 1 ) >> 3 ) ) // If we need to allocate 1 or more new bytes
+ {
+#ifdef _DEBUG
+ // If this assert hits then we need to specify true for the third parameter in the constructor
+ // It needs to reallocate to hold all the data and can't do it unless we allocated to begin with
+ // Often hits if you call Write or Serialize on a read-only bitstream
+ OVR_ASSERT( copyData == true );
+#endif
+
+ // Less memory efficient but saves on news and deletes
+ /// Cap to 1 meg buffer to save on huge allocations
+ newNumberOfBitsAllocated = ( numberOfBitsToWrite + numberOfBitsUsed ) * 2;
+ if (newNumberOfBitsAllocated - ( numberOfBitsToWrite + numberOfBitsUsed ) > 1048576 )
+ newNumberOfBitsAllocated = numberOfBitsToWrite + numberOfBitsUsed + 1048576;
+
+ // BitSize_t newByteOffset = BITS_TO_BYTES( numberOfBitsAllocated );
+ // Use realloc and free so we are more efficient than delete and new for resizing
+ BitSize_t amountToAllocate = BITS_TO_BYTES( newNumberOfBitsAllocated );
+ if (data==(unsigned char*)stackData)
+ {
+ if (amountToAllocate > BITSTREAM_STACK_ALLOCATION_SIZE)
+ {
+ data = ( unsigned char* ) OVR_ALLOC( (size_t) amountToAllocate);
+ OVR_ASSERT(data);
+ if (data)
+ {
+ // need to copy the stack data over to our new memory area too
+ memcpy ((void *)data, (void *)stackData, (size_t) BITS_TO_BYTES( numberOfBitsAllocated ));
+ }
+ }
+ }
+ else
+ {
+ data = ( unsigned char* ) OVR_REALLOC( data, (size_t) amountToAllocate);
+ }
+
+#ifdef _DEBUG
+ OVR_ASSERT( data ); // Make sure realloc succeeded
+#endif
+ // memset(data+newByteOffset, 0, ((newNumberOfBitsAllocated-1)>>3) - ((numberOfBitsAllocated-1)>>3)); // Set the new data block to 0
+ }
+
+ if ( newNumberOfBitsAllocated > numberOfBitsAllocated )
+ numberOfBitsAllocated = newNumberOfBitsAllocated;
+}
+BitSize_t BitStream::GetNumberOfBitsAllocated(void) const
+{
+ return numberOfBitsAllocated;
+}
+void BitStream::PadWithZeroToByteLength( unsigned int bytes )
+{
+ if (GetNumberOfBytesUsed() < bytes)
+ {
+ AlignWriteToByteBoundary();
+ unsigned int numToWrite = bytes - GetNumberOfBytesUsed();
+ AddBitsAndReallocate( BYTES_TO_BITS(numToWrite) );
+ memset(data+BITS_TO_BYTES(numberOfBitsUsed), 0, (size_t) numToWrite);
+ numberOfBitsUsed+=BYTES_TO_BITS(numToWrite);
+ }
+}
+
+/*
+// Julius Goryavsky's version of Harley's algorithm.
+// 17 elementary ops plus an indexed load, if the machine
+// has "and not."
+
+int nlz10b(unsigned x) {
+
+ static char table[64] =
+ {32,20,19, u, u,18, u, 7, 10,17, u, u,14, u, 6, u,
+ u, 9, u,16, u, u, 1,26, u,13, u, u,24, 5, u, u,
+ u,21, u, 8,11, u,15, u, u, u, u, 2,27, 0,25, u,
+ 22, u,12, u, u, 3,28, u, 23, u, 4,29, u, u,30,31};
+
+ x = x | (x >> 1); // Propagate leftmost
+ x = x | (x >> 2); // 1-bit to the right.
+ x = x | (x >> 4);
+ x = x | (x >> 8);
+ x = x & ~(x >> 16);
+ x = x*0xFD7049FF; // Activate this line or the following 3.
+// x = (x << 9) - x; // Multiply by 511.
+// x = (x << 11) - x; // Multiply by 2047.
+// x = (x << 14) - x; // Multiply by 16383.
+ return table[x >> 26];
+}
+*/
+int BitStream::NumberOfLeadingZeroes( int8_t x ) {return NumberOfLeadingZeroes((uint8_t)x);}
+int BitStream::NumberOfLeadingZeroes( uint8_t x )
+{
+ uint8_t y;
+ int n;
+
+ n = 8;
+ y = x >> 4; if (y != 0) {n = n - 4; x = y;}
+ y = x >> 2; if (y != 0) {n = n - 2; x = y;}
+ y = x >> 1; if (y != 0) return n - 2;
+ return (int)(n - x);
+}
+int BitStream::NumberOfLeadingZeroes( int16_t x ) {return NumberOfLeadingZeroes((uint16_t)x);}
+int BitStream::NumberOfLeadingZeroes( uint16_t x )
+{
+ uint16_t y;
+ int n;
+
+ n = 16;
+ y = x >> 8; if (y != 0) {n = n - 8; x = y;}
+ y = x >> 4; if (y != 0) {n = n - 4; x = y;}
+ y = x >> 2; if (y != 0) {n = n - 2; x = y;}
+ y = x >> 1; if (y != 0) return n - 2;
+ return (int)(n - x);
+}
+int BitStream::NumberOfLeadingZeroes( int32_t x ) {return NumberOfLeadingZeroes((uint32_t)x);}
+int BitStream::NumberOfLeadingZeroes( uint32_t x )
+{
+ uint32_t y;
+ int n;
+
+ n = 32;
+ y = x >>16; if (y != 0) {n = n -16; x = y;}
+ y = x >> 8; if (y != 0) {n = n - 8; x = y;}
+ y = x >> 4; if (y != 0) {n = n - 4; x = y;}
+ y = x >> 2; if (y != 0) {n = n - 2; x = y;}
+ y = x >> 1; if (y != 0) return n - 2;
+ return (int)(n - x);
+}
+int BitStream::NumberOfLeadingZeroes( int64_t x ) {return NumberOfLeadingZeroes((uint64_t)x);}
+int BitStream::NumberOfLeadingZeroes( uint64_t x )
+{
+ uint64_t y;
+ int n;
+
+ n = 64;
+ y = x >>32; if (y != 0) {n = n -32; x = y;}
+ y = x >>16; if (y != 0) {n = n -16; x = y;}
+ y = x >> 8; if (y != 0) {n = n - 8; x = y;}
+ y = x >> 4; if (y != 0) {n = n - 4; x = y;}
+ y = x >> 2; if (y != 0) {n = n - 2; x = y;}
+ y = x >> 1; if (y != 0) return n - 2;
+ return (int)(n - x);
+}
+
+// Should hit if reads didn't match writes
+void BitStream::AssertStreamEmpty( void )
+{
+ OVR_ASSERT( readOffset == numberOfBitsUsed );
+}
+void BitStream::PrintBits( char *out ) const
+{
+ if ( numberOfBitsUsed <= 0 )
+ {
+ OVR_strcpy(out, 128, "No bits\n" );
+ return;
+ }
+
+ unsigned int strIndex=0;
+ for ( BitSize_t counter = 0; counter < BITS_TO_BYTES( numberOfBitsUsed ) && strIndex < 2000 ; counter++ )
+ {
+ BitSize_t stop;
+
+ if ( counter == ( numberOfBitsUsed - 1 ) >> 3 )
+ stop = 8 - ( ( ( numberOfBitsUsed - 1 ) & 7 ) + 1 );
+ else
+ stop = 0;
+
+ for ( BitSize_t counter2 = 7; counter2 >= stop; counter2-- )
+ {
+ if ( ( data[ counter ] >> counter2 ) & 1 )
+ out[strIndex++]='1';
+ else
+ out[strIndex++]='0';
+
+ if (counter2==0)
+ break;
+ }
+
+ out[strIndex++]=' ';
+ }
+
+ out[strIndex++]='\n';
+
+ out[strIndex++]=0;
+}
+void BitStream::PrintBits( void ) const
+{
+ char out[2048];
+ PrintBits(out);
+ printf("%s", out);
+}
+void BitStream::PrintHex( char *out ) const
+{
+ BitSize_t i;
+ for ( i=0; i < GetNumberOfBytesUsed(); i++)
+ {
+ OVR_sprintf(out+i*3, 128, "%02x ", data[i]);
+ }
+}
+void BitStream::PrintHex( void ) const
+{
+ char out[2048];
+ PrintHex(out);
+ printf("%s", out);
+}
+
+// Exposes the data for you to look at, like PrintBits does.
+// Data will point to the stream. Returns the length in bits of the stream.
+BitSize_t BitStream::CopyData( unsigned char** _data ) const
+{
+#ifdef _DEBUG
+ OVR_ASSERT( numberOfBitsUsed > 0 );
+#endif
+
+ *_data = (unsigned char*) OVR_ALLOC( (size_t) BITS_TO_BYTES( numberOfBitsUsed ));
+ memcpy( *_data, data, sizeof(unsigned char) * (size_t) ( BITS_TO_BYTES( numberOfBitsUsed ) ) );
+ return numberOfBitsUsed;
+}
+
+// Ignore data we don't intend to read
+void BitStream::IgnoreBits( const BitSize_t numberOfBits )
+{
+ readOffset += numberOfBits;
+}
+
+void BitStream::IgnoreBytes( const unsigned int numberOfBytes )
+{
+ IgnoreBits(BYTES_TO_BITS(numberOfBytes));
+}
+
+// Move the write pointer to a position on the array. Dangerous if you don't know what you are doing!
+// Doesn't work with non-aligned data!
+void BitStream::SetWriteOffset( const BitSize_t offset )
+{
+ numberOfBitsUsed = offset;
+}
+
+/*
+BitSize_t BitStream::GetWriteOffset( void ) const
+{
+return numberOfBitsUsed;
+}
+
+// Returns the length in bits of the stream
+BitSize_t BitStream::GetNumberOfBitsUsed( void ) const
+{
+return GetWriteOffset();
+}
+
+// Returns the length in bytes of the stream
+BitSize_t BitStream::GetNumberOfBytesUsed( void ) const
+{
+return BITS_TO_BYTES( numberOfBitsUsed );
+}
+
+// Returns the number of bits into the stream that we have read
+BitSize_t BitStream::GetReadOffset( void ) const
+{
+return readOffset;
+}
+
+
+// Sets the read bit index
+void BitStream::SetReadOffset( const BitSize_t newReadOffset )
+{
+readOffset=newReadOffset;
+}
+
+// Returns the number of bits left in the stream that haven't been read
+BitSize_t BitStream::GetNumberOfUnreadBits( void ) const
+{
+return numberOfBitsUsed - readOffset;
+}
+// Exposes the internal data
+unsigned char* BitStream::GetData( void ) const
+{
+return data;
+}
+
+*/
+// If we used the constructor version with copy data off, this makes sure it is set to on and the data pointed to is copied.
+void BitStream::AssertCopyData( void )
+{
+ if ( copyData == false )
+ {
+ copyData = true;
+
+ if ( numberOfBitsAllocated > 0 )
+ {
+ unsigned char * newdata = ( unsigned char* ) OVR_ALLOC( (size_t) BITS_TO_BYTES( numberOfBitsAllocated ));
+#ifdef _DEBUG
+
+ OVR_ASSERT( data );
+#endif
+
+ memcpy( newdata, data, (size_t) BITS_TO_BYTES( numberOfBitsAllocated ) );
+ data = newdata;
+ }
+
+ else
+ data = 0;
+ }
+}
+bool BitStream::IsNetworkOrderInternal(void)
+{
+#if defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3)
+ return true;
+#elif defined(SN_TARGET_PSP2)
+ return false;
+#else
+ static unsigned long htonlValue = htonl(12345);
+ return htonlValue == 12345;
+#endif
+}
+void BitStream::ReverseBytes(unsigned char *inByteArray, unsigned char *inOutByteArray, const unsigned int length)
+{
+ for (BitSize_t i=0; i < length; i++)
+ inOutByteArray[i]=inByteArray[length-i-1];
+}
+void BitStream::ReverseBytesInPlace(unsigned char *inOutData,const unsigned int length)
+{
+ unsigned char temp;
+ BitSize_t i;
+ for (i=0; i < (length>>1); i++)
+ {
+ temp = inOutData[i];
+ inOutData[i]=inOutData[length-i-1];
+ inOutData[length-i-1]=temp;
+ }
+}
+
+void BitStream::WriteAlignedVar8(const char *inByteArray)
+{
+ OVR_ASSERT((numberOfBitsUsed&7)==0);
+ AddBitsAndReallocate(1*8);
+ data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[0];
+ numberOfBitsUsed+=1*8;
+}
+bool BitStream::ReadAlignedVar8(char *inOutByteArray)
+{
+ OVR_ASSERT((readOffset&7)==0);
+ if ( readOffset + 1*8 > numberOfBitsUsed )
+ return false;
+
+ inOutByteArray[0] = data[( readOffset >> 3 ) + 0];
+ readOffset+=1*8;
+ return true;
+}
+void BitStream::WriteAlignedVar16(const char *inByteArray)
+{
+ OVR_ASSERT((numberOfBitsUsed&7)==0);
+ AddBitsAndReallocate(2*8);
+#ifndef __BITSTREAM_NATIVE_END
+ if (DoEndianSwap())
+ {
+ data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[1];
+ data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[0];
+ }
+ else
+#endif
+ {
+ data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[0];
+ data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[1];
+ }
+
+ numberOfBitsUsed+=2*8;
+}
+bool BitStream::ReadAlignedVar16(char *inOutByteArray)
+{
+ OVR_ASSERT((readOffset&7)==0);
+ if ( readOffset + 2*8 > numberOfBitsUsed )
+ return false;
+#ifndef __BITSTREAM_NATIVE_END
+ if (DoEndianSwap())
+ {
+ inOutByteArray[0] = data[( readOffset >> 3 ) + 1];
+ inOutByteArray[1] = data[( readOffset >> 3 ) + 0];
+ }
+ else
+#endif
+ {
+ inOutByteArray[0] = data[( readOffset >> 3 ) + 0];
+ inOutByteArray[1] = data[( readOffset >> 3 ) + 1];
+ }
+
+ readOffset+=2*8;
+ return true;
+}
+void BitStream::WriteAlignedVar32(const char *inByteArray)
+{
+ OVR_ASSERT((numberOfBitsUsed&7)==0);
+ AddBitsAndReallocate(4*8);
+#ifndef __BITSTREAM_NATIVE_END
+ if (DoEndianSwap())
+ {
+ data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[3];
+ data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[2];
+ data[( numberOfBitsUsed >> 3 ) + 2] = inByteArray[1];
+ data[( numberOfBitsUsed >> 3 ) + 3] = inByteArray[0];
+ }
+ else
+#endif
+ {
+ data[( numberOfBitsUsed >> 3 ) + 0] = inByteArray[0];
+ data[( numberOfBitsUsed >> 3 ) + 1] = inByteArray[1];
+ data[( numberOfBitsUsed >> 3 ) + 2] = inByteArray[2];
+ data[( numberOfBitsUsed >> 3 ) + 3] = inByteArray[3];
+ }
+
+ numberOfBitsUsed+=4*8;
+}
+bool BitStream::ReadAlignedVar32(char *inOutByteArray)
+{
+ OVR_ASSERT((readOffset&7)==0);
+ if ( readOffset + 4*8 > numberOfBitsUsed )
+ return false;
+#ifndef __BITSTREAM_NATIVE_END
+ if (DoEndianSwap())
+ {
+ inOutByteArray[0] = data[( readOffset >> 3 ) + 3];
+ inOutByteArray[1] = data[( readOffset >> 3 ) + 2];
+ inOutByteArray[2] = data[( readOffset >> 3 ) + 1];
+ inOutByteArray[3] = data[( readOffset >> 3 ) + 0];
+ }
+ else
+#endif
+ {
+ inOutByteArray[0] = data[( readOffset >> 3 ) + 0];
+ inOutByteArray[1] = data[( readOffset >> 3 ) + 1];
+ inOutByteArray[2] = data[( readOffset >> 3 ) + 2];
+ inOutByteArray[3] = data[( readOffset >> 3 ) + 3];
+ }
+
+ readOffset+=4*8;
+ return true;
+}
+bool BitStream::ReadFloat16( float &outFloat, float floatMin, float floatMax )
+{
+ uint16_t percentile;
+ if (Read(percentile))
+ {
+ OVR_ASSERT(floatMax>floatMin);
+ outFloat = floatMin + ((float) percentile / 65535.0f) * (floatMax-floatMin);
+ if (outFloat<floatMin)
+ outFloat=floatMin;
+ else if (outFloat>floatMax)
+ outFloat=floatMax;
+ return true;
+ }
+ return false;
+}
+bool BitStream::SerializeFloat16(bool writeToBitstream, float &inOutFloat, float floatMin, float floatMax)
+{
+ if (writeToBitstream)
+ WriteFloat16(inOutFloat, floatMin, floatMax);
+ else
+ return ReadFloat16(inOutFloat, floatMin, floatMax);
+ return true;
+}
+void BitStream::WriteFloat16( float inOutFloat, float floatMin, float floatMax )
+{
+ OVR_ASSERT(floatMax>floatMin);
+ if (inOutFloat>floatMax+.001)
+ {
+ OVR_ASSERT(inOutFloat<=floatMax+.001);
+ }
+ if (inOutFloat<floatMin-.001)
+ {
+ OVR_ASSERT(inOutFloat>=floatMin-.001);
+ }
+ float percentile=65535.0f * (inOutFloat-floatMin)/(floatMax-floatMin);
+ if (percentile<0.0)
+ percentile=0.0;
+ if (percentile>65535.0f)
+ percentile=65535.0f;
+ Write((uint16_t)percentile);
+}
+
+
+}} // OVR::Net
diff --git a/LibOVR/Src/Net/OVR_BitStream.h b/LibOVR/Src/Net/OVR_BitStream.h
new file mode 100644
index 0000000..b1ddc8f
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_BitStream.h
@@ -0,0 +1,1744 @@
+/************************************************************************************
+
+PublicHeader: n/a
+Filename : OVR_BitStream.h
+Content : A generic serialization toolkit for packing data to a binary stream.
+Created : June 10, 2014
+Authors : Kevin Jenkins
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Bitstream_h
+#define OVR_Bitstream_h
+
+#include <math.h>
+#include "../Kernel/OVR_Types.h"
+#include "../Kernel/OVR_Std.h"
+#include "../Kernel/OVR_String.h"
+
+namespace OVR { namespace Net {
+
+typedef uint32_t BitSize_t;
+#define BITSTREAM_STACK_ALLOCATION_SIZE 256
+#define BITS_TO_BYTES(x) (((x)+7)>>3)
+#define BYTES_TO_BITS(x) ((x)<<3)
+
+
+//-----------------------------------------------------------------------------
+// BitStream
+
+// Generic serialization class to binary stream
+class BitStream : public NewOverrideBase
+{
+public:
+ /// Default Constructor
+ BitStream();
+
+ /// \brief Create the bitstream, with some number of bytes to immediately allocate.
+ /// \details There is no benefit to calling this, unless you know exactly how many bytes you need and it is greater than BITSTREAM_STACK_ALLOCATION_SIZE.
+ /// In that case all it does is save you one or more realloc calls.
+ /// \param[in] initialBytesToAllocate the number of bytes to pre-allocate.
+ BitStream( const unsigned int initialBytesToAllocate );
+
+ /// \brief Initialize the BitStream, immediately setting the data it contains to a predefined pointer.
+ /// \details Set \a _copyData to true if you want to make an internal copy of the data you are passing. Set it to false to just save a pointer to the data.
+ /// You shouldn't call Write functions with \a _copyData as false, as this will write to unallocated memory
+ /// 99% of the time you will use this function to cast Packet::data to a bitstream for reading, in which case you should write something as follows:
+ /// \code
+ /// RakNet::BitStream bs(packet->data, packet->length, false);
+ /// \endcode
+ /// \param[in] _data An array of bytes.
+ /// \param[in] lengthInBytes Size of the \a _data.
+ /// \param[in] _copyData true or false to make a copy of \a _data or not.
+ BitStream( char* _data, const unsigned int lengthInBytes, bool _copyData );
+
+ // Destructor
+ ~BitStream();
+
+public:
+ /// Resets the bitstream for reuse.
+ void Reset( void );
+
+ /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream.
+ /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping.
+ /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
+ /// \param[in] inOutTemplateVar The value to write
+ /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
+ template <class templateType>
+ bool Serialize(bool writeToBitstream, templateType &inOutTemplateVar);
+
+ /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream.
+ /// \details If the current value is different from the last value
+ /// the current value will be written. Otherwise, a single bit will be written
+ /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
+ /// \param[in] inOutCurrentValue The current value to write
+ /// \param[in] lastValue The last value to compare against. Only used if \a writeToBitstream is true.
+ /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
+ template <class templateType>
+ bool SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue);
+
+ /// \brief Bidirectional version of SerializeDelta when you don't know what the last value is, or there is no last value.
+ /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
+ /// \param[in] inOutCurrentValue The current value to write
+ /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
+ template <class templateType>
+ bool SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue);
+
+ /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream.
+ /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping.
+ /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte
+ /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1.
+ /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type
+ /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
+ /// \param[in] inOutTemplateVar The value to write
+ /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
+ template <class templateType>
+ bool SerializeCompressed(bool writeToBitstream, templateType &inOutTemplateVar);
+
+ /// \brief Bidirectional serialize/deserialize any integral type to/from a bitstream.
+ /// \details If the current value is different from the last value
+ /// the current value will be written. Otherwise, a single bit will be written
+ /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1.
+ /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type
+ /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte
+ /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
+ /// \param[in] inOutCurrentValue The current value to write
+ /// \param[in] lastValue The last value to compare against. Only used if \a writeToBitstream is true.
+ /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
+ template <class templateType>
+ bool SerializeCompressedDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue);
+
+ /// \brief Save as SerializeCompressedDelta(templateType &currentValue, const templateType &lastValue) when we have an unknown second parameter
+ /// \return true on data read. False on insufficient data in bitstream
+ template <class templateType>
+ bool SerializeCompressedDelta(bool writeToBitstream, templateType &inOutTemplateVar);
+
+ /// \brief Bidirectional serialize/deserialize an array or casted stream or raw data. This does NOT do endian swapping.
+ /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
+ /// \param[in] inOutByteArray a byte buffer
+ /// \param[in] numberOfBytes the size of \a input in bytes
+ /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
+ bool Serialize(bool writeToBitstream, char* inOutByteArray, const unsigned int numberOfBytes );
+
+ /// \brief Serialize a float into 2 bytes, spanning the range between \a floatMin and \a floatMax
+ /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
+ /// \param[in] inOutFloat The float to write
+ /// \param[in] floatMin Predetermined minimum value of f
+ /// \param[in] floatMax Predetermined maximum value of f
+ bool SerializeFloat16(bool writeToBitstream, float &inOutFloat, float floatMin, float floatMax);
+
+ /// Serialize one type casted to another (smaller) type, to save bandwidth
+ /// serializationType should be uint8_t, uint16_t, uint24_t, or uint32_t
+ /// Example: int num=53; SerializeCasted<uint8_t>(true, num); would use 1 byte to write what would otherwise be an integer (4 or 8 bytes)
+ /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
+ /// \param[in] value The value to serialize
+ template <class serializationType, class sourceType >
+ bool SerializeCasted( bool writeToBitstream, sourceType &value );
+
+ /// Given the minimum and maximum values for an integer type, figure out the minimum number of bits to represent the range
+ /// Then serialize only those bits
+ /// \note A static is used so that the required number of bits for (maximum-minimum) is only calculated once. This does require that \a minimum and \maximum are fixed values for a given line of code for the life of the program
+ /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
+ /// \param[in] value Integer value to write, which should be between \a minimum and \a maximum
+ /// \param[in] minimum Minimum value of \a value
+ /// \param[in] maximum Maximum value of \a value
+ /// \param[in] allowOutsideRange If true, all sends will take an extra bit, however value can deviate from outside \a minimum and \a maximum. If false, will assert if the value deviates
+ template <class templateType>
+ bool SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange=false );
+ /// \param[in] requiredBits Primarily for internal use, called from above function() after calculating number of bits needed to represent maximum-minimum
+ template <class templateType>
+ bool SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange=false );
+
+ /// \brief Bidirectional serialize/deserialize a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes.
+ /// \details Will further compress y or z axis aligned vectors.
+ /// Accurate to 1/32767.5.
+ /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
+ /// \param[in] x x
+ /// \param[in] y y
+ /// \param[in] z z
+ /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
+ template <class templateType> // templateType for this function must be a float or double
+ bool SerializeNormVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z );
+
+ /// \brief Bidirectional serialize/deserialize a vector, using 10 bytes instead of 12.
+ /// \details Loses accuracy to about 3/10ths and only saves 2 bytes, so only use if accuracy is not important.
+ /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
+ /// \param[in] x x
+ /// \param[in] y y
+ /// \param[in] z z
+ /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
+ template <class templateType> // templateType for this function must be a float or double
+ bool SerializeVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z );
+
+ /// \brief Bidirectional serialize/deserialize a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy.
+ /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
+ /// \param[in] w w
+ /// \param[in] x x
+ /// \param[in] y y
+ /// \param[in] z z
+ /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
+ template <class templateType> // templateType for this function must be a float or double
+ bool SerializeNormQuat(bool writeToBitstream, templateType &w, templateType &x, templateType &y, templateType &z);
+
+ /// \brief Bidirectional serialize/deserialize an orthogonal matrix by creating a quaternion, and writing 3 components of the quaternion in 2 bytes each.
+ /// \details Use 6 bytes instead of 36
+ /// Lossy, although the result is renormalized
+ /// \return true on success, false on failure.
+ template <class templateType> // templateType for this function must be a float or double
+ bool SerializeOrthMatrix(
+ bool writeToBitstream,
+ templateType &m00, templateType &m01, templateType &m02,
+ templateType &m10, templateType &m11, templateType &m12,
+ templateType &m20, templateType &m21, templateType &m22 );
+
+ /// \brief Bidirectional serialize/deserialize numberToSerialize bits to/from the input.
+ /// \details Right aligned data means in the case of a partial byte, the bits are aligned
+ /// from the right (bit 0) rather than the left (as in the normal
+ /// internal representation) You would set this to true when
+ /// writing user data, and false when copying bitstream data, such
+ /// as writing one bitstream to another
+ /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
+ /// \param[in] inOutByteArray The data
+ /// \param[in] numberOfBitsToSerialize The number of bits to write
+ /// \param[in] rightAlignedBits if true data will be right aligned
+ /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
+ bool SerializeBits(bool writeToBitstream, unsigned char* inOutByteArray, const BitSize_t numberOfBitsToSerialize, const bool rightAlignedBits = true );
+
+ /// \brief Write any integral type to a bitstream.
+ /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping.
+ /// \param[in] inTemplateVar The value to write
+ template <class templateType>
+ void Write(const templateType &inTemplateVar);
+
+ /// \brief Write the dereferenced pointer to any integral type to a bitstream.
+ /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping.
+ /// \param[in] inTemplateVar The value to write
+ template <class templateType>
+ void WritePtr(templateType *inTemplateVar);
+
+ /// \brief Write any integral type to a bitstream.
+ /// \details If the current value is different from the last value
+ /// the current value will be written. Otherwise, a single bit will be written
+ /// \param[in] currentValue The current value to write
+ /// \param[in] lastValue The last value to compare against
+ template <class templateType>
+ void WriteDelta(const templateType &currentValue, const templateType &lastValue);
+
+ /// \brief WriteDelta when you don't know what the last value is, or there is no last value.
+ /// \param[in] currentValue The current value to write
+ template <class templateType>
+ void WriteDelta(const templateType &currentValue);
+
+ /// \brief Write any integral type to a bitstream.
+ /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping.
+ /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte
+ /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1.
+ /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type
+ /// \param[in] inTemplateVar The value to write
+ template <class templateType>
+ void WriteCompressed(const templateType &inTemplateVar);
+
+ /// \brief Write any integral type to a bitstream.
+ /// \details If the current value is different from the last value
+ /// the current value will be written. Otherwise, a single bit will be written
+ /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1.
+ /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type
+ /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte
+ /// \param[in] currentValue The current value to write
+ /// \param[in] lastValue The last value to compare against
+ template <class templateType>
+ void WriteCompressedDelta(const templateType &currentValue, const templateType &lastValue);
+
+ /// \brief Save as WriteCompressedDelta(const templateType &currentValue, const templateType &lastValue) when we have an unknown second parameter
+ template <class templateType>
+ void WriteCompressedDelta(const templateType &currentValue);
+
+ /// \brief Read any integral type from a bitstream.
+ /// \details Define __BITSTREAM_NATIVE_END if you need endian swapping.
+ /// \param[in] outTemplateVar The value to read
+ /// \return true on success, false on failure.
+ template <class templateType>
+ bool Read(templateType &outTemplateVar);
+
+ /// \brief Read any integral type from a bitstream.
+ /// \details If the written value differed from the value compared against in the write function,
+ /// var will be updated. Otherwise it will retain the current value.
+ /// ReadDelta is only valid from a previous call to WriteDelta
+ /// \param[in] outTemplateVar The value to read
+ /// \return true on success, false on failure.
+ template <class templateType>
+ bool ReadDelta(templateType &outTemplateVar);
+
+ /// \brief Read any integral type from a bitstream.
+ /// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping.
+ /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1.
+ /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type
+ /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte
+ /// \param[in] outTemplateVar The value to read
+ /// \return true on success, false on failure.
+ template <class templateType>
+ bool ReadCompressed(templateType &outTemplateVar);
+
+ /// \brief Read any integral type from a bitstream.
+ /// \details If the written value differed from the value compared against in the write function,
+ /// var will be updated. Otherwise it will retain the current value.
+ /// the current value will be updated.
+ /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1.
+ /// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type
+ /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte
+ /// ReadCompressedDelta is only valid from a previous call to WriteDelta
+ /// \param[in] outTemplateVar The value to read
+ /// \return true on success, false on failure.
+ template <class templateType>
+ bool ReadCompressedDelta(templateType &outTemplateVar);
+
+ /// \brief Read one bitstream to another.
+ /// \param[in] numberOfBits bits to read
+ /// \param bitStream the bitstream to read into from
+ /// \return true on success, false on failure.
+ bool Read( BitStream *bitStream, BitSize_t numberOfBits );
+ bool Read( BitStream *bitStream );
+ bool Read( BitStream &bitStream, BitSize_t numberOfBits );
+ bool Read( BitStream &bitStream );
+
+ /// \brief Write an array or casted stream or raw data. This does NOT do endian swapping.
+ /// \param[in] inputByteArray a byte buffer
+ /// \param[in] numberOfBytes the size of \a input in bytes
+ void Write( const char* inputByteArray, const unsigned int numberOfBytes );
+
+ /// \brief Write one bitstream to another.
+ /// \param[in] numberOfBits bits to write
+ /// \param bitStream the bitstream to copy from
+ void Write( BitStream *bitStream, BitSize_t numberOfBits );
+ void Write( BitStream *bitStream );
+ void Write( BitStream &bitStream, BitSize_t numberOfBits );
+ void Write( BitStream &bitStream );\
+
+ /// \brief Write a float into 2 bytes, spanning the range between \a floatMin and \a floatMax
+ /// \param[in] x The float to write
+ /// \param[in] floatMin Predetermined minimum value of f
+ /// \param[in] floatMax Predetermined maximum value of f
+ void WriteFloat16( float x, float floatMin, float floatMax );
+
+ /// Write one type serialized as another (smaller) type, to save bandwidth
+ /// serializationType should be uint8_t, uint16_t, uint24_t, or uint32_t
+ /// Example: int num=53; WriteCasted<uint8_t>(num); would use 1 byte to write what would otherwise be an integer (4 or 8 bytes)
+ /// \param[in] value The value to write
+ template <class serializationType, class sourceType >
+ void WriteCasted( const sourceType &value );
+
+ /// Given the minimum and maximum values for an integer type, figure out the minimum number of bits to represent the range
+ /// Then write only those bits
+ /// \note A static is used so that the required number of bits for (maximum-minimum) is only calculated once. This does require that \a minimum and \maximum are fixed values for a given line of code for the life of the program
+ /// \param[in] value Integer value to write, which should be between \a minimum and \a maximum
+ /// \param[in] minimum Minimum value of \a value
+ /// \param[in] maximum Maximum value of \a value
+ /// \param[in] allowOutsideRange If true, all sends will take an extra bit, however value can deviate from outside \a minimum and \a maximum. If false, will assert if the value deviates. This should match the corresponding value passed to Read().
+ template <class templateType>
+ void WriteBitsFromIntegerRange( const templateType value, const templateType minimum, const templateType maximum, bool allowOutsideRange=false );
+ /// \param[in] requiredBits Primarily for internal use, called from above function() after calculating number of bits needed to represent maximum-minimum
+ template <class templateType>
+ void WriteBitsFromIntegerRange( const templateType value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange=false );
+
+ /// \brief Write a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes.
+ /// \details Will further compress y or z axis aligned vectors.
+ /// Accurate to 1/32767.5.
+ /// \param[in] x x
+ /// \param[in] y y
+ /// \param[in] z z
+ template <class templateType> // templateType for this function must be a float or double
+ void WriteNormVector( templateType x, templateType y, templateType z );
+
+ /// \brief Write a vector, using 10 bytes instead of 12.
+ /// \details Loses accuracy to about 3/10ths and only saves 2 bytes,
+ /// so only use if accuracy is not important.
+ /// \param[in] x x
+ /// \param[in] y y
+ /// \param[in] z z
+ template <class templateType> // templateType for this function must be a float or double
+ void WriteVector( templateType x, templateType y, templateType z );
+
+ /// \brief Write a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy.
+ /// \param[in] w w
+ /// \param[in] x x
+ /// \param[in] y y
+ /// \param[in] z z
+ template <class templateType> // templateType for this function must be a float or double
+ void WriteNormQuat( templateType w, templateType x, templateType y, templateType z);
+
+ /// \brief Write an orthogonal matrix by creating a quaternion, and writing 3 components of the quaternion in 2 bytes each.
+ /// \details Use 6 bytes instead of 36
+ /// Lossy, although the result is renormalized
+ template <class templateType> // templateType for this function must be a float or double
+ void WriteOrthMatrix(
+ templateType m00, templateType m01, templateType m02,
+ templateType m10, templateType m11, templateType m12,
+ templateType m20, templateType m21, templateType m22 );
+
+ /// \brief Read an array or casted stream of byte.
+ /// \details The array is raw data. There is no automatic endian conversion with this function
+ /// \param[in] output The result byte array. It should be larger than @em numberOfBytes.
+ /// \param[in] numberOfBytes The number of byte to read
+ /// \return true on success false if there is some missing bytes.
+ bool Read( char* output, const unsigned int numberOfBytes );
+
+ /// \brief Read a float into 2 bytes, spanning the range between \a floatMin and \a floatMax
+ /// \param[in] outFloat The float to read
+ /// \param[in] floatMin Predetermined minimum value of f
+ /// \param[in] floatMax Predetermined maximum value of f
+ bool ReadFloat16( float &outFloat, float floatMin, float floatMax );
+
+ /// Read one type serialized to another (smaller) type, to save bandwidth
+ /// serializationType should be uint8_t, uint16_t, uint24_t, or uint32_t
+ /// Example: int num; ReadCasted<uint8_t>(num); would read 1 bytefrom the stream, and put the value in an integer
+ /// \param[in] value The value to write
+ template <class serializationType, class sourceType >
+ bool ReadCasted( sourceType &value );
+
+ /// Given the minimum and maximum values for an integer type, figure out the minimum number of bits to represent the range
+ /// Then read only those bits
+ /// \note A static is used so that the required number of bits for (maximum-minimum) is only calculated once. This does require that \a minimum and \maximum are fixed values for a given line of code for the life of the program
+ /// \param[in] value Integer value to read, which should be between \a minimum and \a maximum
+ /// \param[in] minimum Minimum value of \a value
+ /// \param[in] maximum Maximum value of \a value
+ /// \param[in] allowOutsideRange If true, all sends will take an extra bit, however value can deviate from outside \a minimum and \a maximum. If false, will assert if the value deviates. This should match the corresponding value passed to Write().
+ template <class templateType>
+ bool ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange=false );
+ /// \param[in] requiredBits Primarily for internal use, called from above function() after calculating number of bits needed to represent maximum-minimum
+ template <class templateType>
+ bool ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange=false );
+
+ /// \brief Read a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes.
+ /// \details Will further compress y or z axis aligned vectors.
+ /// Accurate to 1/32767.5.
+ /// \param[in] x x
+ /// \param[in] y y
+ /// \param[in] z z
+ /// \return true on success, false on failure.
+ template <class templateType> // templateType for this function must be a float or double
+ bool ReadNormVector( templateType &x, templateType &y, templateType &z );
+
+ /// \brief Read 3 floats or doubles, using 10 bytes, where those float or doubles comprise a vector.
+ /// \details Loses accuracy to about 3/10ths and only saves 2 bytes,
+ /// so only use if accuracy is not important.
+ /// \param[in] x x
+ /// \param[in] y y
+ /// \param[in] z z
+ /// \return true on success, false on failure.
+ template <class templateType> // templateType for this function must be a float or double
+ bool ReadVector( templateType &x, templateType &y, templateType &z );
+
+ /// \brief Read a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes.
+ /// \param[in] w w
+ /// \param[in] x x
+ /// \param[in] y y
+ /// \param[in] z z
+ /// \return true on success, false on failure.
+ template <class templateType> // templateType for this function must be a float or double
+ bool ReadNormQuat( templateType &w, templateType &x, templateType &y, templateType &z);
+
+ /// \brief Read an orthogonal matrix from a quaternion, reading 3 components of the quaternion in 2 bytes each and extrapolatig the 4th.
+ /// \details Use 6 bytes instead of 36
+ /// Lossy, although the result is renormalized
+ /// \return true on success, false on failure.
+ template <class templateType> // templateType for this function must be a float or double
+ bool ReadOrthMatrix(
+ templateType &m00, templateType &m01, templateType &m02,
+ templateType &m10, templateType &m11, templateType &m12,
+ templateType &m20, templateType &m21, templateType &m22 );
+
+ /// \brief Sets the read pointer back to the beginning of your data.
+ void ResetReadPointer( void );
+
+ /// \brief Sets the write pointer back to the beginning of your data.
+ void ResetWritePointer( void );
+
+ /// \brief This is good to call when you are done with the stream to make
+ /// sure you didn't leave any data left over void
+ void AssertStreamEmpty( void );
+
+ /// \brief RAKNET_DEBUG_PRINTF the bits in the stream. Great for debugging.
+ void PrintBits( char *out ) const;
+ void PrintBits( void ) const;
+ void PrintHex( char *out ) const;
+ void PrintHex( void ) const;
+
+ /// \brief Ignore data we don't intend to read
+ /// \param[in] numberOfBits The number of bits to ignore
+ void IgnoreBits( const BitSize_t numberOfBits );
+
+ /// \brief Ignore data we don't intend to read
+ /// \param[in] numberOfBits The number of bytes to ignore
+ void IgnoreBytes( const unsigned int numberOfBytes );
+
+ /// \brief Move the write pointer to a position on the array.
+ /// \param[in] offset the offset from the start of the array.
+ /// \attention
+ /// \details Dangerous if you don't know what you are doing!
+ /// For efficiency reasons you can only write mid-stream if your data is byte aligned.
+ void SetWriteOffset( const BitSize_t offset );
+
+ /// \brief Returns the length in bits of the stream
+ inline BitSize_t GetNumberOfBitsUsed( void ) const {return GetWriteOffset();}
+ inline BitSize_t GetWriteOffset( void ) const {return numberOfBitsUsed;}
+
+ /// \brief Returns the length in bytes of the stream
+ inline BitSize_t GetNumberOfBytesUsed( void ) const {return BITS_TO_BYTES( numberOfBitsUsed );}
+
+ /// \brief Returns the number of bits into the stream that we have read
+ inline BitSize_t GetReadOffset( void ) const {return readOffset;}
+
+ /// \brief Sets the read bit index
+ void SetReadOffset( const BitSize_t newReadOffset ) {readOffset=newReadOffset;}
+
+ /// \brief Returns the number of bits left in the stream that haven't been read
+ inline BitSize_t GetNumberOfUnreadBits( void ) const {return numberOfBitsUsed - readOffset;}
+
+ /// \brief Makes a copy of the internal data for you \a _data will point to
+ /// the stream. Partial bytes are left aligned.
+ /// \param[out] _data The allocated copy of GetData()
+ /// \return The length in bits of the stream.
+ BitSize_t CopyData( unsigned char** _data ) const;
+
+ /// \internal
+ /// Set the stream to some initial data.
+ void SetData( unsigned char *inByteArray );
+
+ /// Gets the data that BitStream is writing to / reading from.
+ /// Partial bytes are left aligned.
+ /// \return A pointer to the internal state
+ inline char* GetData( void ) const {return (char*) data;}
+
+ /// \brief Write numberToWrite bits from the input source.
+ /// \details Right aligned data means in the case of a partial byte, the bits are aligned
+ /// from the right (bit 0) rather than the left (as in the normal
+ /// internal representation) You would set this to true when
+ /// writing user data, and false when copying bitstream data, such
+ /// as writing one bitstream to another.
+ /// \param[in] inByteArray The data
+ /// \param[in] numberOfBitsToWrite The number of bits to write
+ /// \param[in] rightAlignedBits if true data will be right aligned
+ void WriteBits( const unsigned char* inByteArray, BitSize_t numberOfBitsToWrite, const bool rightAlignedBits = true );
+
+ /// \brief Align the bitstream to the byte boundary and then write the
+ /// specified number of bits.
+ /// \details This is faster than WriteBits but
+ /// wastes the bits to do the alignment and requires you to call
+ /// ReadAlignedBits at the corresponding read position.
+ /// \param[in] inByteArray The data
+ /// \param[in] numberOfBytesToWrite The size of input.
+ void WriteAlignedBytes( const unsigned char *inByteArray, const unsigned int numberOfBytesToWrite );
+
+ // Endian swap bytes already in the bitstream
+ void EndianSwapBytes( int byteOffset, int length );
+
+ /// \brief Aligns the bitstream, writes inputLength, and writes input. Won't write beyond maxBytesToWrite
+ /// \param[in] inByteArray The data
+ /// \param[in] inputLength The size of input.
+ /// \param[in] maxBytesToWrite Max bytes to write
+ void WriteAlignedBytesSafe( const char *inByteArray, const unsigned int inputLength, const unsigned int maxBytesToWrite );
+
+ /// \brief Read bits, starting at the next aligned bits.
+ /// \details Note that the modulus 8 starting offset of the sequence must be the same as
+ /// was used with WriteBits. This will be a problem with packet
+ /// coalescence unless you byte align the coalesced packets.
+ /// \param[in] inOutByteArray The byte array larger than @em numberOfBytesToRead
+ /// \param[in] numberOfBytesToRead The number of byte to read from the internal state
+ /// \return true if there is enough byte.
+ bool ReadAlignedBytes( unsigned char *inOutByteArray, const unsigned int numberOfBytesToRead );
+
+ /// \brief Reads what was written by WriteAlignedBytesSafe.
+ /// \param[in] inOutByteArray The data
+ /// \param[in] maxBytesToRead Maximum number of bytes to read
+ /// \return true on success, false on failure.
+ bool ReadAlignedBytesSafe( char *inOutByteArray, int &inputLength, const int maxBytesToRead );
+ bool ReadAlignedBytesSafe( char *inOutByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead );
+
+ /// \brief Same as ReadAlignedBytesSafe() but allocates the memory for you using new, rather than assuming it is safe to write to
+ /// \param[in] outByteArray outByteArray will be deleted if it is not a pointer to 0
+ /// \return true on success, false on failure.
+ bool ReadAlignedBytesSafeAlloc( char **outByteArray, int &inputLength, const unsigned int maxBytesToRead );
+ bool ReadAlignedBytesSafeAlloc( char **outByteArray, unsigned int &inputLength, const unsigned int maxBytesToRead );
+
+ /// \brief Align the next write and/or read to a byte boundary.
+ /// \details This can be used to 'waste' bits to byte align for efficiency reasons It
+ /// can also be used to force coalesced bitstreams to start on byte
+ /// boundaries so so WriteAlignedBits and ReadAlignedBits both
+ /// calculate the same offset when aligning.
+ inline void AlignWriteToByteBoundary( void ) {numberOfBitsUsed += 8 - ( (( numberOfBitsUsed - 1 ) & 7) + 1 );}
+
+ /// \brief Align the next write and/or read to a byte boundary.
+ /// \details This can be used to 'waste' bits to byte align for efficiency reasons It
+ /// can also be used to force coalesced bitstreams to start on byte
+ /// boundaries so so WriteAlignedBits and ReadAlignedBits both
+ /// calculate the same offset when aligning.
+ inline void AlignReadToByteBoundary( void ) {readOffset += 8 - ( (( readOffset - 1 ) & 7 ) + 1 );}
+
+ /// \brief Read \a numberOfBitsToRead bits to the output source.
+ /// \details alignBitsToRight should be set to true to convert internal
+ /// bitstream data to userdata. It should be false if you used
+ /// WriteBits with rightAlignedBits false
+ /// \param[in] inOutByteArray The resulting bits array
+ /// \param[in] numberOfBitsToRead The number of bits to read
+ /// \param[in] alignBitsToRight if true bits will be right aligned.
+ /// \return true if there is enough bits to read
+ bool ReadBits( unsigned char *inOutByteArray, BitSize_t numberOfBitsToRead, const bool alignBitsToRight = true );
+
+ /// \brief Write a 0
+ void Write0( void );
+
+ /// \brief Write a 1
+ void Write1( void );
+
+ /// \brief Reads 1 bit and returns true if that bit is 1 and false if it is 0.
+ bool ReadBit( void );
+
+ /// \brief If we used the constructor version with copy data off, this
+ /// *makes sure it is set to on and the data pointed to is copied.
+ void AssertCopyData( void );
+
+ /// \brief Use this if you pass a pointer copy to the constructor
+ /// *(_copyData==false) and want to overallocate to prevent
+ /// reallocation.
+ void SetNumberOfBitsAllocated( const BitSize_t lengthInBits );
+
+ /// \brief Reallocates (if necessary) in preparation of writing numberOfBitsToWrite
+ void AddBitsAndReallocate( const BitSize_t numberOfBitsToWrite );
+
+ /// \internal
+ /// \return How many bits have been allocated internally
+ BitSize_t GetNumberOfBitsAllocated(void) const;
+
+ /// Write zeros until the bitstream is filled up to \a bytes
+ void PadWithZeroToByteLength( unsigned int bytes );
+
+ /// Get the number of leading zeros for a number
+ /// \param[in] x Number to test
+ static int NumberOfLeadingZeroes( uint8_t x );
+ static int NumberOfLeadingZeroes( uint16_t x );
+ static int NumberOfLeadingZeroes( uint32_t x );
+ static int NumberOfLeadingZeroes( uint64_t x );
+ static int NumberOfLeadingZeroes( int8_t x );
+ static int NumberOfLeadingZeroes( int16_t x );
+ static int NumberOfLeadingZeroes( int32_t x );
+ static int NumberOfLeadingZeroes( int64_t x );
+
+ /// \internal Unrolled inner loop, for when performance is critical
+ void WriteAlignedVar8(const char *inByteArray);
+ /// \internal Unrolled inner loop, for when performance is critical
+ bool ReadAlignedVar8(char *inOutByteArray);
+ /// \internal Unrolled inner loop, for when performance is critical
+ void WriteAlignedVar16(const char *inByteArray);
+ /// \internal Unrolled inner loop, for when performance is critical
+ bool ReadAlignedVar16(char *inOutByteArray);
+ /// \internal Unrolled inner loop, for when performance is critical
+ void WriteAlignedVar32(const char *inByteArray);
+ /// \internal Unrolled inner loop, for when performance is critical
+ bool ReadAlignedVar32(char *inOutByteArray);
+
+ inline void Write(const char * const inStringVar)
+ {
+ uint16_t l = (uint16_t) OVR_strlen(inStringVar);
+ Write(l);
+ WriteAlignedBytes((const unsigned char*) inStringVar, (const unsigned int) l);
+ }
+ inline void Write(const unsigned char * const inTemplateVar)
+ {
+ Write((const char*)inTemplateVar);
+ }
+ inline void Write(char * const inTemplateVar)
+ {
+ Write((const char*)inTemplateVar);
+ }
+ inline void Write(unsigned char * const inTemplateVar)
+ {
+ Write((const char*)inTemplateVar);
+ }
+
+ /// ---- Member function template specialization declarations ----
+ // Used for VC7
+#if defined(OVR_CC_MSVC) && _MSC_VER == 1300
+ /// Write a bool to a bitstream.
+ /// \param[in] var The value to write
+ template <>
+ void Write(const bool &var);
+
+ /// Write a RakNetGUID to a bitsteam
+ /// \param[in] var The value to write
+ template <>
+ void Write(const RakNetGuid &var);
+
+ /// Write a string to a bitstream
+ /// \param[in] var The value to write
+ template <>
+ void Write(const char* const &var);
+ template <>
+ void Write(const unsigned char* const &var);
+ template <>
+ void Write(char* const &var);
+ template <>
+ void Write(unsigned char* const &var);
+ template <>
+ void Write(const OVR::String &var);
+
+ /// \brief Write a bool delta.
+ /// \details Same thing as just calling Write
+ /// \param[in] currentValue The current value to write
+ /// \param[in] lastValue The last value to compare against
+ template <>
+ void WriteDelta(const bool &currentValue, const bool &lastValue);
+
+ template <>
+ void WriteCompressed(const bool &var);
+
+ /// For values between -1 and 1
+ template <>
+ void WriteCompressed(const float &var);
+
+ /// For values between -1 and 1
+ template <>
+ void WriteCompressed(const double &var);
+
+ /// \brief Write a bool delta.
+ /// \details Same thing as just calling Write
+ /// \param[in] currentValue The current value to write
+ /// \param[in] lastValue The last value to compare against
+ template <>
+ void WriteCompressedDelta(const bool &currentValue, const bool &lastValue);
+
+ /// \brief Save as WriteCompressedDelta(bool currentValue, const templateType &lastValue)
+ /// when we have an unknown second bool
+ template <>
+ void WriteCompressedDelta(const bool &currentValue);
+
+ /// \brief Read a bool from a bitstream.
+ /// \param[in] var The value to read
+ /// \return true on success, false on failure.
+ template <>
+ bool Read(bool &var);
+
+ /// \brief Read a String from a bitstream.
+ /// \param[in] var The value to read
+ /// \return true on success, false on failure.
+ template <>
+ bool Read(char *&var);
+ template <>
+ bool Read(wchar_t *&var);
+ template <>
+ bool Read(unsigned char *&var);
+
+ /// \brief Read a bool from a bitstream.
+ /// \param[in] var The value to read
+ /// \return true on success, false on failure.
+ template <>
+ bool ReadDelta(bool &var);
+
+ template <>
+ bool ReadCompressed(bool &var);
+
+ template <>
+ bool ReadCompressed(float &var);
+
+ /// For values between -1 and 1
+ /// \return true on success, false on failure.
+ template <>
+ bool ReadCompressed(double &var);
+
+ template <>
+ bool ReadCompressed(char* &var);
+ template <>
+ bool ReadCompressed(wchar_t* &var);
+ template <>
+ bool ReadCompressed(unsigned char *&var);
+ template <>
+ bool ReadCompressed(OVR::String &var);
+
+ /// \brief Read a bool from a bitstream.
+ /// \param[in] var The value to read
+ /// \return true on success, false on failure.
+ template <>
+ bool ReadCompressedDelta(bool &var);
+#endif
+
+ inline static bool DoEndianSwap(void) {
+#ifndef __BITSTREAM_NATIVE_END
+ return IsNetworkOrder()==false;
+#else
+ return false;
+#endif
+ }
+ inline static bool IsBigEndian(void)
+ {
+ return IsNetworkOrder();
+ }
+ inline static bool IsNetworkOrder(void) {bool r = IsNetworkOrderInternal(); return r;}
+ // Not inline, won't compile on PC due to winsock include errors
+ static bool IsNetworkOrderInternal(void);
+ static void ReverseBytes(unsigned char *inByteArray, unsigned char *inOutByteArray, const unsigned int length);
+ static void ReverseBytesInPlace(unsigned char *inOutData,const unsigned int length);
+
+private:
+
+ BitStream( const BitStream & /*invalid*/) : numberOfBitsUsed(0), numberOfBitsAllocated(0), readOffset(0),data(NULL), copyData(false) {
+ OVR_ASSERT(0);
+ }
+
+ BitStream& operator = ( const BitStream& /*invalid*/ ) {
+ OVR_ASSERT(0);
+ static BitStream i;
+ return i;
+ }
+
+ /// \brief Assume the input source points to a native type, compress and write it.
+ void WriteCompressed( const unsigned char* inByteArray, const unsigned int size, const bool unsignedData );
+
+ /// \brief Assume the input source points to a compressed native type. Decompress and read it.
+ bool ReadCompressed( unsigned char* inOutByteArray, const unsigned int size, const bool unsignedData );
+
+
+ BitSize_t numberOfBitsUsed;
+
+ BitSize_t numberOfBitsAllocated;
+
+ BitSize_t readOffset;
+
+ unsigned char *data;
+
+ /// true if the internal buffer is copy of the data passed to the constructor
+ bool copyData;
+
+ /// BitStreams that use less than BITSTREAM_STACK_ALLOCATION_SIZE use the stack, rather than the heap to store data. It switches over if BITSTREAM_STACK_ALLOCATION_SIZE is exceeded
+ unsigned char stackData[BITSTREAM_STACK_ALLOCATION_SIZE];
+};
+
+template <class templateType>
+inline bool BitStream::Serialize(bool writeToBitstream, templateType &inOutTemplateVar)
+{
+ if (writeToBitstream)
+ Write(inOutTemplateVar);
+ else
+ return Read(inOutTemplateVar);
+ return true;
+}
+
+template <class templateType>
+inline bool BitStream::SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue)
+{
+ if (writeToBitstream)
+ WriteDelta(inOutCurrentValue, lastValue);
+ else
+ return ReadDelta(inOutCurrentValue);
+ return true;
+}
+
+template <class templateType>
+inline bool BitStream::SerializeDelta(bool writeToBitstream, templateType &inOutCurrentValue)
+{
+ if (writeToBitstream)
+ WriteDelta(inOutCurrentValue);
+ else
+ return ReadDelta(inOutCurrentValue);
+ return true;
+}
+
+template <class templateType>
+inline bool BitStream::SerializeCompressed(bool writeToBitstream, templateType &inOutTemplateVar)
+{
+ if (writeToBitstream)
+ WriteCompressed(inOutTemplateVar);
+ else
+ return ReadCompressed(inOutTemplateVar);
+ return true;
+}
+
+template <class templateType>
+inline bool BitStream::SerializeCompressedDelta(bool writeToBitstream, templateType &inOutCurrentValue, const templateType &lastValue)
+{
+ if (writeToBitstream)
+ WriteCompressedDelta(inOutCurrentValue,lastValue);
+ else
+ return ReadCompressedDelta(inOutCurrentValue);
+ return true;
+}
+//Stoppedhere
+template <class templateType>
+inline bool BitStream::SerializeCompressedDelta(bool writeToBitstream, templateType &inOutCurrentValue)
+{
+ if (writeToBitstream)
+ WriteCompressedDelta(inOutCurrentValue);
+ else
+ return ReadCompressedDelta(inOutCurrentValue);
+ return true;
+}
+
+inline bool BitStream::Serialize(bool writeToBitstream, char* inOutByteArray, const unsigned int numberOfBytes )
+{
+ if (writeToBitstream)
+ Write(inOutByteArray, numberOfBytes);
+ else
+ return Read(inOutByteArray, numberOfBytes);
+ return true;
+}
+
+template <class serializationType, class sourceType >
+bool BitStream::SerializeCasted( bool writeToBitstream, sourceType &value )
+{
+ if (writeToBitstream) WriteCasted<serializationType>(value);
+ else return ReadCasted<serializationType>(value);
+ return true;
+}
+
+template <class templateType>
+bool BitStream::SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange )
+{
+ int requiredBits=BYTES_TO_BITS(sizeof(templateType))-NumberOfLeadingZeroes(templateType(maximum-minimum));
+ return SerializeBitsFromIntegerRange(writeToBitstream,value,minimum,maximum,requiredBits,allowOutsideRange);
+}
+template <class templateType>
+bool BitStream::SerializeBitsFromIntegerRange( bool writeToBitstream, templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange )
+{
+ if (writeToBitstream) WriteBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange);
+ else return ReadBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange);
+ return true;
+}
+
+template <class templateType>
+inline bool BitStream::SerializeNormVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z )
+{
+ if (writeToBitstream)
+ WriteNormVector(x,y,z);
+ else
+ return ReadNormVector(x,y,z);
+ return true;
+}
+
+template <class templateType>
+inline bool BitStream::SerializeVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z )
+{
+ if (writeToBitstream)
+ WriteVector(x,y,z);
+ else
+ return ReadVector(x,y,z);
+ return true;
+}
+
+template <class templateType>
+inline bool BitStream::SerializeNormQuat(bool writeToBitstream, templateType &w, templateType &x, templateType &y, templateType &z)
+{
+ if (writeToBitstream)
+ WriteNormQuat(w,x,y,z);
+ else
+ return ReadNormQuat(w,x,y,z);
+ return true;
+}
+
+template <class templateType>
+inline bool BitStream::SerializeOrthMatrix(
+ bool writeToBitstream,
+ templateType &m00, templateType &m01, templateType &m02,
+ templateType &m10, templateType &m11, templateType &m12,
+ templateType &m20, templateType &m21, templateType &m22 )
+{
+ if (writeToBitstream)
+ WriteOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22);
+ else
+ return ReadOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22);
+ return true;
+}
+
+inline bool BitStream::SerializeBits(bool writeToBitstream, unsigned char* inOutByteArray, const BitSize_t numberOfBitsToSerialize, const bool rightAlignedBits )
+{
+ if (writeToBitstream)
+ WriteBits(inOutByteArray,numberOfBitsToSerialize,rightAlignedBits);
+ else
+ return ReadBits(inOutByteArray,numberOfBitsToSerialize,rightAlignedBits);
+ return true;
+}
+
+template <class templateType>
+inline void BitStream::Write(const templateType &inTemplateVar)
+{
+#ifdef OVR_CC_MSVC
+#pragma warning(disable:4127) // conditional expression is constant
+#endif
+ if (sizeof(inTemplateVar)==1)
+ WriteBits( ( unsigned char* ) & inTemplateVar, sizeof( templateType ) * 8, true );
+ else
+ {
+#ifndef __BITSTREAM_NATIVE_END
+ if (DoEndianSwap())
+ {
+ unsigned char output[sizeof(templateType)];
+ ReverseBytes((unsigned char*)&inTemplateVar, output, sizeof(templateType));
+ WriteBits( ( unsigned char* ) output, sizeof(templateType) * 8, true );
+ }
+ else
+#endif
+ WriteBits( ( unsigned char* ) & inTemplateVar, sizeof(templateType) * 8, true );
+ }
+}
+
+template <class templateType>
+inline void BitStream::WritePtr(templateType *inTemplateVar)
+{
+#ifdef OVR_CC_MSVC
+#pragma warning(disable:4127) // conditional expression is constant
+#endif
+ if (sizeof(templateType)==1)
+ WriteBits( ( unsigned char* ) inTemplateVar, sizeof( templateType ) * 8, true );
+ else
+ {
+#ifndef __BITSTREAM_NATIVE_END
+ if (DoEndianSwap())
+ {
+ unsigned char output[sizeof(templateType)];
+ ReverseBytes((unsigned char*) inTemplateVar, output, sizeof(templateType));
+ WriteBits( ( unsigned char* ) output, sizeof(templateType) * 8, true );
+ }
+ else
+#endif
+ WriteBits( ( unsigned char* ) inTemplateVar, sizeof(templateType) * 8, true );
+ }
+}
+
+/// \brief Write a bool to a bitstream.
+/// \param[in] inTemplateVar The value to write
+template <>
+inline void BitStream::Write(const bool &inTemplateVar)
+{
+ if ( inTemplateVar )
+ Write1();
+ else
+ Write0();
+}
+
+
+/// \brief Write a string to a bitstream.
+/// \param[in] var The value to write
+template <>
+inline void BitStream::Write(const OVR::String &inTemplateVar)
+{
+ uint16_t l = (uint16_t) inTemplateVar.GetLength();
+ Write(l);
+ WriteAlignedBytes((const unsigned char*) inTemplateVar.ToCStr(), (const unsigned int) l);
+}
+template <>
+inline void BitStream::Write(const char * const &inStringVar)
+{
+ uint16_t l = (uint16_t) strlen(inStringVar);
+ Write(l);
+ WriteAlignedBytes((const unsigned char*) inStringVar, (const unsigned int) l);
+}
+template <>
+inline void BitStream::Write(const unsigned char * const &inTemplateVar)
+{
+ Write((const char*)inTemplateVar);
+}
+template <>
+inline void BitStream::Write(char * const &inTemplateVar)
+{
+ Write((const char*)inTemplateVar);
+}
+template <>
+inline void BitStream::Write(unsigned char * const &inTemplateVar)
+{
+ Write((const char*)inTemplateVar);
+}
+
+/// \brief Write any integral type to a bitstream.
+/// \details If the current value is different from the last value
+/// the current value will be written. Otherwise, a single bit will be written
+/// \param[in] currentValue The current value to write
+/// \param[in] lastValue The last value to compare against
+template <class templateType>
+inline void BitStream::WriteDelta(const templateType &currentValue, const templateType &lastValue)
+{
+ if (currentValue==lastValue)
+ {
+ Write(false);
+ }
+ else
+ {
+ Write(true);
+ Write(currentValue);
+ }
+}
+
+/// \brief Write a bool delta. Same thing as just calling Write
+/// \param[in] currentValue The current value to write
+/// \param[in] lastValue The last value to compare against
+template <>
+inline void BitStream::WriteDelta(const bool &currentValue, const bool &lastValue)
+{
+ (void) lastValue;
+
+ Write(currentValue);
+}
+
+/// \brief WriteDelta when you don't know what the last value is, or there is no last value.
+/// \param[in] currentValue The current value to write
+template <class templateType>
+inline void BitStream::WriteDelta(const templateType &currentValue)
+{
+ Write(true);
+ Write(currentValue);
+}
+
+/// \brief Write any integral type to a bitstream.
+/// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping.
+/// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1.
+/// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type
+/// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte
+/// \param[in] inTemplateVar The value to write
+template <class templateType>
+inline void BitStream::WriteCompressed(const templateType &inTemplateVar)
+{
+#ifdef OVR_CC_MSVC
+#pragma warning(disable:4127) // conditional expression is constant
+#endif
+ if (sizeof(inTemplateVar)==1)
+ WriteCompressed( ( unsigned char* ) & inTemplateVar, sizeof( templateType ) * 8, true );
+ else
+ {
+#ifndef __BITSTREAM_NATIVE_END
+#ifdef OVR_CC_MSVC
+#pragma warning(disable:4244) // '=' : conversion from 'unsigned long' to 'uint16_t', possible loss of data
+#endif
+
+ if (DoEndianSwap())
+ {
+ unsigned char output[sizeof(templateType)];
+ ReverseBytes((unsigned char*)&inTemplateVar, output, sizeof(templateType));
+ WriteCompressed( ( unsigned char* ) output, sizeof(templateType) * 8, true );
+ }
+ else
+#endif
+ WriteCompressed( ( unsigned char* ) & inTemplateVar, sizeof(templateType) * 8, true );
+ }
+}
+
+template <>
+inline void BitStream::WriteCompressed(const bool &inTemplateVar)
+{
+ Write(inTemplateVar);
+}
+
+/// For values between -1 and 1
+template <>
+inline void BitStream::WriteCompressed(const float &inTemplateVar)
+{
+ OVR_ASSERT(inTemplateVar > -1.01f && inTemplateVar < 1.01f);
+ float varCopy=inTemplateVar;
+ if (varCopy < -1.0f)
+ varCopy=-1.0f;
+ if (varCopy > 1.0f)
+ varCopy=1.0f;
+ Write((uint16_t)((varCopy+1.0f)*32767.5f));
+}
+
+/// For values between -1 and 1
+template <>
+inline void BitStream::WriteCompressed(const double &inTemplateVar)
+{
+ OVR_ASSERT(inTemplateVar > -1.01 && inTemplateVar < 1.01);
+ double varCopy=inTemplateVar;
+ if (varCopy < -1.0f)
+ varCopy=-1.0f;
+ if (varCopy > 1.0f)
+ varCopy=1.0f;
+ Write((uint32_t)((varCopy+1.0)*2147483648.0));
+}
+
+/// \brief Write any integral type to a bitstream.
+/// \details If the current value is different from the last value
+/// the current value will be written. Otherwise, a single bit will be written
+/// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1.
+/// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type
+/// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte
+/// \param[in] currentValue The current value to write
+/// \param[in] lastValue The last value to compare against
+template <class templateType>
+inline void BitStream::WriteCompressedDelta(const templateType &currentValue, const templateType &lastValue)
+{
+ if (currentValue==lastValue)
+ {
+ Write(false);
+ }
+ else
+ {
+ Write(true);
+ WriteCompressed(currentValue);
+ }
+}
+
+/// \brief Write a bool delta. Same thing as just calling Write
+/// \param[in] currentValue The current value to write
+/// \param[in] lastValue The last value to compare against
+template <>
+inline void BitStream::WriteCompressedDelta(const bool &currentValue, const bool &lastValue)
+{
+ (void) lastValue;
+
+ Write(currentValue);
+}
+
+/// \brief Save as WriteCompressedDelta(const templateType &currentValue, const templateType &lastValue)
+/// when we have an unknown second parameter
+template <class templateType>
+inline void BitStream::WriteCompressedDelta(const templateType &currentValue)
+{
+ Write(true);
+ WriteCompressed(currentValue);
+}
+
+/// \brief Save as WriteCompressedDelta(bool currentValue, const templateType &lastValue)
+/// when we have an unknown second bool
+template <>
+inline void BitStream::WriteCompressedDelta(const bool &currentValue)
+{
+ Write(currentValue);
+}
+
+/// \brief Read any integral type from a bitstream. Define __BITSTREAM_NATIVE_END if you need endian swapping.
+/// \param[in] outTemplateVar The value to read
+template <class templateType>
+inline bool BitStream::Read(templateType &outTemplateVar)
+{
+#ifdef OVR_CC_MSVC
+#pragma warning(disable:4127) // conditional expression is constant
+#endif
+ if (sizeof(outTemplateVar)==1)
+ return ReadBits( ( unsigned char* ) &outTemplateVar, sizeof(templateType) * 8, true );
+ else
+ {
+#ifndef __BITSTREAM_NATIVE_END
+#ifdef OVR_CC_MSVC
+#pragma warning(disable:4244) // '=' : conversion from 'unsigned long' to 'uint16_t', possible loss of data
+#endif
+ if (DoEndianSwap())
+ {
+ unsigned char output[sizeof(templateType)];
+ if (ReadBits( ( unsigned char* ) output, sizeof(templateType) * 8, true ))
+ {
+ ReverseBytes(output, (unsigned char*)&outTemplateVar, sizeof(templateType));
+ return true;
+ }
+ return false;
+ }
+ else
+#endif
+ return ReadBits( ( unsigned char* ) & outTemplateVar, sizeof(templateType) * 8, true );
+ }
+}
+
+/// \brief Read a bool from a bitstream.
+/// \param[in] outTemplateVar The value to read
+template <>
+inline bool BitStream::Read(bool &outTemplateVar)
+{
+ if ( readOffset + 1 > numberOfBitsUsed )
+ return false;
+
+ if ( data[ readOffset >> 3 ] & ( 0x80 >> ( readOffset & 7 ) ) ) // Is it faster to just write it out here?
+ outTemplateVar = true;
+ else
+ outTemplateVar = false;
+
+ // Has to be on a different line for Mac
+ readOffset++;
+
+ return true;
+}
+
+template <>
+inline bool BitStream::Read(OVR::String &outTemplateVar)
+{
+ bool b;
+ uint16_t l;
+ b=Read(l);
+ if (b && l>0)
+ {
+ AlignReadToByteBoundary();
+ outTemplateVar.AssignString((const char*) (data + ( readOffset >> 3 )), (size_t) l);
+ IgnoreBytes(l);
+ }
+ else
+ {
+ AlignReadToByteBoundary();
+ }
+ return b;
+}
+template <>
+inline bool BitStream::Read(char *&varString)
+{
+ bool b;
+ uint16_t l;
+ b=Read(l);
+ if (b && l>0)
+ {
+ memcpy(varString, data + ( readOffset >> 3 ), l);
+ IgnoreBytes(l);
+ }
+ else
+ {
+ AlignReadToByteBoundary();
+ }
+ return b;
+}
+template <>
+inline bool BitStream::Read(unsigned char *&varString)
+{
+ bool b;
+ uint16_t l;
+ b=Read(l);
+ if (b && l>0)
+ {
+ memcpy(varString, data + ( readOffset >> 3 ), l);
+ IgnoreBytes(l);
+ }
+ else
+ {
+ AlignReadToByteBoundary();
+ }
+ return b;
+}
+
+/// \brief Read any integral type from a bitstream.
+/// \details If the written value differed from the value compared against in the write function,
+/// var will be updated. Otherwise it will retain the current value.
+/// ReadDelta is only valid from a previous call to WriteDelta
+/// \param[in] outTemplateVar The value to read
+template <class templateType>
+inline bool BitStream::ReadDelta(templateType &outTemplateVar)
+{
+ bool dataWritten;
+ bool success;
+ success=Read(dataWritten);
+ if (dataWritten)
+ success=Read(outTemplateVar);
+ return success;
+}
+
+/// \brief Read a bool from a bitstream.
+/// \param[in] outTemplateVar The value to read
+template <>
+inline bool BitStream::ReadDelta(bool &outTemplateVar)
+{
+ return Read(outTemplateVar);
+}
+
+/// \brief Read any integral type from a bitstream.
+/// \details Undefine __BITSTREAM_NATIVE_END if you need endian swapping.
+/// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1.
+/// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type
+/// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte
+/// \param[in] outTemplateVar The value to read
+template <class templateType>
+inline bool BitStream::ReadCompressed(templateType &outTemplateVar)
+{
+#ifdef OVR_CC_MSVC
+#pragma warning(disable:4127) // conditional expression is constant
+#endif
+ if (sizeof(outTemplateVar)==1)
+ return ReadCompressed( ( unsigned char* ) &outTemplateVar, sizeof(templateType) * 8, true );
+ else
+ {
+#ifndef __BITSTREAM_NATIVE_END
+ if (DoEndianSwap())
+ {
+ unsigned char output[sizeof(templateType)];
+ if (ReadCompressed( ( unsigned char* ) output, sizeof(templateType) * 8, true ))
+ {
+ ReverseBytes(output, (unsigned char*)&outTemplateVar, sizeof(templateType));
+ return true;
+ }
+ return false;
+ }
+ else
+#endif
+ return ReadCompressed( ( unsigned char* ) & outTemplateVar, sizeof(templateType) * 8, true );
+ }
+}
+
+template <>
+inline bool BitStream::ReadCompressed(bool &outTemplateVar)
+{
+ return Read(outTemplateVar);
+}
+
+/// For values between -1 and 1
+template <>
+inline bool BitStream::ReadCompressed(float &outTemplateVar)
+{
+ uint16_t compressedFloat;
+ if (Read(compressedFloat))
+ {
+ outTemplateVar = ((float)compressedFloat / 32767.5f - 1.0f);
+ return true;
+ }
+ return false;
+}
+
+/// For values between -1 and 1
+template <>
+inline bool BitStream::ReadCompressed(double &outTemplateVar)
+{
+ uint32_t compressedFloat;
+ if (Read(compressedFloat))
+ {
+ outTemplateVar = ((double)compressedFloat / 2147483648.0 - 1.0);
+ return true;
+ }
+ return false;
+}
+
+/// \brief Read any integral type from a bitstream.
+/// \details If the written value differed from the value compared against in the write function,
+/// var will be updated. Otherwise it will retain the current value.
+/// the current value will be updated.
+/// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1.
+/// For non-floating point, this is lossless, but only has benefit if you use less than half the bits of the type
+/// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte
+/// ReadCompressedDelta is only valid from a previous call to WriteDelta
+/// \param[in] outTemplateVar The value to read
+template <class templateType>
+inline bool BitStream::ReadCompressedDelta(templateType &outTemplateVar)
+{
+ bool dataWritten;
+ bool success;
+ success=Read(dataWritten);
+ if (dataWritten)
+ success=ReadCompressed(outTemplateVar);
+ return success;
+}
+
+/// \brief Read a bool from a bitstream.
+/// \param[in] outTemplateVar The value to read
+template <>
+inline bool BitStream::ReadCompressedDelta(bool &outTemplateVar)
+{
+ return Read(outTemplateVar);
+}
+
+template <class destinationType, class sourceType >
+void BitStream::WriteCasted( const sourceType &value )
+{
+ destinationType val = (destinationType) value;
+ Write(val);
+}
+
+template <class templateType>
+void BitStream::WriteBitsFromIntegerRange( const templateType value, const templateType minimum,const templateType maximum, bool allowOutsideRange )
+{
+ int requiredBits=BYTES_TO_BITS(sizeof(templateType))-NumberOfLeadingZeroes(templateType(maximum-minimum));
+ WriteBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange);
+}
+template <class templateType>
+void BitStream::WriteBitsFromIntegerRange( const templateType value, const templateType minimum,const templateType maximum, const int requiredBits, bool allowOutsideRange )
+{
+ OVR_ASSERT(maximum>=minimum);
+ OVR_ASSERT(allowOutsideRange==true || (value>=minimum && value<=maximum));
+ if (allowOutsideRange)
+ {
+ if (value<minimum || value>maximum)
+ {
+ Write(true);
+ Write(value);
+ return;
+ }
+ Write(false);
+ }
+ templateType valueOffMin=value-minimum;
+ if (IsBigEndian()==true)
+ {
+ unsigned char output[sizeof(templateType)];
+ ReverseBytes((unsigned char*)&valueOffMin, output, sizeof(templateType));
+ WriteBits(output,requiredBits);
+ }
+ else
+ {
+ WriteBits((unsigned char*) &valueOffMin,requiredBits);
+ }
+}
+
+template <class templateType> // templateType for this function must be a float or double
+void BitStream::WriteNormVector( templateType x, templateType y, templateType z )
+{
+#ifdef _DEBUG
+ OVR_ASSERT(x <= 1.01 && y <= 1.01 && z <= 1.01 && x >= -1.01 && y >= -1.01 && z >= -1.01);
+#endif
+
+ WriteFloat16((float)x,-1.0f,1.0f);
+ WriteFloat16((float)y,-1.0f,1.0f);
+ WriteFloat16((float)z,-1.0f,1.0f);
+}
+
+template <class templateType> // templateType for this function must be a float or double
+void BitStream::WriteVector( templateType x, templateType y, templateType z )
+{
+ templateType magnitude = sqrt(x * x + y * y + z * z);
+ Write((float)magnitude);
+ if (magnitude > 0.00001f)
+ {
+ WriteCompressed((float)(x/magnitude));
+ WriteCompressed((float)(y/magnitude));
+ WriteCompressed((float)(z/magnitude));
+ // Write((uint16_t)((x/magnitude+1.0f)*32767.5f));
+ // Write((uint16_t)((y/magnitude+1.0f)*32767.5f));
+ // Write((uint16_t)((z/magnitude+1.0f)*32767.5f));
+ }
+}
+
+template <class templateType> // templateType for this function must be a float or double
+void BitStream::WriteNormQuat( templateType w, templateType x, templateType y, templateType z)
+{
+ Write((bool)(w<0.0));
+ Write((bool)(x<0.0));
+ Write((bool)(y<0.0));
+ Write((bool)(z<0.0));
+ Write((uint16_t)(fabs(x)*65535.0));
+ Write((uint16_t)(fabs(y)*65535.0));
+ Write((uint16_t)(fabs(z)*65535.0));
+ // Leave out w and calculate it on the target
+}
+
+template <class templateType> // templateType for this function must be a float or double
+void BitStream::WriteOrthMatrix(
+ templateType m00, templateType m01, templateType m02,
+ templateType m10, templateType m11, templateType m12,
+ templateType m20, templateType m21, templateType m22 )
+{
+
+ double qw;
+ double qx;
+ double qy;
+ double qz;
+
+ // Convert matrix to quat
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
+ float sum;
+ sum = 1 + m00 + m11 + m22;
+ if (sum < 0.0f) sum=0.0f;
+ qw = sqrt( sum ) / 2;
+ sum = 1 + m00 - m11 - m22;
+ if (sum < 0.0f) sum=0.0f;
+ qx = sqrt( sum ) / 2;
+ sum = 1 - m00 + m11 - m22;
+ if (sum < 0.0f) sum=0.0f;
+ qy = sqrt( sum ) / 2;
+ sum = 1 - m00 - m11 + m22;
+ if (sum < 0.0f) sum=0.0f;
+ qz = sqrt( sum ) / 2;
+ if (qw < 0.0) qw=0.0;
+ if (qx < 0.0) qx=0.0;
+ if (qy < 0.0) qy=0.0;
+ if (qz < 0.0) qz=0.0;
+#ifdef OVR_OS_WIN32
+ qx = _copysign( (double) qx, (double) (m21 - m12) );
+ qy = _copysign( (double) qy, (double) (m02 - m20) );
+ qz = _copysign( (double) qz, (double) (m10 - m01) );
+#else
+ qx = copysign( (double) qx, (double) (m21 - m12) );
+ qy = copysign( (double) qy, (double) (m02 - m20) );
+ qz = copysign( (double) qz, (double) (m10 - m01) );
+#endif
+
+ WriteNormQuat(qw,qx,qy,qz);
+}
+
+template <class serializationType, class sourceType >
+bool BitStream::ReadCasted( sourceType &value )
+{
+ serializationType val;
+ bool success = Read(val);
+ value=(sourceType) val;
+ return success;
+}
+
+template <class templateType>
+bool BitStream::ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, bool allowOutsideRange )
+{
+ int requiredBits=BYTES_TO_BITS(sizeof(templateType))-NumberOfLeadingZeroes(templateType(maximum-minimum));
+ return ReadBitsFromIntegerRange(value,minimum,maximum,requiredBits,allowOutsideRange);
+}
+template <class templateType>
+bool BitStream::ReadBitsFromIntegerRange( templateType &value, const templateType minimum, const templateType maximum, const int requiredBits, bool allowOutsideRange )
+{
+ OVR_ASSERT_AND_UNUSED(maximum>=minimum, maximum);
+ if (allowOutsideRange)
+ {
+ bool isOutsideRange;
+ Read(isOutsideRange);
+ if (isOutsideRange)
+ return Read(value);
+ }
+ unsigned char output[sizeof(templateType)];
+ memset(output,0,sizeof(output));
+ bool success = ReadBits(output,requiredBits);
+ if (success)
+ {
+ if (IsBigEndian()==true)
+ ReverseBytesInPlace(output,sizeof(output));
+ memcpy(&value,output,sizeof(output));
+
+ value+=minimum;
+ }
+
+ return success;
+}
+
+template <class templateType> // templateType for this function must be a float or double
+bool BitStream::ReadNormVector( templateType &x, templateType &y, templateType &z )
+{
+ float xIn,yIn,zIn;
+ ReadFloat16(xIn,-1.0f,1.0f);
+ ReadFloat16(yIn,-1.0f,1.0f);
+ ReadFloat16(zIn,-1.0f,1.0f);
+ x=xIn;
+ y=yIn;
+ z=zIn;
+ return true;
+}
+
+template <class templateType> // templateType for this function must be a float or double
+bool BitStream::ReadVector( templateType &x, templateType &y, templateType &z )
+{
+ float magnitude;
+ //uint16_t sx,sy,sz;
+ if (!Read(magnitude))
+ return false;
+ if (magnitude>0.00001f)
+ {
+ // Read(sx);
+ // Read(sy);
+ // if (!Read(sz))
+ // return false;
+ // x=((float)sx / 32767.5f - 1.0f) * magnitude;
+ // y=((float)sy / 32767.5f - 1.0f) * magnitude;
+ // z=((float)sz / 32767.5f - 1.0f) * magnitude;
+ float cx=0.0f,cy=0.0f,cz=0.0f;
+ ReadCompressed(cx);
+ ReadCompressed(cy);
+ if (!ReadCompressed(cz))
+ return false;
+ x=cx;
+ y=cy;
+ z=cz;
+ x*=magnitude;
+ y*=magnitude;
+ z*=magnitude;
+ }
+ else
+ {
+ x=0.0;
+ y=0.0;
+ z=0.0;
+ }
+ return true;
+}
+
+template <class templateType> // templateType for this function must be a float or double
+bool BitStream::ReadNormQuat( templateType &w, templateType &x, templateType &y, templateType &z)
+{
+ bool cwNeg=false, cxNeg=false, cyNeg=false, czNeg=false;
+ uint16_t cx,cy,cz;
+ Read(cwNeg);
+ Read(cxNeg);
+ Read(cyNeg);
+ Read(czNeg);
+ Read(cx);
+ Read(cy);
+ if (!Read(cz))
+ return false;
+
+ // Calculate w from x,y,z
+ x=(templateType)(cx/65535.0);
+ y=(templateType)(cy/65535.0);
+ z=(templateType)(cz/65535.0);
+ if (cxNeg) x=-x;
+ if (cyNeg) y=-y;
+ if (czNeg) z=-z;
+ float difference = 1.0f - x*x - y*y - z*z;
+ if (difference < 0.0f)
+ difference=0.0f;
+ w = (templateType)(sqrt(difference));
+ if (cwNeg)
+ w=-w;
+
+ return true;
+}
+
+template <class templateType> // templateType for this function must be a float or double
+bool BitStream::ReadOrthMatrix(
+ templateType &m00, templateType &m01, templateType &m02,
+ templateType &m10, templateType &m11, templateType &m12,
+ templateType &m20, templateType &m21, templateType &m22 )
+{
+ float qw,qx,qy,qz;
+ if (!ReadNormQuat(qw,qx,qy,qz))
+ return false;
+
+ // Quat to orthogonal rotation matrix
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/index.htm
+ double sqw = (double)qw*(double)qw;
+ double sqx = (double)qx*(double)qx;
+ double sqy = (double)qy*(double)qy;
+ double sqz = (double)qz*(double)qz;
+ m00 = (templateType)(sqx - sqy - sqz + sqw); // since sqw + sqx + sqy + sqz =1
+ m11 = (templateType)(-sqx + sqy - sqz + sqw);
+ m22 = (templateType)(-sqx - sqy + sqz + sqw);
+
+ double tmp1 = (double)qx*(double)qy;
+ double tmp2 = (double)qz*(double)qw;
+ m10 = (templateType)(2.0 * (tmp1 + tmp2));
+ m01 = (templateType)(2.0 * (tmp1 - tmp2));
+
+ tmp1 = (double)qx*(double)qz;
+ tmp2 = (double)qy*(double)qw;
+ m20 =(templateType)(2.0 * (tmp1 - tmp2));
+ m02 = (templateType)(2.0 * (tmp1 + tmp2));
+ tmp1 = (double)qy*(double)qz;
+ tmp2 = (double)qx*(double)qw;
+ m21 = (templateType)(2.0 * (tmp1 + tmp2));
+ m12 = (templateType)(2.0 * (tmp1 - tmp2));
+
+ return true;
+}
+
+template <class templateType>
+BitStream& operator<<(BitStream& out, templateType& c)
+{
+ out.Write(c);
+ return out;
+}
+template <class templateType>
+BitStream& operator>>(BitStream& in, templateType& c)
+{
+ bool success = in.Read(c);
+ (void)success;
+
+ OVR_ASSERT(success);
+ return in;
+}
+
+
+}} // OVR::Net
+
+#endif
diff --git a/LibOVR/Src/Net/OVR_MessageIDTypes.h b/LibOVR/Src/Net/OVR_MessageIDTypes.h
new file mode 100644
index 0000000..849f63a
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_MessageIDTypes.h
@@ -0,0 +1,39 @@
+/************************************************************************************
+
+Filename : OVR_MessageIDTypes.h
+Content : Enumeration list indicating what type of message is being sent
+Created : July 3, 2014
+Authors : Kevin Jenkins
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+namespace OVR { namespace Net {
+
+/// First byte of a network message
+typedef unsigned char MessageID;
+
+enum DefaultMessageIDTypes
+{
+ OVRID_RPC1,
+ OVRID_END = 128,
+ OVRID_LATENCY_TESTER_1,
+};
+
+}} // namespace OVR::Net
diff --git a/LibOVR/Src/Net/OVR_NetworkPlugin.cpp b/LibOVR/Src/Net/OVR_NetworkPlugin.cpp
new file mode 100644
index 0000000..a693499
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_NetworkPlugin.cpp
@@ -0,0 +1,70 @@
+/************************************************************************************
+
+Filename : OVR_NetworkPlugin.cpp
+Content : Base class for an extension to the network objects.
+Created : June 10, 2014
+Authors : Kevin Jenkins
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "OVR_NetworkPlugin.h"
+
+namespace OVR { namespace Net { namespace Plugins {
+
+
+//-----------------------------------------------------------------------------
+// Plugin identifier to assign next
+
+//static uint8_t pluginIdNext = 0;
+
+
+//-----------------------------------------------------------------------------
+// NetworkPlugin
+
+NetworkPlugin::NetworkPlugin()
+{
+ pSession = 0;
+ //PluginId = pluginIdNext++;
+}
+
+NetworkPlugin::~NetworkPlugin()
+{
+}
+
+void NetworkPlugin::OnAddedToSession(Session* _pSession)
+{
+ if (pSession != 0)
+ {
+ pSession->RemoveSessionListener(this);
+ }
+
+ pSession = _pSession;
+}
+
+void NetworkPlugin::OnRemovedFromSession(Session* _pSession)
+{
+ OVR_UNUSED(_pSession);
+ OVR_ASSERT(_pSession == pSession);
+
+ pSession = 0;
+}
+
+
+}}} // OVR::Net::Plugins
diff --git a/LibOVR/Src/Net/OVR_NetworkPlugin.h b/LibOVR/Src/Net/OVR_NetworkPlugin.h
new file mode 100644
index 0000000..b03b86c
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_NetworkPlugin.h
@@ -0,0 +1,60 @@
+/************************************************************************************
+
+PublicHeader: n/a
+Filename : OVR_NetworkPlugin.h
+Content : Base class for an extension to the network objects.
+Created : June 10, 2014
+Authors : Kevin Jenkins
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_NetworkPlugin_h
+#define OVR_NetworkPlugin_h
+
+#include "OVR_Session.h"
+
+namespace OVR { namespace Net { namespace Plugins {
+
+
+//-----------------------------------------------------------------------------
+// NetworkPlugin
+
+// NetworkPlugins use Session and SessionListener to provide network functionality
+// independent of the transport medium.
+// Uses the chain of command design pattern such that plugins can invoke or intercept
+// network events via the Session.
+class NetworkPlugin : public SessionListener
+{
+public:
+ NetworkPlugin();
+ virtual ~NetworkPlugin();
+
+protected:
+ virtual void OnAddedToSession(Session* _pSession);
+ virtual void OnRemovedFromSession(Session* _pSession);
+
+ Session *pSession;
+ //uint8_t PluginId;
+};
+
+
+}}} // OVR::Net::Plugins
+
+#endif
diff --git a/LibOVR/Src/Net/OVR_NetworkTypes.h b/LibOVR/Src/Net/OVR_NetworkTypes.h
new file mode 100644
index 0000000..401f53a
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_NetworkTypes.h
@@ -0,0 +1,42 @@
+/************************************************************************************
+
+PublicHeader: n/a
+Filename : OVR_NetworkTypes.h
+Content : Shared header for network types
+Created : June 12, 2014
+Authors : Kevin Jenkins
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_NetworkTypes_h
+#define OVR_NetworkTypes_h
+
+#include "../Kernel/OVR_Types.h"
+
+namespace OVR { namespace Net {
+
+
+typedef uint64_t NetworkID;
+const NetworkID InvalidNetworkID = ~((NetworkID)0);
+
+
+} } // OVR::Net
+
+#endif
diff --git a/LibOVR/Src/Net/OVR_PacketizedTCPSocket.cpp b/LibOVR/Src/Net/OVR_PacketizedTCPSocket.cpp
new file mode 100644
index 0000000..6352480
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_PacketizedTCPSocket.cpp
@@ -0,0 +1,215 @@
+/************************************************************************************
+
+Filename : OVR_PacketizedTCPSocket.cpp
+Content : TCP with automated message framing.
+Created : June 10, 2014
+Authors : Kevin Jenkins
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "OVR_PacketizedTCPSocket.h"
+
+namespace OVR { namespace Net {
+
+
+//-----------------------------------------------------------------------------
+// Constants
+
+static const int LENGTH_FIELD_BYTES = 4;
+
+
+//-----------------------------------------------------------------------------
+// PacketizedTCPSocket
+
+PacketizedTCPSocket::PacketizedTCPSocket()
+{
+ pRecvBuff = 0;
+ pRecvBuffSize = 0;
+ Transport = TransportType_PacketizedTCP;
+}
+
+PacketizedTCPSocket::PacketizedTCPSocket(SocketHandle _sock, bool isListenSocket) : PacketizedTCPSocketBase(_sock, isListenSocket)
+{
+ pRecvBuff = 0;
+ pRecvBuffSize = 0;
+ Transport = TransportType_PacketizedTCP;
+}
+
+PacketizedTCPSocket::~PacketizedTCPSocket()
+{
+ OVR_FREE(pRecvBuff);
+}
+
+int PacketizedTCPSocket::Send(const void* pData, int bytes)
+{
+ Lock::Locker locker(&sendLock);
+
+ if (bytes <= 0)
+ {
+ return -1;
+ }
+
+ // Convert length to 4 endian-neutral bytes
+ uint32_t lengthWord = bytes;
+ uint8_t lengthBytes[LENGTH_FIELD_BYTES] = {
+ (uint8_t)lengthWord,
+ (uint8_t)(lengthWord >> 8),
+ (uint8_t)(lengthWord >> 16),
+ (uint8_t)(lengthWord >> 24)
+ };
+
+ int s = PacketizedTCPSocketBase::Send(lengthBytes, LENGTH_FIELD_BYTES);
+ if (s > 0)
+ {
+ return PacketizedTCPSocketBase::Send(pData,bytes);
+ }
+ else
+ {
+ return s;
+ }
+}
+
+int PacketizedTCPSocket::SendAndConcatenate(const void** pDataArray, int* dataLengthArray, int arrayCount)
+{
+ Lock::Locker locker(&sendLock);
+
+ if (arrayCount == 0)
+ return 0;
+
+ int totalBytes = 0;
+ for (int i = 0; i < arrayCount; i++)
+ totalBytes += dataLengthArray[i];
+
+ // Convert length to 4 endian-neutral bytes
+ uint32_t lengthWord = totalBytes;
+ uint8_t lengthBytes[LENGTH_FIELD_BYTES] = {
+ (uint8_t)lengthWord,
+ (uint8_t)(lengthWord >> 8),
+ (uint8_t)(lengthWord >> 16),
+ (uint8_t)(lengthWord >> 24)
+ };
+
+ int s = PacketizedTCPSocketBase::Send(lengthBytes, LENGTH_FIELD_BYTES);
+ if (s > 0)
+ {
+ for (int i = 0; i < arrayCount; i++)
+ {
+ PacketizedTCPSocketBase::Send(pDataArray[i], dataLengthArray[i]);
+ }
+ }
+
+ return s;
+}
+
+void PacketizedTCPSocket::OnRecv(SocketEvent_TCP* eventHandler, uint8_t* pData, int bytesRead)
+{
+ uint8_t* dataSource = NULL;
+ int dataSourceSize = 0;
+
+ recvBuffLock.DoLock();
+
+ if (pRecvBuff == NULL)
+ {
+ dataSource = pData;
+ dataSourceSize = bytesRead;
+ }
+ else
+ {
+ uint8_t* pRecvBuffNew = (uint8_t*)OVR_REALLOC(pRecvBuff, bytesRead + pRecvBuffSize);
+ if (!pRecvBuffNew)
+ {
+ OVR_FREE(pRecvBuff);
+ pRecvBuff = NULL;
+ pRecvBuffSize = 0;
+ recvBuffLock.Unlock();
+ return;
+ }
+ else
+ {
+ pRecvBuff = pRecvBuffNew;
+
+ memcpy(pRecvBuff + pRecvBuffSize, pData, bytesRead);
+
+ dataSourceSize = pRecvBuffSize + bytesRead;
+ dataSource = pRecvBuff;
+ }
+ }
+
+ int bytesReadFromStream;
+ while (bytesReadFromStream = BytesFromStream(dataSource, dataSourceSize),
+ LENGTH_FIELD_BYTES + bytesReadFromStream <= dataSourceSize)
+ {
+ dataSource += LENGTH_FIELD_BYTES;
+ dataSourceSize -= LENGTH_FIELD_BYTES;
+
+ TCPSocket::OnRecv(eventHandler, dataSource, bytesReadFromStream);
+
+ dataSource += bytesReadFromStream;
+ dataSourceSize -= bytesReadFromStream;
+ }
+
+ if (dataSourceSize > 0)
+ {
+ if (dataSource != NULL)
+ {
+ if (pRecvBuff == NULL)
+ {
+ pRecvBuff = (uint8_t*)OVR_ALLOC(dataSourceSize);
+ if (!pRecvBuff)
+ {
+ pRecvBuffSize = 0;
+ recvBuffLock.Unlock();
+ return;
+ }
+ else
+ {
+ memcpy(pRecvBuff, dataSource, dataSourceSize);
+ }
+ }
+ else
+ {
+ memmove(pRecvBuff, dataSource, dataSourceSize);
+ }
+ }
+ }
+ else
+ {
+ if (pRecvBuff != NULL)
+ OVR_FREE(pRecvBuff);
+
+ pRecvBuff = NULL;
+ }
+ pRecvBuffSize = dataSourceSize;
+
+ recvBuffLock.Unlock();
+}
+
+int PacketizedTCPSocket::BytesFromStream(uint8_t* pData, int bytesRead)
+{
+ if (pData != 0 && bytesRead >= LENGTH_FIELD_BYTES)
+ {
+ return pData[0] | ((uint32_t)pData[1] << 8) | ((uint32_t)pData[2] << 16) | ((uint32_t)pData[3] << 24);
+ }
+
+ return 0;
+}
+
+
+}} // OVR::Net
diff --git a/LibOVR/Src/Net/OVR_PacketizedTCPSocket.h b/LibOVR/Src/Net/OVR_PacketizedTCPSocket.h
new file mode 100644
index 0000000..8052bd3
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_PacketizedTCPSocket.h
@@ -0,0 +1,87 @@
+/************************************************************************************
+
+PublicHeader: n/a
+Filename : OVR_PacketizedTCPSocket.cpp
+Content : TCP with automated message framing.
+Created : June 10, 2014
+Authors : Kevin Jenkins
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_PacketizedTCPSocket_h
+#define OVR_PacketizedTCPSocket_h
+
+#include "OVR_Socket.h"
+#include "../Kernel/OVR_Allocator.h"
+#include "../Kernel/OVR_Atomic.h"
+
+#ifdef OVR_OS_WIN32
+#include "OVR_Win32_Socket.h"
+#else
+#include "OVR_Unix_Socket.h"
+#endif
+
+namespace OVR { namespace Net {
+
+
+//-----------------------------------------------------------------------------
+// NetworkPlugin
+
+// Packetized TCP base socket
+class PacketizedTCPSocketBase : public TCPSocket
+{
+public:
+ PacketizedTCPSocketBase() {}
+ PacketizedTCPSocketBase(SocketHandle _sock, bool isListenSocket) : TCPSocket(_sock, isListenSocket) {}
+};
+
+
+//-----------------------------------------------------------------------------
+// PacketizedTCPSocket
+
+// Uses TCP but is message aligned rather than stream aligned
+// Alternative to reliable UDP
+class PacketizedTCPSocket : public PacketizedTCPSocketBase
+{
+public:
+ PacketizedTCPSocket();
+ PacketizedTCPSocket(SocketHandle _sock, bool isListenSocket);
+ virtual ~PacketizedTCPSocket();
+
+public:
+ virtual int Send(const void* pData, int bytes);
+ virtual int SendAndConcatenate(const void** pDataArray, int *dataLengthArray, int arrayCount);
+
+protected:
+ virtual void OnRecv(SocketEvent_TCP* eventHandler, uint8_t* pData, int bytesRead);
+
+ int BytesFromStream(uint8_t* pData, int bytesRead);
+
+ Lock sendLock;
+ Lock recvBuffLock;
+
+ uint8_t* pRecvBuff; // Queued receive buffered data
+ int pRecvBuffSize; // Size of receive queue in bytes
+};
+
+
+}} // OVR::Net
+
+#endif
diff --git a/LibOVR/Src/Net/OVR_RPC1.cpp b/LibOVR/Src/Net/OVR_RPC1.cpp
new file mode 100644
index 0000000..12afb09
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_RPC1.cpp
@@ -0,0 +1,270 @@
+/************************************************************************************
+
+Filename : OVR_RPC1.cpp
+Content : A network plugin that provides remote procedure call functionality.
+Created : June 10, 2014
+Authors : Kevin Jenkins
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "OVR_RPC1.h"
+#include "OVR_BitStream.h"
+#include "../Kernel/OVR_Threads.h" // Thread::MSleep
+#include "OVR_MessageIDTypes.h"
+
+namespace OVR { namespace Net { namespace Plugins {
+
+
+//-----------------------------------------------------------------------------
+// Types
+
+enum {
+ ID_RPC4_SIGNAL,
+ CALL_BLOCKING,
+ RPC_ERROR_FUNCTION_NOT_REGISTERED,
+ ID_RPC4_RETURN,
+};
+
+
+//-----------------------------------------------------------------------------
+// RPC1
+
+RPC1::RPC1()
+{
+ blockingOnThisConnection = 0;
+ blockingReturnValue = new BitStream();
+}
+
+RPC1::~RPC1()
+{
+ slotHash.Clear();
+ delete blockingReturnValue;
+}
+
+void RPC1::RegisterSlot(OVR::String sharedIdentifier, OVR::Observer<RPCSlot>* rpcSlotObserver )
+{
+ slotHash.AddObserverToSubject(sharedIdentifier, rpcSlotObserver);
+}
+
+bool RPC1::RegisterBlockingFunction(OVR::String uniqueID, RPCDelegate blockingFunction)
+{
+ if (registeredBlockingFunctions.Get(uniqueID))
+ return false;
+
+ registeredBlockingFunctions.Set(uniqueID, blockingFunction);
+ return true;
+}
+
+void RPC1::UnregisterBlockingFunction(OVR::String uniqueID)
+{
+ registeredBlockingFunctions.Remove(uniqueID);
+}
+
+bool RPC1::CallBlocking( OVR::String uniqueID, OVR::Net::BitStream* bitStream, Ptr<Connection> pConnection, OVR::Net::BitStream* returnData )
+{
+ // If invalid parameters,
+ if (!pConnection)
+ {
+ // Note: This may happen if the endpoint disconnects just before the call
+ return false;
+ }
+
+ OVR::Net::BitStream out;
+ out.Write((MessageID) OVRID_RPC1);
+ out.Write((MessageID) CALL_BLOCKING);
+ out.Write(uniqueID);
+ if (bitStream)
+ {
+ bitStream->ResetReadPointer();
+ out.AlignWriteToByteBoundary();
+ out.Write(bitStream);
+ }
+
+ SendParameters sp(pConnection, out.GetData(), out.GetNumberOfBytesUsed());
+
+ if (returnData)
+ {
+ returnData->Reset();
+ }
+
+ // Only one thread call at a time
+ Lock::Locker singleRPCLocker(&singleRPCLock);
+
+ // Note this does not prevent multiple calls at a time because .Wait will unlock it below.
+ // The purpose of this mutex is to synchronize the polling thread and this one, not prevent
+ // multiple threads from invoking RPC.
+ Mutex::Locker locker(&callBlockingMutex);
+
+ blockingReturnValue->Reset();
+ blockingOnThisConnection = pConnection;
+
+ int bytesSent = pSession->Send(&sp);
+ if (bytesSent == sp.Bytes)
+ {
+ while (blockingOnThisConnection == pConnection)
+ {
+ callBlockingWait.Wait(&callBlockingMutex);
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ if (returnData)
+ {
+ returnData->Write(blockingReturnValue);
+ returnData->ResetReadPointer();
+ }
+
+ return true;
+}
+
+bool RPC1::Signal(OVR::String sharedIdentifier, OVR::Net::BitStream* bitStream, Ptr<Connection> pConnection)
+{
+ OVR::Net::BitStream out;
+ out.Write((MessageID) OVRID_RPC1);
+ out.Write((MessageID) ID_RPC4_SIGNAL);
+ //out.Write(PluginId);
+ out.Write(sharedIdentifier);
+ if (bitStream)
+ {
+ bitStream->ResetReadPointer();
+ out.AlignWriteToByteBoundary();
+ out.Write(bitStream);
+ }
+ SendParameters sp(pConnection, out.GetData(), out.GetNumberOfBytesUsed());
+ int32_t bytesSent = pSession->Send(&sp);
+ return bytesSent == sp.Bytes;
+}
+void RPC1::BroadcastSignal(OVR::String sharedIdentifier, OVR::Net::BitStream* bitStream)
+{
+ OVR::Net::BitStream out;
+ out.Write((MessageID) OVRID_RPC1);
+ out.Write((MessageID) ID_RPC4_SIGNAL);
+ //out.Write(PluginId);
+ out.Write(sharedIdentifier);
+ if (bitStream)
+ {
+ bitStream->ResetReadPointer();
+ out.AlignWriteToByteBoundary();
+ out.Write(bitStream);
+ }
+ BroadcastParameters p(out.GetData(), out.GetNumberOfBytesUsed());
+ pSession->Broadcast(&p);
+}
+void RPC1::OnReceive(ReceivePayload *pPayload, ListenerReceiveResult *lrrOut)
+{
+ OVR_UNUSED(lrrOut);
+
+ if (pPayload->pData[0] == OVRID_RPC1)
+ {
+ OVR_ASSERT(pPayload->Bytes >= 2);
+
+ OVR::Net::BitStream bsIn((char*)pPayload->pData, pPayload->Bytes, false);
+ bsIn.IgnoreBytes(2);
+
+ if (pPayload->pData[1] == RPC_ERROR_FUNCTION_NOT_REGISTERED)
+ {
+ Mutex::Locker locker(&callBlockingMutex);
+
+ blockingReturnValue->Reset();
+ blockingOnThisConnection = 0;
+ callBlockingWait.NotifyAll();
+ }
+ else if (pPayload->pData[1] == ID_RPC4_RETURN)
+ {
+ Mutex::Locker locker(&callBlockingMutex);
+
+ blockingReturnValue->Reset();
+ blockingReturnValue->Write(bsIn);
+ blockingOnThisConnection = 0;
+ callBlockingWait.NotifyAll();
+ }
+ else if (pPayload->pData[1] == CALL_BLOCKING)
+ {
+ OVR::String uniqueId;
+ bsIn.Read(uniqueId);
+
+ RPCDelegate *bf = registeredBlockingFunctions.Get(uniqueId);
+ if (bf==0)
+ {
+ OVR::Net::BitStream bsOut;
+ bsOut.Write((unsigned char) OVRID_RPC1);
+ bsOut.Write((unsigned char) RPC_ERROR_FUNCTION_NOT_REGISTERED);
+
+ SendParameters sp(pPayload->pConnection, bsOut.GetData(), bsOut.GetNumberOfBytesUsed());
+ pSession->Send(&sp);
+
+ return;
+ }
+
+ OVR::Net::BitStream returnData;
+ bsIn.AlignReadToByteBoundary();
+ (*bf)(&bsIn, &returnData, pPayload);
+
+ OVR::Net::BitStream out;
+ out.Write((MessageID) OVRID_RPC1);
+ out.Write((MessageID) ID_RPC4_RETURN);
+ returnData.ResetReadPointer();
+ out.AlignWriteToByteBoundary();
+ out.Write(returnData);
+
+ SendParameters sp(pPayload->pConnection, out.GetData(), out.GetNumberOfBytesUsed());
+ pSession->Send(&sp);
+ }
+ else if (pPayload->pData[1]==ID_RPC4_SIGNAL)
+ {
+ OVR::String sharedIdentifier;
+ bsIn.Read(sharedIdentifier);
+
+ Observer<RPCSlot> *o = slotHash.GetSubject(sharedIdentifier);
+
+ if (o)
+ {
+ bsIn.AlignReadToByteBoundary();
+
+ if (o)
+ {
+ OVR::Net::BitStream serializedParameters(bsIn.GetData() + bsIn.GetReadOffset()/8, bsIn.GetNumberOfUnreadBits()/8, false);
+
+ o->Call(&serializedParameters, pPayload);
+ }
+ }
+ }
+ }
+}
+
+void RPC1::OnDisconnected(Connection* conn)
+{
+ if (blockingOnThisConnection == conn)
+ {
+ blockingOnThisConnection = 0;
+ callBlockingWait.NotifyAll();
+ }
+}
+
+void RPC1::OnConnected(Connection* conn)
+{
+ OVR_UNUSED(conn);
+}
+
+
+}}} // OVR::Net::Plugins
diff --git a/LibOVR/Src/Net/OVR_RPC1.h b/LibOVR/Src/Net/OVR_RPC1.h
new file mode 100644
index 0000000..6104ccf
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_RPC1.h
@@ -0,0 +1,105 @@
+/************************************************************************************
+
+PublicHeader: n/a
+Filename : OVR_RPC1.h
+Content : A network plugin that provides remote procedure call functionality.
+Created : June 10, 2014
+Authors : Kevin Jenkins
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Net_RPC_h
+#define OVR_Net_RPC_h
+
+#include "OVR_NetworkPlugin.h"
+#include "../Kernel/OVR_Hash.h"
+#include "../Kernel/OVR_String.h"
+#include "OVR_BitStream.h"
+#include "../Kernel/OVR_Threads.h"
+#include "../Kernel/OVR_Delegates.h"
+#include "../Kernel//OVR_Observer.h"
+
+namespace OVR { namespace Net { namespace Plugins {
+
+
+typedef Delegate3<void, BitStream*, BitStream*, ReceivePayload*> RPCDelegate;
+typedef Delegate2<void, BitStream*, ReceivePayload*> RPCSlot;
+// typedef void ( *Slot ) ( OVR::Net::BitStream *userData, OVR::Net::ReceivePayload *pPayload );
+
+/// NetworkPlugin that maps strings to function pointers. Can invoke the functions using blocking calls with return values, or signal/slots. Networked parameters serialized with BitStream
+class RPC1 : public NetworkPlugin, public NewOverrideBase
+{
+public:
+ RPC1();
+ virtual ~RPC1();
+
+ /// Register a slot, which is a function pointer to one or more implementations that supports this function signature
+ /// When a signal occurs, all slots with the same identifier are called.
+ /// \param[in] sharedIdentifier A string to identify the slot. Recommended to be the same as the name of the function.
+ /// \param[in] functionPtr Pointer to the function.
+ /// \param[in] callPriority Slots are called by order of the highest callPriority first. For slots with the same priority, they are called in the order they are registered
+ void RegisterSlot(OVR::String sharedIdentifier, OVR::Observer<RPCSlot> *rpcSlotObserver);
+
+ /// \brief Same as \a RegisterFunction, but is called with CallBlocking() instead of Call() and returns a value to the caller
+ bool RegisterBlockingFunction(OVR::String uniqueID, RPCDelegate blockingFunction);
+
+ /// \brief Same as UnregisterFunction, except for a blocking function
+ void UnregisterBlockingFunction(OVR::String uniqueID);
+
+ // \brief Same as call, but don't return until the remote system replies.
+ /// Broadcasting parameter does not exist, this can only call one remote system
+ /// \note This function does not return until the remote system responds, disconnects, or was never connected to begin with
+ /// \param[in] Identifier originally passed to RegisterBlockingFunction() on the remote system(s)
+ /// \param[in] bitStream bitStream encoded data to send to the function callback
+ /// \param[in] pConnection connection to send on
+ /// \param[out] returnData Written to by the function registered with RegisterBlockingFunction.
+ /// \return true if successfully called. False on disconnect, function not registered, or not connected to begin with
+ bool CallBlocking( OVR::String uniqueID, OVR::Net::BitStream * bitStream, Ptr<Connection> pConnection, OVR::Net::BitStream *returnData = NULL );
+
+ /// Calls zero or more functions identified by sharedIdentifier registered with RegisterSlot()
+ /// \param[in] sharedIdentifier parameter of the same name passed to RegisterSlot() on the remote system
+ /// \param[in] bitStream bitStream encoded data to send to the function callback
+ /// \param[in] pConnection connection to send on
+ bool Signal(OVR::String sharedIdentifier, OVR::Net::BitStream * bitStream, Ptr<Connection> pConnection);
+ void BroadcastSignal(OVR::String sharedIdentifier, OVR::Net::BitStream * bitStream);
+
+
+protected:
+ virtual void OnReceive(ReceivePayload *pPayload, ListenerReceiveResult *lrrOut);
+
+ virtual void OnDisconnected(Connection* conn);
+ virtual void OnConnected(Connection* conn);
+
+ Hash< String, RPCDelegate, String::HashFunctor > registeredBlockingFunctions;
+ ObserverHash< RPCSlot > slotHash;
+
+ // Synchronization for RPC caller
+ Lock singleRPCLock;
+ Mutex callBlockingMutex;
+ WaitCondition callBlockingWait;
+
+ Net::BitStream* blockingReturnValue;
+ Ptr<Connection> blockingOnThisConnection;
+};
+
+
+}}} // OVR::Net::Plugins
+
+#endif // OVR_Net_RPC_h
diff --git a/LibOVR/Src/Net/OVR_Session.cpp b/LibOVR/Src/Net/OVR_Session.cpp
new file mode 100644
index 0000000..508f0c9
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_Session.cpp
@@ -0,0 +1,736 @@
+/************************************************************************************
+
+Filename : OVR_Session.h
+Content : One network session that provides connection/disconnection events.
+Created : June 10, 2014
+Authors : Kevin Jenkins, Chris Taylor
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "OVR_Session.h"
+#include "OVR_PacketizedTCPSocket.h"
+#include "../Kernel/OVR_Log.h"
+#include "../Service/Service_NetSessionCommon.h"
+
+namespace OVR { namespace Net {
+
+
+//-----------------------------------------------------------------------------
+// Protocol
+
+static const char* OfficialHelloString = "OculusVR_Hello";
+static const char* OfficialAuthorizedString = "OculusVR_Authorized";
+
+void RPC_C2S_Hello::Generate(Net::BitStream* bs)
+{
+ RPC_C2S_Hello hello;
+ hello.HelloString = OfficialHelloString;
+ hello.MajorVersion = RPCVersion_Major;
+ hello.MinorVersion = RPCVersion_Minor;
+ hello.PatchVersion = RPCVersion_Patch;
+ hello.Serialize(bs);
+}
+
+bool RPC_C2S_Hello::Validate()
+{
+ return MajorVersion == RPCVersion_Major &&
+ MinorVersion <= RPCVersion_Minor &&
+ HelloString.CompareNoCase(OfficialHelloString) == 0;
+}
+
+void RPC_S2C_Authorization::Generate(Net::BitStream* bs, String errorString)
+{
+ RPC_S2C_Authorization auth;
+ if (errorString.IsEmpty())
+ {
+ auth.AuthString = OfficialAuthorizedString;
+ }
+ else
+ {
+ auth.AuthString = errorString;
+ }
+ auth.MajorVersion = RPCVersion_Major;
+ auth.MinorVersion = RPCVersion_Minor;
+ auth.PatchVersion = RPCVersion_Patch;
+ auth.Serialize(bs);
+}
+
+bool RPC_S2C_Authorization::Validate()
+{
+ return AuthString.CompareNoCase(OfficialAuthorizedString) == 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Session
+
+void Session::Shutdown()
+{
+ {
+ Lock::Locker locker(&SocketListenersLock);
+
+ const int count = SocketListeners.GetSizeI();
+ for (int i = 0; i < count; ++i)
+ {
+ SocketListeners[i]->Close();
+ }
+ }
+
+ Lock::Locker locker(&ConnectionsLock);
+
+ const int count = AllConnections.GetSizeI();
+ for (int i = 0; i < count; ++i)
+ {
+ Connection* arrayItem = AllConnections[i].GetPtr();
+
+ if (arrayItem->Transport == TransportType_PacketizedTCP)
+ {
+ PacketizedTCPConnection* ptcp = (PacketizedTCPConnection*)arrayItem;
+
+ ptcp->pSocket->Close();
+ }
+ }
+}
+
+SessionResult Session::Listen(ListenerDescription* pListenerDescription)
+{
+ if (pListenerDescription->Transport == TransportType_PacketizedTCP)
+ {
+ BerkleyListenerDescription* bld = (BerkleyListenerDescription*)pListenerDescription;
+ TCPSocket* tcpSocket = (TCPSocket*)bld->BoundSocketToListenWith.GetPtr();
+
+ if (tcpSocket->Listen() < 0)
+ {
+ return SessionResult_ListenFailure;
+ }
+
+ Lock::Locker locker(&SocketListenersLock);
+ SocketListeners.PushBack(tcpSocket);
+ }
+ else if (pListenerDescription->Transport == TransportType_Loopback)
+ {
+ HasLoopbackListener = true;
+ }
+ else
+ {
+ OVR_ASSERT(false);
+ }
+
+ return SessionResult_OK;
+}
+
+SessionResult Session::Connect(ConnectParameters *cp)
+{
+ if (cp->Transport == TransportType_PacketizedTCP)
+ {
+ ConnectParametersBerkleySocket* cp2 = (ConnectParametersBerkleySocket*)cp;
+ Ptr<PacketizedTCPConnection> c;
+
+ {
+ Lock::Locker locker(&ConnectionsLock);
+
+ int connIndex;
+ Ptr<PacketizedTCPConnection> conn = findConnectionBySocket(AllConnections, cp2->BoundSocketToConnectWith, &connIndex);
+ if (conn)
+ {
+ return SessionResult_AlreadyConnected;
+ }
+
+ TCPSocketBase* tcpSock = (TCPSocketBase*)cp2->BoundSocketToConnectWith.GetPtr();
+
+ int ret = tcpSock->Connect(&cp2->RemoteAddress);
+ if (ret < 0)
+ {
+ return SessionResult_ConnectFailure;
+ }
+
+ Ptr<Connection> newConnection = AllocConnection(cp2->Transport);
+ if (!newConnection)
+ {
+ return SessionResult_ConnectFailure;
+ }
+
+ c = (PacketizedTCPConnection*)newConnection.GetPtr();
+ c->pSocket = (TCPSocket*) cp2->BoundSocketToConnectWith.GetPtr();
+ c->Address = cp2->RemoteAddress;
+ c->Transport = cp2->Transport;
+ c->SetState(Client_Connecting);
+
+ AllConnections.PushBack(c);
+
+ }
+
+ if (cp2->Blocking)
+ {
+ c->WaitOnConnecting();
+ }
+
+ if (c->State == State_Connected)
+ {
+ return SessionResult_OK;
+ }
+ else if (c->State == Client_Connecting)
+ {
+ return SessionResult_ConnectInProgress;
+ }
+ else
+ {
+ return SessionResult_ConnectFailure;
+ }
+ }
+ else if (cp->Transport == TransportType_Loopback)
+ {
+ if (HasLoopbackListener)
+ {
+ Ptr<Connection> c = AllocConnection(cp->Transport);
+ if (!c)
+ {
+ return SessionResult_ConnectFailure;
+ }
+
+ c->Transport = cp->Transport;
+ c->SetState(State_Connected);
+
+ {
+ Lock::Locker locker(&ConnectionsLock);
+ AllConnections.PushBack(c);
+ }
+
+ invokeSessionEvent(&SessionListener::OnConnectionRequestAccepted, c);
+ }
+ else
+ {
+ OVR_ASSERT(false);
+ }
+ }
+ else
+ {
+ OVR_ASSERT(false);
+ }
+
+ return SessionResult_OK;
+}
+
+SessionResult Session::ListenPTCP(OVR::Net::BerkleyBindParameters *bbp)
+{
+ Ptr<PacketizedTCPSocket> listenSocket = *new OVR::Net::PacketizedTCPSocket();
+ if (listenSocket->Bind(bbp) == INVALID_SOCKET)
+ {
+ return SessionResult_BindFailure;
+ }
+
+ BerkleyListenerDescription bld;
+ bld.BoundSocketToListenWith = listenSocket.GetPtr();
+ bld.Transport = TransportType_PacketizedTCP;
+
+ return Listen(&bld);
+}
+
+SessionResult Session::ConnectPTCP(OVR::Net::BerkleyBindParameters* bbp, SockAddr* remoteAddress, bool blocking)
+{
+ ConnectParametersBerkleySocket cp(NULL, remoteAddress, blocking, TransportType_PacketizedTCP);
+ Ptr<PacketizedTCPSocket> connectSocket = *new PacketizedTCPSocket();
+
+ cp.BoundSocketToConnectWith = connectSocket.GetPtr();
+ if (connectSocket->Bind(bbp) == INVALID_SOCKET)
+ {
+ return SessionResult_BindFailure;
+ }
+
+ return Connect(&cp);
+}
+
+Ptr<PacketizedTCPConnection> Session::findConnectionBySockAddr(SockAddr* address)
+{
+ const int count = AllConnections.GetSizeI();
+ for (int i = 0; i < count; ++i)
+ {
+ Connection* arrayItem = AllConnections[i].GetPtr();
+
+ if (arrayItem->Transport == TransportType_PacketizedTCP)
+ {
+ PacketizedTCPConnection* conn = (PacketizedTCPConnection*)arrayItem;
+
+ if (conn->Address == *address)
+ {
+ return conn;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int Session::Send(SendParameters *payload)
+{
+ if (payload->pConnection->Transport == TransportType_Loopback)
+ {
+ Lock::Locker locker(&SessionListenersLock);
+
+ const int count = SessionListeners.GetSizeI();
+ for (int i = 0; i < count; ++i)
+ {
+ SessionListener* sl = SessionListeners[i];
+
+ // FIXME: This looks like it needs to be reviewed at some point..
+ ReceivePayload rp;
+ rp.Bytes = payload->Bytes;
+ rp.pConnection = payload->pConnection;
+ rp.pData = (uint8_t*)payload->pData; // FIXME
+ ListenerReceiveResult lrr = LRR_CONTINUE;
+ sl->OnReceive(&rp, &lrr);
+ if (lrr == LRR_RETURN)
+ {
+ return payload->Bytes;
+ }
+ else if (lrr == LRR_BREAK)
+ {
+ break;
+ }
+ }
+
+ return payload->Bytes;
+ }
+ else if (payload->pConnection->Transport == TransportType_PacketizedTCP)
+ {
+ PacketizedTCPConnection* conn = (PacketizedTCPConnection*)payload->pConnection.GetPtr();
+
+ return conn->pSocket->Send(payload->pData, payload->Bytes);
+ }
+ else
+ {
+ OVR_ASSERT(false);
+ }
+
+ return 0;
+}
+void Session::Broadcast(BroadcastParameters *payload)
+{
+ SendParameters sp;
+ sp.Bytes=payload->Bytes;
+ sp.pData=payload->pData;
+
+ {
+ Lock::Locker locker(&ConnectionsLock);
+
+ const int connectionCount = FullConnections.GetSizeI();
+ for (int i = 0; i < connectionCount; ++i)
+ {
+ sp.pConnection = FullConnections[i];
+ Send(&sp);
+ }
+ }
+}
+// DO NOT CALL Poll() FROM MULTIPLE THREADS due to allBlockingTcpSockets being a member
+void Session::Poll(bool listeners)
+{
+ allBlockingTcpSockets.Clear();
+
+ if (listeners)
+ {
+ Lock::Locker locker(&SocketListenersLock);
+
+ const int listenerCount = SocketListeners.GetSizeI();
+ for (int i = 0; i < listenerCount; ++i)
+ {
+ allBlockingTcpSockets.PushBack(SocketListeners[i]);
+ }
+ }
+
+ {
+ Lock::Locker locker(&ConnectionsLock);
+
+ const int connectionCount = AllConnections.GetSizeI();
+ for (int i = 0; i < connectionCount; ++i)
+ {
+ Connection* arrayItem = AllConnections[i].GetPtr();
+
+ if (arrayItem->Transport == TransportType_PacketizedTCP)
+ {
+ PacketizedTCPConnection* ptcp = (PacketizedTCPConnection*)arrayItem;
+
+ allBlockingTcpSockets.PushBack(ptcp->pSocket);
+ }
+ else
+ {
+ OVR_ASSERT(false);
+ }
+ }
+ }
+
+ const int count = allBlockingTcpSockets.GetSizeI();
+ if (count > 0)
+ {
+ TCPSocketPollState state;
+
+ // Add all the sockets for polling,
+ for (int i = 0; i < count; ++i)
+ {
+ Net::TCPSocket* sock = allBlockingTcpSockets[i].GetPtr();
+
+ // If socket handle is invalid,
+ if (sock->GetSocketHandle() == INVALID_SOCKET)
+ {
+ OVR_DEBUG_LOG(("[Session] Detected an invalid socket handle - Treating it as a disconnection."));
+ sock->IsConnecting = false;
+ TCP_OnClosed(sock);
+ }
+ else
+ {
+ state.Add(sock);
+ }
+ }
+
+ // If polling returns with an event,
+ if (state.Poll(allBlockingTcpSockets[0]->GetBlockingTimeoutUsec(), allBlockingTcpSockets[0]->GetBlockingTimeoutSec()))
+ {
+ // Handle any events for each socket
+ for (int i = 0; i < count; ++i)
+ {
+ state.HandleEvent(allBlockingTcpSockets[i], this);
+ }
+ }
+ }
+}
+
+void Session::AddSessionListener(SessionListener* se)
+{
+ Lock::Locker locker(&SessionListenersLock);
+
+ const int count = SessionListeners.GetSizeI();
+ for (int i = 0; i < count; ++i)
+ {
+ if (SessionListeners[i] == se)
+ {
+ // Already added
+ return;
+ }
+ }
+
+ SessionListeners.PushBack(se);
+ se->OnAddedToSession(this);
+}
+
+void Session::RemoveSessionListener(SessionListener* se)
+{
+ Lock::Locker locker(&SessionListenersLock);
+
+ const int count = SessionListeners.GetSizeI();
+ for (int i = 0; i < count; ++i)
+ {
+ if (SessionListeners[i] == se)
+ {
+ se->OnRemovedFromSession(this);
+
+ SessionListeners.RemoveAtUnordered(i);
+ break;
+ }
+ }
+}
+SInt32 Session::GetActiveSocketsCount()
+{
+ Lock::Locker locker1(&SocketListenersLock);
+ Lock::Locker locker2(&ConnectionsLock);
+ return SocketListeners.GetSize() + AllConnections.GetSize()>0;
+}
+Ptr<Connection> Session::AllocConnection(TransportType transport)
+{
+ switch (transport)
+ {
+ case TransportType_Loopback: return *new Connection();
+ case TransportType_TCP: return *new TCPConnection();
+ case TransportType_PacketizedTCP: return *new PacketizedTCPConnection();
+ default:
+ OVR_ASSERT(false);
+ break;
+ }
+
+ return NULL;
+}
+
+Ptr<PacketizedTCPConnection> Session::findConnectionBySocket(Array< Ptr<Connection> >& connectionArray, Socket* s, int *connectionIndex)
+{
+ const int count = connectionArray.GetSizeI();
+ for (int i = 0; i < count; ++i)
+ {
+ Connection* arrayItem = connectionArray[i].GetPtr();
+
+ if (arrayItem->Transport == TransportType_PacketizedTCP)
+ {
+ PacketizedTCPConnection* ptc = (PacketizedTCPConnection*)arrayItem;
+
+ if (ptc->pSocket == s)
+ {
+ if (connectionIndex)
+ {
+ *connectionIndex = i;
+ }
+ return ptc;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+int Session::invokeSessionListeners(ReceivePayload* rp)
+{
+ Lock::Locker locker(&SessionListenersLock);
+
+ const int count = SessionListeners.GetSizeI();
+ for (int j = 0; j < count; ++j)
+ {
+ ListenerReceiveResult lrr = LRR_CONTINUE;
+ SessionListeners[j]->OnReceive(rp, &lrr);
+
+ if (lrr == LRR_RETURN || lrr == LRR_BREAK)
+ {
+ break;
+ }
+ }
+
+ return rp->Bytes;
+}
+
+void Session::TCP_OnRecv(Socket* pSocket, uint8_t* pData, int bytesRead)
+{
+ // KevinJ: 9/2/2014 Fix deadlock - Watchdog calls Broadcast(), which locks ConnectionsLock().
+ // Lock::Locker locker(&ConnectionsLock);
+
+ // Look for the connection in the full connection list first
+ int connIndex;
+ ConnectionsLock.DoLock();
+ Ptr<PacketizedTCPConnection> conn = findConnectionBySocket(AllConnections, pSocket, &connIndex);
+ ConnectionsLock.Unlock();
+ if (conn)
+ {
+ if (conn->State == State_Connected)
+ {
+ ReceivePayload rp;
+ rp.Bytes = bytesRead;
+ rp.pConnection = conn;
+ rp.pData = pData;
+
+ // Call listeners
+ invokeSessionListeners(&rp);
+ }
+ else if (conn->State == Client_ConnectedWait)
+ {
+ // Check the version data from the message
+ BitStream bsIn((char*)pData, bytesRead, false);
+
+ RPC_S2C_Authorization auth;
+ if (!auth.Deserialize(&bsIn) ||
+ !auth.Validate())
+ {
+ LogError("{ERR-001} [Session] REJECTED: OVRService did not authorize us: %s", auth.AuthString.ToCStr());
+
+ conn->SetState(State_Zombie);
+ invokeSessionEvent(&SessionListener::OnIncompatibleProtocol, conn);
+ }
+ else
+ {
+ // Read remote version
+ conn->RemoteMajorVersion = auth.MajorVersion;
+ conn->RemoteMinorVersion = auth.MinorVersion;
+ conn->RemotePatchVersion = auth.PatchVersion;
+
+ // Mark as connected
+ conn->SetState(State_Connected);
+ ConnectionsLock.DoLock();
+ int connIndex2;
+ if (findConnectionBySocket(AllConnections, pSocket, &connIndex2)==conn && findConnectionBySocket(FullConnections, pSocket, &connIndex2)==NULL)
+ {
+ FullConnections.PushBack(conn);
+ }
+ ConnectionsLock.Unlock();
+ invokeSessionEvent(&SessionListener::OnConnectionRequestAccepted, conn);
+ }
+ }
+ else if (conn->State == Server_ConnectedWait)
+ {
+ // Check the version data from the message
+ BitStream bsIn((char*)pData, bytesRead, false);
+
+ RPC_C2S_Hello hello;
+ if (!hello.Deserialize(&bsIn) ||
+ !hello.Validate())
+ {
+ LogError("{ERR-002} [Session] REJECTED: Rift application is using an incompatible version %d.%d.%d (my version=%d.%d.%d)",
+ hello.MajorVersion, hello.MinorVersion, hello.PatchVersion,
+ RPCVersion_Major, RPCVersion_Minor, RPCVersion_Patch);
+
+ conn->SetState(State_Zombie);
+
+ // Send auth response
+ BitStream bsOut;
+ RPC_S2C_Authorization::Generate(&bsOut, "Incompatible protocol version. Please make sure your OVRService and SDK are both up to date.");
+ conn->pSocket->Send(bsOut.GetData(), bsOut.GetNumberOfBytesUsed());
+ }
+ else
+ {
+ // Read remote version
+ conn->RemoteMajorVersion = hello.MajorVersion;
+ conn->RemoteMinorVersion = hello.MinorVersion;
+ conn->RemotePatchVersion = hello.PatchVersion;
+
+ // Send auth response
+ BitStream bsOut;
+ RPC_S2C_Authorization::Generate(&bsOut);
+ conn->pSocket->Send(bsOut.GetData(), bsOut.GetNumberOfBytesUsed());
+
+ // Mark as connected
+ conn->SetState(State_Connected);
+ ConnectionsLock.DoLock();
+ int connIndex2;
+ if (findConnectionBySocket(AllConnections, pSocket, &connIndex2)==conn && findConnectionBySocket(FullConnections, pSocket, &connIndex2)==NULL)
+ {
+ FullConnections.PushBack(conn);
+ }
+ ConnectionsLock.Unlock();
+ invokeSessionEvent(&SessionListener::OnNewIncomingConnection, conn);
+
+ }
+ }
+ else
+ {
+ OVR_ASSERT(false);
+ }
+ }
+}
+
+void Session::TCP_OnClosed(TCPSocket* s)
+{
+ Lock::Locker locker(&ConnectionsLock);
+
+ // If found in the full connection list,
+ int connIndex;
+ Ptr<PacketizedTCPConnection> conn = findConnectionBySocket(AllConnections, s, &connIndex);
+ if (conn)
+ {
+ AllConnections.RemoveAtUnordered(connIndex);
+
+ // If in the full connection list,
+ if (findConnectionBySocket(FullConnections, s, &connIndex))
+ {
+ FullConnections.RemoveAtUnordered(connIndex);
+ }
+
+ // Generate an appropriate event for the current state
+ switch (conn->State)
+ {
+ case Client_Connecting:
+ invokeSessionEvent(&SessionListener::OnConnectionAttemptFailed, conn);
+ break;
+ case Client_ConnectedWait:
+ case Server_ConnectedWait:
+ invokeSessionEvent(&SessionListener::OnHandshakeAttemptFailed, conn);
+ break;
+ case State_Connected:
+ case State_Zombie:
+ invokeSessionEvent(&SessionListener::OnDisconnected, conn);
+ break;
+ default:
+ OVR_ASSERT(false);
+ break;
+ }
+
+ conn->SetState(State_Zombie);
+ }
+}
+
+void Session::TCP_OnAccept(TCPSocket* pListener, SockAddr* pSockAddr, SocketHandle newSock)
+{
+ OVR_UNUSED(pListener);
+ OVR_ASSERT(pListener->Transport == TransportType_PacketizedTCP);
+
+
+ Ptr<PacketizedTCPSocket> newSocket = *new PacketizedTCPSocket(newSock, false);
+ // If pSockAddr is not localhost, then close newSock
+ if (pSockAddr->IsLocalhost()==false)
+ {
+ newSocket->Close();
+ return;
+ }
+
+ if (newSocket)
+ {
+ Ptr<Connection> b = AllocConnection(TransportType_PacketizedTCP);
+ Ptr<PacketizedTCPConnection> c = (PacketizedTCPConnection*)b.GetPtr();
+ c->pSocket = newSocket;
+ c->Address = *pSockAddr;
+ c->State = Server_ConnectedWait;
+
+ {
+ Lock::Locker locker(&ConnectionsLock);
+ AllConnections.PushBack(c);
+ }
+
+ // Server does not send the first packet. It waits for the client to send its version
+ }
+}
+
+void Session::TCP_OnConnected(TCPSocket *s)
+{
+ Lock::Locker locker(&ConnectionsLock);
+
+ // If connection was found,
+ PacketizedTCPConnection* conn = findConnectionBySocket(AllConnections, s);
+ if (conn)
+ {
+ OVR_ASSERT(conn->State == Client_Connecting);
+
+ // Send hello message
+ BitStream bsOut;
+ RPC_C2S_Hello::Generate(&bsOut);
+ conn->pSocket->Send(bsOut.GetData(), bsOut.GetNumberOfBytesUsed());
+
+ // Just update state but do not generate any notifications yet
+ conn->State = Client_ConnectedWait;
+ }
+}
+
+void Session::invokeSessionEvent(void(SessionListener::*f)(Connection*), Connection* conn)
+{
+ Lock::Locker locker(&SessionListenersLock);
+
+ const int count = SessionListeners.GetSizeI();
+ for (int i = 0; i < count; ++i)
+ {
+ (SessionListeners[i]->*f)(conn);
+ }
+}
+
+Ptr<Connection> Session::GetConnectionAtIndex(int index)
+{
+ Lock::Locker locker(&ConnectionsLock);
+
+ const int count = FullConnections.GetSizeI();
+
+ if (index < count)
+ {
+ return FullConnections[index];
+ }
+
+ return NULL;
+}
+
+
+}} // OVR::Net
diff --git a/LibOVR/Src/Net/OVR_Session.h b/LibOVR/Src/Net/OVR_Session.h
new file mode 100644
index 0000000..b6ef07b
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_Session.h
@@ -0,0 +1,513 @@
+/************************************************************************************
+
+PublicHeader: n/a
+Filename : OVR_Session.h
+Content : One network session that provides connection/disconnection events.
+Created : June 10, 2014
+Authors : Kevin Jenkins, Chris Taylor
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Session_h
+#define OVR_Session_h
+
+#include "OVR_Socket.h"
+#include "OVR_PacketizedTCPSocket.h"
+#include "../Kernel/OVR_Array.h"
+#include "../Kernel/OVR_Threads.h"
+#include "../Kernel/OVR_Atomic.h"
+#include "../Kernel/OVR_RefCount.h"
+
+namespace OVR { namespace Net {
+
+class Session;
+
+
+//-----------------------------------------------------------------------------
+// Based on Semantic Versioning ( http://semver.org/ )
+//
+// Please update changelog below:
+// 1.0.0 - [SDK 0.4.0] Initial version (July 21, 2014)
+// 1.1.0 - Add Get/SetDriverMode_1, HMDCountUpdate_1
+// Version mismatch results (July 28, 2014)
+//-----------------------------------------------------------------------------
+
+static const uint16_t RPCVersion_Major = 1; // MAJOR version when you make incompatible API changes,
+static const uint16_t RPCVersion_Minor = 2; // MINOR version when you add functionality in a backwards-compatible manner, and
+static const uint16_t RPCVersion_Patch = 0; // PATCH version when you make backwards-compatible bug fixes.
+
+// Client starts communication by sending its version number.
+struct RPC_C2S_Hello
+{
+ RPC_C2S_Hello() :
+ MajorVersion(0),
+ MinorVersion(0),
+ PatchVersion(0)
+ {
+ }
+
+ String HelloString;
+
+ // Client version info
+ uint16_t MajorVersion, MinorVersion, PatchVersion;
+
+ void Serialize(Net::BitStream* bs)
+ {
+ bs->Write(HelloString);
+ bs->Write(MajorVersion);
+ bs->Write(MinorVersion);
+ bs->Write(PatchVersion);
+ }
+
+ bool Deserialize(Net::BitStream* bs)
+ {
+ bs->Read(HelloString);
+ bs->Read(MajorVersion);
+ bs->Read(MinorVersion);
+ return bs->Read(PatchVersion);
+ }
+
+ static void Generate(Net::BitStream* bs);
+
+ bool Validate();
+};
+
+// Server responds with an authorization accepted message, including the server's version number
+struct RPC_S2C_Authorization
+{
+ RPC_S2C_Authorization() :
+ MajorVersion(0),
+ MinorVersion(0),
+ PatchVersion(0)
+ {
+ }
+
+ String AuthString;
+
+ // Server version info
+ uint16_t MajorVersion, MinorVersion, PatchVersion;
+
+ void Serialize(Net::BitStream* bs)
+ {
+ bs->Write(AuthString);
+ bs->Write(MajorVersion);
+ bs->Write(MinorVersion);
+ bs->Write(PatchVersion);
+ }
+
+ bool Deserialize(Net::BitStream* bs)
+ {
+ bs->Read(AuthString);
+ bs->Read(MajorVersion);
+ bs->Read(MinorVersion);
+ return bs->Read(PatchVersion);
+ }
+
+ static void Generate(Net::BitStream* bs, String errorString = "");
+
+ bool Validate();
+};
+
+
+//-----------------------------------------------------------------------------
+// Result of a session function
+enum SessionResult
+{
+ SessionResult_OK,
+ SessionResult_BindFailure,
+ SessionResult_ListenFailure,
+ SessionResult_ConnectFailure,
+ SessionResult_ConnectInProgress,
+ SessionResult_AlreadyConnected,
+};
+
+
+//-----------------------------------------------------------------------------
+// Connection state
+enum EConnectionState
+{
+ State_Zombie, // Disconnected
+
+ // Client-only:
+ Client_Connecting, // Waiting for TCP connection
+ Client_ConnectedWait, // Connected! Waiting for server to authorize
+
+ // Server-only:
+ Server_ConnectedWait, // Connected! Waiting for client handshake
+
+ State_Connected // Connected
+};
+
+
+//-----------------------------------------------------------------------------
+// Generic connection over any transport
+class Connection : public RefCountBase<Connection>
+{
+public:
+ Connection() :
+ Transport(TransportType_None),
+ State(State_Zombie),
+ RemoteMajorVersion(0),
+ RemoteMinorVersion(0),
+ RemotePatchVersion(0)
+ {
+ }
+ virtual ~Connection() // Allow delete from base
+ {
+ }
+
+public:
+ virtual void SetState(EConnectionState s) {State = s;}
+
+ TransportType Transport;
+ EConnectionState State;
+
+ // Version number read from remote host just before connection completes
+ int RemoteMajorVersion;
+ int RemoteMinorVersion;
+ int RemotePatchVersion;
+};
+
+
+//-----------------------------------------------------------------------------
+// Generic network connection over any network transport
+class NetworkConnection : public Connection
+{
+protected:
+ NetworkConnection()
+ {
+ }
+ virtual ~NetworkConnection()
+ {
+ }
+
+public:
+ virtual void SetState(EConnectionState s)
+ {
+ if (s != State)
+ {
+ Mutex::Locker locker(&StateMutex);
+
+ if (s != State)
+ {
+ State = s;
+
+ if (State != Client_Connecting)
+ {
+ ConnectingWait.NotifyAll();
+ }
+ }
+ }
+ }
+
+ void WaitOnConnecting()
+ {
+ Mutex::Locker locker(&StateMutex);
+
+ while (State == Client_Connecting)
+ {
+ ConnectingWait.Wait(&StateMutex);
+ }
+ }
+
+ SockAddr Address;
+ Mutex StateMutex;
+ WaitCondition ConnectingWait;
+};
+
+
+//-----------------------------------------------------------------------------
+// TCP Connection
+class TCPConnection : public NetworkConnection
+{
+public:
+ TCPConnection()
+ {
+ }
+ virtual ~TCPConnection()
+ {
+ }
+
+public:
+ Ptr<TCPSocket> pSocket;
+};
+
+
+//-----------------------------------------------------------------------------
+// Packetized TCP Connection
+class PacketizedTCPConnection : public TCPConnection
+{
+public:
+ PacketizedTCPConnection()
+ {
+ Transport = TransportType_PacketizedTCP;
+ }
+ virtual ~PacketizedTCPConnection()
+ {
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// Generic socket listener description
+class ListenerDescription
+{
+public:
+ ListenerDescription() :
+ Transport(TransportType_None)
+ {
+ }
+
+ TransportType Transport;
+};
+
+
+//-----------------------------------------------------------------------------
+// Description for a Berkley socket listener
+class BerkleyListenerDescription : public ListenerDescription
+{
+public:
+ static const int DefaultMaxIncomingConnections = 64;
+ static const int DefaultMaxConnections = 128;
+
+ BerkleyListenerDescription() :
+ MaxIncomingConnections(DefaultMaxIncomingConnections),
+ MaxConnections(DefaultMaxConnections)
+ {
+ }
+
+ Ptr<BerkleySocket> BoundSocketToListenWith;
+ int MaxIncomingConnections;
+ int MaxConnections;
+};
+
+
+//-----------------------------------------------------------------------------
+// Receive payload
+struct ReceivePayload
+{
+ Connection* pConnection; // Source connection
+ uint8_t* pData; // Pointer to data received
+ int Bytes; // Number of bytes of data received
+};
+
+//-----------------------------------------------------------------------------
+// Broadcast parameters
+class BroadcastParameters
+{
+public:
+ BroadcastParameters() :
+ pData(NULL),
+ Bytes(0)
+ {
+ }
+
+ BroadcastParameters(const void* _pData, int _bytes) :
+ pData(_pData),
+ Bytes(_bytes)
+ {
+ }
+
+public:
+ const void* pData; // Pointer to data to send
+ int Bytes; // Number of bytes of data received
+};
+
+//-----------------------------------------------------------------------------
+// Send parameters
+class SendParameters
+{
+public:
+ SendParameters() :
+ pData(NULL),
+ Bytes(0)
+ {
+ }
+ SendParameters(Ptr<Connection> _pConnection, const void* _pData, int _bytes) :
+ pConnection(_pConnection),
+ pData(_pData),
+ Bytes(_bytes)
+ {
+ }
+
+public:
+ Ptr<Connection> pConnection; // Connection to use
+ const void* pData; // Pointer to data to send
+ int Bytes; // Number of bytes of data received
+};
+
+
+//-----------------------------------------------------------------------------
+// Parameters to connect
+struct ConnectParameters
+{
+public:
+ ConnectParameters() :
+ Transport(TransportType_None)
+ {
+ }
+
+ TransportType Transport;
+};
+
+struct ConnectParametersBerkleySocket : public ConnectParameters
+{
+ SockAddr RemoteAddress;
+ Ptr<BerkleySocket> BoundSocketToConnectWith;
+ bool Blocking;
+
+ ConnectParametersBerkleySocket(BerkleySocket* s, SockAddr* addr, bool blocking,
+ TransportType transport) :
+ RemoteAddress(*addr),
+ BoundSocketToConnectWith(s),
+ Blocking(blocking)
+ {
+ Transport = transport;
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// Listener receive result
+enum ListenerReceiveResult
+{
+ /// The SessionListener used this message and it shouldn't be given to the user.
+ LRR_RETURN = 0,
+
+ /// The SessionListener is going to hold on to this message. Do not deallocate it but do not pass it to other plugins either.
+ LRR_BREAK,
+
+ /// This message will be processed by other SessionListeners, and at last by the user.
+ LRR_CONTINUE,
+};
+
+
+//-----------------------------------------------------------------------------
+// SessionListener
+
+// Callback interface for network events such as connecting, disconnecting, getting data, independent of the transport medium
+class SessionListener
+{
+public:
+ virtual ~SessionListener(){}
+
+ // Data events
+ virtual void OnReceive(ReceivePayload* pPayload, ListenerReceiveResult* lrrOut) { OVR_UNUSED2(pPayload, lrrOut); }
+
+ // Connection was closed
+ virtual void OnDisconnected(Connection* conn) = 0;
+
+ // Connection was created (some data was exchanged to verify protocol compatibility too)
+ virtual void OnConnected(Connection* conn) = 0;
+
+ // Server accepted client
+ virtual void OnNewIncomingConnection(Connection* conn) { OnConnected(conn); }
+ // Client was accepted
+ virtual void OnConnectionRequestAccepted(Connection* conn) { OnConnected(conn); }
+
+ // Connection attempt failed for some reason
+ virtual void OnConnectionAttemptFailed(Connection* conn) { OnDisconnected(conn); }
+
+ // Incompatible protocol
+ virtual void OnIncompatibleProtocol(Connection* conn) { OnConnectionAttemptFailed(conn); }
+ // Disconnected during initial handshake
+ virtual void OnHandshakeAttemptFailed(Connection* conn) { OnConnectionAttemptFailed(conn); }
+
+ // Other
+ virtual void OnAddedToSession(Session* session) { OVR_UNUSED(session); }
+ virtual void OnRemovedFromSession(Session* session) { OVR_UNUSED(session); }
+};
+
+
+//-----------------------------------------------------------------------------
+// Session
+
+// Interface for network events such as listening on a socket, sending data, connecting, and disconnecting. Works independently of the transport medium and also implements loopback
+class Session : public SocketEvent_TCP, public NewOverrideBase
+{
+ // Implement a policy to avoid releasing memory backing allBlockingTcpSockets
+ struct ArrayNoShrinkPolicy : ArrayDefaultPolicy
+ {
+ bool NeverShrinking() const { return 1; }
+ };
+
+public:
+ Session() :
+ HasLoopbackListener(false)
+ {
+ }
+ virtual ~Session()
+ {
+ // Ensure memory backing the sockets array is released
+ allBlockingTcpSockets.ClearAndRelease();
+ }
+
+ virtual SessionResult Listen(ListenerDescription* pListenerDescription);
+ virtual SessionResult Connect(ConnectParameters* cp);
+ virtual int Send(SendParameters* payload);
+ virtual void Broadcast(BroadcastParameters* payload);
+ // DO NOT CALL Poll() FROM MULTIPLE THREADS due to allBlockingTcpSockets being a member
+ virtual void Poll(bool listeners = true);
+ virtual void AddSessionListener(SessionListener* se);
+ virtual void RemoveSessionListener(SessionListener* se);
+ virtual SInt32 GetActiveSocketsCount();
+
+ // Packetized TCP convenience functions
+ virtual SessionResult ListenPTCP(BerkleyBindParameters* bbp);
+ virtual SessionResult ConnectPTCP(BerkleyBindParameters* bbp, SockAddr* RemoteAddress, bool blocking);
+
+ // Closes all the sockets; useful for interrupting the socket polling during shutdown
+ void Shutdown();
+
+ // Get count of successful connections (past handshake point)
+ int GetConnectionCount() const
+ {
+ return FullConnections.GetSizeI();
+ }
+ Ptr<Connection> GetConnectionAtIndex(int index);
+
+protected:
+ virtual Ptr<Connection> AllocConnection(TransportType transportType);
+
+ Lock SocketListenersLock, ConnectionsLock, SessionListenersLock;
+ bool HasLoopbackListener; // Has loopback listener installed?
+ Array< Ptr<TCPSocket> > SocketListeners; // List of active sockets
+ Array< Ptr<Connection> > AllConnections; // List of active connections stuck at the versioning handshake
+ Array< Ptr<Connection> > FullConnections; // List of active connections past the versioning handshake
+ Array< SessionListener* > SessionListeners; // List of session listeners
+ Array< Ptr< Net::TCPSocket >, ArrayNoShrinkPolicy > allBlockingTcpSockets; // Preallocated blocking sockets array
+
+ // Tools
+ Ptr<PacketizedTCPConnection> findConnectionBySocket(Array< Ptr<Connection> >& connectionArray, Socket* s, int *connectionIndex = NULL); // Call with ConnectionsLock held
+ Ptr<PacketizedTCPConnection> findConnectionBySockAddr(SockAddr* address); // Call with ConnectionsLock held
+ int invokeSessionListeners(ReceivePayload*);
+ void invokeSessionEvent(void(SessionListener::*f)(Connection*), Connection* pConnection);
+
+ // TCP
+ virtual void TCP_OnRecv(Socket* pSocket, uint8_t* pData, int bytesRead);
+ virtual void TCP_OnClosed(TCPSocket* pSocket);
+ virtual void TCP_OnAccept(TCPSocket* pListener, SockAddr* pSockAddr, SocketHandle newSock);
+ virtual void TCP_OnConnected(TCPSocket* pSocket);
+};
+
+
+}} // OVR::Net
+
+#endif
diff --git a/LibOVR/Src/Net/OVR_Socket.cpp b/LibOVR/Src/Net/OVR_Socket.cpp
new file mode 100644
index 0000000..d0b4e77
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_Socket.cpp
@@ -0,0 +1,94 @@
+/************************************************************************************
+
+Filename : OVR_Socket.cpp
+Content : Socket common data shared between all platforms.
+Created : June 10, 2014
+Authors : Kevin Jenkins, Chris Taylor
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "OVR_Socket.h"
+
+namespace OVR { namespace Net {
+
+
+//-----------------------------------------------------------------------------
+// Socket
+
+Socket::Socket() :
+ Transport(TransportType_None)
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// BerkleyBindParameters
+
+BerkleyBindParameters::BerkleyBindParameters() :
+ Port(0),
+ Address(),
+ blockingTimeout(0x7fffffff)
+{
+}
+
+//-----------------------------------------------------------------------------
+// BerkleySocket
+
+BerkleySocket::BerkleySocket() :
+ TheSocket(INVALID_SOCKET)
+ //TimeoutUsec(0) // Initialized by SetBlockingTimeout
+ //TimeoutSec(0) // "
+{
+ SetBlockingTimeout(1000);
+}
+
+BerkleySocket::~BerkleySocket()
+{
+ // Close socket on destruction
+ Close();
+}
+
+
+//-----------------------------------------------------------------------------
+// UDPSocketBase
+
+UDPSocketBase::UDPSocketBase()
+{
+ Transport = TransportType_UDP;
+}
+
+
+//-----------------------------------------------------------------------------
+// TCPSocketBase
+
+TCPSocketBase::TCPSocketBase()
+ : IsListenSocket(false)
+{
+ Transport = TransportType_TCP;
+}
+
+TCPSocketBase::TCPSocketBase(SocketHandle handle)
+ : IsListenSocket(false)
+{
+ TheSocket = handle;
+}
+
+
+}} // OVR::Net
diff --git a/LibOVR/Src/Net/OVR_Socket.h b/LibOVR/Src/Net/OVR_Socket.h
new file mode 100644
index 0000000..b02e038
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_Socket.h
@@ -0,0 +1,242 @@
+/************************************************************************************
+
+PublicHeader: n/a
+Filename : OVR_Socket.h
+Content : Socket common data shared between all platforms.
+Created : June 10, 2014
+Authors : Kevin Jenkins, Chris Taylor
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Socket_h
+#define OVR_Socket_h
+
+#include "../Kernel/OVR_Types.h"
+#include "../Kernel/OVR_Timer.h"
+#include "../Kernel/OVR_Allocator.h"
+#include "../Kernel/OVR_RefCount.h"
+#include "../Kernel/OVR_String.h"
+
+// OS-specific socket headers
+#if defined(OVR_OS_WIN32)
+#include <WinSock2.h>
+#include <WS2tcpip.h>
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#else
+# include <unistd.h>
+# include <sys/types.h>
+# include <netinet/in.h>
+#ifdef OVR_OS_ANDROID
+#include <sys/socket.h>
+#endif
+#endif
+
+namespace OVR { namespace Net {
+
+class SockAddr;
+class UDPSocket;
+class TCPSocket;
+
+
+//-----------------------------------------------------------------------------
+// Portable numeric Socket handle
+#if defined(OVR_OS_WIN32)
+typedef SOCKET SocketHandle;
+#else
+typedef int SocketHandle;
+static const SocketHandle INVALID_SOCKET = -1;
+static const int SOCKET_ERROR = -1;
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Types of network transport
+enum TransportType
+{
+ TransportType_None, // No transport (useful placeholder for invalid states)
+ TransportType_Loopback, // Loopback transport: Class talks to itself
+ TransportType_TCP, // TCP/IPv4/v6
+ TransportType_UDP, // UDP/IPv4/v6
+ TransportType_PacketizedTCP // Packetized TCP: Message framing is automatic
+};
+
+
+//-----------------------------------------------------------------------------
+// Abstraction for a network socket. Inheritance hierarchy
+// modeled after RakNet so that future support can be added
+// for Linux, Windows RT, consoles, etc.
+class Socket : public RefCountBase<Socket>
+{
+public:
+ Socket();
+ virtual void Close() = 0;
+
+public:
+ TransportType Transport; // Type of transport
+};
+
+
+//-----------------------------------------------------------------------------
+// Bind parameters for Berkley sockets
+struct BerkleyBindParameters
+{
+public:
+ BerkleyBindParameters();
+
+public:
+ uint16_t Port; // Port
+ String Address;
+ uint32_t blockingTimeout;
+};
+
+
+//-----------------------------------------------------------------------------
+// Berkley socket
+class BerkleySocket : public Socket
+{
+public:
+ BerkleySocket();
+ virtual ~BerkleySocket();
+
+ virtual void Close();
+ virtual int32_t GetSockname(SockAddr* pSockAddrOut);
+ virtual void SetBlockingTimeout(int timeoutMs) // milliseconds
+ {
+ TimeoutSec = timeoutMs / 1000;
+ TimeoutUsec = (timeoutMs % 1000) * 1000;
+ }
+ int GetBlockingTimeoutUsec() const
+ {
+ return TimeoutUsec;
+ }
+ int GetBlockingTimeoutSec() const
+ {
+ return TimeoutSec;
+ }
+ SocketHandle GetSocketHandle() const
+ {
+ return TheSocket;
+ }
+
+protected:
+ SocketHandle TheSocket; // Socket handle
+ int TimeoutUsec, TimeoutSec;
+};
+
+
+//-----------------------------------------------------------------------------
+// UDP socket events
+class SocketEvent_UDP
+{
+public:
+ virtual ~SocketEvent_UDP(){}
+
+ virtual void UDP_OnRecv(Socket* pSocket, uint8_t* pData,
+ uint32_t bytesRead, SockAddr* pSockAddr)
+ {
+ OVR_UNUSED4(pSocket, pData, bytesRead, pSockAddr);
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// TCP socket events
+class SocketEvent_TCP
+{
+public:
+ virtual ~SocketEvent_TCP(){}
+
+ virtual void TCP_OnRecv (Socket* pSocket,
+ uint8_t* pData,
+ int bytesRead)
+ {
+ OVR_UNUSED3(pSocket, pData, bytesRead);
+ }
+ virtual void TCP_OnClosed (TCPSocket* pSocket)
+ {
+ OVR_UNUSED(pSocket);
+ }
+ virtual void TCP_OnAccept (TCPSocket* pListener,
+ SockAddr* pSockAddr,
+ SocketHandle newSock)
+ {
+ OVR_UNUSED3(pListener, pSockAddr, newSock);
+ }
+ virtual void TCP_OnConnected(TCPSocket* pSocket)
+ {
+ OVR_UNUSED(pSocket);
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// UDP Berkley socket
+
+// Base class for UDP sockets, code shared between platforms
+class UDPSocketBase : public BerkleySocket
+{
+public:
+ UDPSocketBase();
+
+public:
+ virtual SocketHandle Bind(BerkleyBindParameters* pBindParameters) = 0;
+ virtual int Send(const void* pData,
+ int bytes,
+ SockAddr* pSockAddr) = 0;
+ virtual void Poll(SocketEvent_UDP* eventHandler) = 0;
+
+protected:
+ virtual void OnRecv(SocketEvent_UDP* eventHandler,
+ uint8_t* pData,
+ int bytesRead,
+ SockAddr* address) = 0;
+};
+
+
+//-----------------------------------------------------------------------------
+// TCP Berkley socket
+
+// Base class for TCP sockets, code shared between platforms
+class TCPSocketBase : public BerkleySocket
+{
+public:
+ TCPSocketBase();
+ TCPSocketBase(SocketHandle handle);
+
+public:
+ virtual SocketHandle Bind(BerkleyBindParameters* pBindParameters) = 0;
+ virtual int Listen() = 0;
+ virtual int Connect(SockAddr* pSockAddr) = 0;
+ virtual int Send(const void* pData,
+ int bytes) = 0;
+protected:
+ virtual void OnRecv(SocketEvent_TCP* eventHandler,
+ uint8_t* pData,
+ int bytesRead) = 0;
+
+protected:
+ bool IsListenSocket; // Is the socket listening (acting as a server)?
+};
+
+
+}} // OVR::Net
+
+#endif
diff --git a/LibOVR/Src/Net/OVR_Unix_Socket.cpp b/LibOVR/Src/Net/OVR_Unix_Socket.cpp
new file mode 100644
index 0000000..6f2a678
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_Unix_Socket.cpp
@@ -0,0 +1,600 @@
+/************************************************************************************
+
+Filename : OVR_Unix_Socket.cpp
+Content : Berkley sockets networking implementation
+Created : July 1, 2014
+Authors : Kevin Jenkins
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "OVR_Unix_Socket.h"
+#include "../Kernel/OVR_Std.h"
+#include "../Kernel/OVR_Allocator.h"
+#include "../Kernel/OVR_Threads.h" // Thread::MSleep
+#include "../Kernel/OVR_Log.h"
+
+#include <errno.h>
+
+namespace OVR { namespace Net {
+
+//-----------------------------------------------------------------------------
+// BerkleySocket
+
+void BerkleySocket::Close()
+{
+ if (TheSocket != INVALID_SOCKET)
+ {
+ close(TheSocket);
+ TheSocket = INVALID_SOCKET;
+ }
+}
+
+SInt32 BerkleySocket::GetSockname(SockAddr *pSockAddrOut)
+{
+ struct sockaddr_in6 sa;
+ memset(&sa,0,sizeof(sa));
+ socklen_t size = sizeof(sa);
+ SInt32 i = getsockname(TheSocket, (sockaddr*)&sa, &size);
+ if (i>=0)
+ {
+ pSockAddrOut->Set(&sa);
+ }
+ return i;
+}
+
+
+//-----------------------------------------------------------------------------
+// BitStream overloads for SockAddr
+
+BitStream& operator<<(BitStream& out, SockAddr& in)
+{
+ out.WriteBits((const unsigned char*) &in.Addr6, sizeof(in.Addr6)*8, true);
+ return out;
+}
+
+BitStream& operator>>(BitStream& in, SockAddr& out)
+{
+ bool success = in.ReadBits((unsigned char*) &out.Addr6, sizeof(out.Addr6)*8, true);
+ OVR_ASSERT(success);
+ OVR_UNUSED(success);
+ return in;
+}
+
+
+//-----------------------------------------------------------------------------
+// SockAddr
+
+SockAddr::SockAddr()
+{
+}
+
+SockAddr::SockAddr(SockAddr* address)
+{
+ Set(&address->Addr6);
+}
+
+SockAddr::SockAddr(sockaddr_storage* storage)
+{
+ Set(storage);
+}
+
+SockAddr::SockAddr(sockaddr_in6* address)
+{
+ Set(address);
+}
+
+SockAddr::SockAddr(const char* hostAddress, UInt16 port, int sockType)
+{
+ Set(hostAddress, port, sockType);
+}
+
+void SockAddr::Set(const sockaddr_storage* storage)
+{
+ memcpy(&Addr6, storage, sizeof(Addr6));
+}
+
+void SockAddr::Set(const sockaddr_in6* address)
+{
+ memcpy(&Addr6, address, sizeof(Addr6));
+}
+
+void SockAddr::Set(const char* hostAddress, UInt16 port, int sockType)
+{
+ memset(&Addr6, 0, sizeof(Addr6));
+
+ struct addrinfo hints;
+
+ // make sure the struct is empty
+ memset(&hints, 0, sizeof (addrinfo));
+
+ hints.ai_socktype = sockType; // SOCK_DGRAM or SOCK_STREAM
+ hints.ai_flags = AI_PASSIVE; // fill in my IP for me
+ hints.ai_family = AF_UNSPEC ;
+
+ if (SOCK_DGRAM == sockType)
+ {
+ hints.ai_protocol = IPPROTO_UDP;
+ }
+ else if (SOCK_STREAM == sockType)
+ {
+ hints.ai_protocol = IPPROTO_TCP;
+ }
+
+ struct addrinfo* servinfo = NULL; // will point to the results
+
+ char portStr[32];
+ OVR_itoa(port, portStr, sizeof(portStr), 10);
+ int errcode = getaddrinfo(hostAddress, portStr, &hints, &servinfo);
+
+ if (0 != errcode)
+ {
+ OVR::LogError("getaddrinfo error: %s", gai_strerror(errcode));
+ }
+
+ OVR_ASSERT(servinfo);
+
+ if (servinfo)
+ {
+ memcpy(&Addr6, servinfo->ai_addr, sizeof(Addr6));
+
+ freeaddrinfo(servinfo);
+ }
+}
+
+UInt16 SockAddr::GetPort()
+{
+ return htons(Addr6.sin6_port);
+}
+
+String SockAddr::ToString(bool writePort, char portDelineator) const
+{
+ char dest[INET6_ADDRSTRLEN + 1];
+
+ int ret = getnameinfo((struct sockaddr*)&Addr6,
+ sizeof(struct sockaddr_in6),
+ dest,
+ INET6_ADDRSTRLEN,
+ NULL,
+ 0,
+ NI_NUMERICHOST);
+ if (ret != 0)
+ {
+ dest[0] = '\0';
+ }
+
+ if (writePort)
+ {
+ unsigned char ch[2];
+ ch[0]=portDelineator;
+ ch[1]=0;
+ OVR_strcat(dest, 16, (const char*) ch);
+ OVR_itoa(ntohs(Addr6.sin6_port), dest+strlen(dest), 16, 10);
+ }
+
+ return String(dest);
+}
+bool SockAddr::IsLocalhost() const
+{
+ static const unsigned char localhost_bytes[] =
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
+
+ return memcmp(Addr6.sin6_addr.s6_addr, localhost_bytes, 16) == 0;
+}
+bool SockAddr::operator==( const SockAddr& right ) const
+{
+ return memcmp(&Addr6, &right.Addr6, sizeof(Addr6)) == 0;
+}
+
+bool SockAddr::operator!=( const SockAddr& right ) const
+{
+ return !(*this == right);
+}
+
+bool SockAddr::operator>( const SockAddr& right ) const
+{
+ return memcmp(&Addr6, &right.Addr6, sizeof(Addr6)) > 0;
+}
+
+bool SockAddr::operator<( const SockAddr& right ) const
+{
+ return memcmp(&Addr6, &right.Addr6, sizeof(Addr6)) < 0;
+}
+
+
+// Returns true on success
+static bool SetSocketOptions(SocketHandle sock)
+{
+ bool failed = false;
+ int sock_opt;
+ int sockError = 0;
+
+ // This doubles the max throughput rate
+ sock_opt=1024*256;
+ sockError = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) );
+ if (sockError != 0)
+ {
+ int errsv = errno;
+ OVR::LogError("[Socket] Failed SO_RCVBUF setsockopt, errno: %d", errsv);
+ failed = true;
+ }
+
+ // This doesn't make much difference: 10% maybe
+ // Not supported on console 2
+ sock_opt=1024*16;
+ sockError = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) );
+ if (sockError != 0)
+ {
+ int errsv = errno;
+ OVR::LogError("[Socket] Failed SO_SNDBUF setsockopt, errno: %d", errsv);
+ failed = true;
+ }
+
+ // NOTE: This should be OVR_OS_BSD, not Mac.
+#ifdef OVR_OS_MAC
+ int value = 1;
+ sockError = setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value));
+ if (sockError != 0)
+ {
+ int errsv = errno;
+ OVR::LogError("[Socket] Failed SO_NOSIGPIPE setsockopt, errno: %d", errsv);
+ failed = true;
+ }
+#endif
+
+ // Reuse address is only needed for posix platforms, as it is the default
+ // on Windows platforms.
+ int optval = 1;
+ sockError = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int));
+ if (sockError != 0)
+ {
+ int errsv = errno;
+ OVR::LogError("[Socket] Failed SO_REUSEADDR setsockopt, errno: %d", errsv);
+ failed = true;
+ }
+
+ return !failed;
+}
+
+void _Ioctlsocket(SocketHandle sock, unsigned long nonblocking)
+{
+ int flags = fcntl(sock, F_GETFL, 0);
+ if (flags < 0) return; // return false
+ if (nonblocking == 0) { flags &= ~O_NONBLOCK; }
+ else { flags |= O_NONBLOCK; }
+ fcntl(sock, F_SETFL, flags);
+}
+
+static SocketHandle BindShared(int ai_family, int ai_socktype, BerkleyBindParameters *pBindParameters)
+{
+ SocketHandle sock;
+
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof (addrinfo)); // make sure the struct is empty
+ hints.ai_family = ai_family;
+ hints.ai_socktype = ai_socktype;
+ hints.ai_flags = AI_PASSIVE; // fill in my IP for me
+ struct addrinfo *servinfo=0, *aip; // will point to the results
+ char portStr[32];
+ OVR_itoa(pBindParameters->Port, portStr, sizeof(portStr), 10);
+
+ int errcode = 0;
+ if (!pBindParameters->Address.IsEmpty())
+ errcode = getaddrinfo(pBindParameters->Address.ToCStr(), portStr, &hints, &servinfo);
+ else
+ errcode = getaddrinfo(0, portStr, &hints, &servinfo);
+
+ if (0 != errcode)
+ {
+ OVR::LogError("getaddrinfo error: %s", gai_strerror(errcode));
+ }
+
+ for (aip = servinfo; aip != NULL; aip = aip->ai_next)
+ {
+ // Open socket. The address type depends on what
+ // getaddrinfo() gave us.
+ sock = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
+ if (sock != 0)
+ {
+ SetSocketOptions(sock);
+ int ret = bind( sock, aip->ai_addr, (int) aip->ai_addrlen );
+ if (ret>=0)
+ {
+ // The actual socket is always non-blocking
+ // I control blocking or not using WSAEventSelect
+ _Ioctlsocket(sock, 1);
+ freeaddrinfo(servinfo);
+ return sock;
+ }
+ else
+ {
+ close(sock);
+ }
+ }
+ }
+
+ if (servinfo) { freeaddrinfo(servinfo); }
+ return INVALID_SOCKET;
+}
+
+
+//-----------------------------------------------------------------------------
+// UDPSocket
+
+UDPSocket::UDPSocket()
+{
+ RecvBuf = new UByte[RecvBufSize];
+}
+
+UDPSocket::~UDPSocket()
+{
+ delete[] RecvBuf;
+}
+
+SocketHandle UDPSocket::Bind(BerkleyBindParameters *pBindParameters)
+{
+ SocketHandle s = BindShared(AF_INET6, SOCK_DGRAM, pBindParameters);
+ if (s < 0)
+ return s;
+
+ Close();
+ TheSocket = s;
+
+ return TheSocket;
+}
+
+void UDPSocket::OnRecv(SocketEvent_UDP* eventHandler, UByte* pData, int bytesRead, SockAddr* address)
+{
+ eventHandler->UDP_OnRecv(this, pData, bytesRead, address);
+}
+
+int UDPSocket::Send(const void* pData, int bytes, SockAddr* address)
+{
+ // NOTE: This should be OVR_OS_BSD
+#ifdef OVR_OS_MAC
+ int flags = 0;
+#else
+ int flags = MSG_NOSIGNAL;
+#endif
+
+ return (int)sendto(TheSocket, (const char*)pData, bytes, flags, (const sockaddr*)&address->Addr6, sizeof(address->Addr6));
+}
+
+void UDPSocket::Poll(SocketEvent_UDP *eventHandler)
+{
+ struct sockaddr_storage win32_addr;
+ socklen_t fromlen;
+ int bytesRead;
+
+ // FIXME: Implement blocking poll wait for UDP
+
+ // While some bytes are read,
+ while (fromlen = sizeof(win32_addr), // Must set fromlen each time
+ bytesRead = (int)recvfrom(TheSocket, (char*)RecvBuf, RecvBufSize, 0, (sockaddr*)&win32_addr, &fromlen),
+ bytesRead > 0)
+ {
+ SockAddr address(&win32_addr); // Wrap address
+
+ OnRecv(eventHandler, RecvBuf, bytesRead, &address);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// TCPSocket
+
+TCPSocket::TCPSocket()
+{
+ IsConnecting = false;
+ IsListenSocket = false;
+}
+TCPSocket::TCPSocket(SocketHandle boundHandle, bool isListenSocket)
+{
+ TheSocket = boundHandle;
+ IsListenSocket = isListenSocket;
+ IsConnecting = false;
+ SetSocketOptions(TheSocket);
+
+ // The actual socket is always non-blocking
+ _Ioctlsocket(TheSocket, 1);
+}
+
+TCPSocket::~TCPSocket()
+{
+}
+
+void TCPSocket::OnRecv(SocketEvent_TCP* eventHandler, UByte* pData, int bytesRead)
+{
+ eventHandler->TCP_OnRecv(this, pData, bytesRead);
+}
+
+SocketHandle TCPSocket::Bind(BerkleyBindParameters* pBindParameters)
+{
+ SocketHandle s = BindShared(AF_INET6, SOCK_STREAM, pBindParameters);
+ if (s < 0)
+ return s;
+
+ Close();
+
+ SetBlockingTimeout(pBindParameters->blockingTimeout);
+ TheSocket = s;
+
+ return TheSocket;
+}
+
+int TCPSocket::Listen()
+{
+ if (IsListenSocket)
+ {
+ return 0;
+ }
+
+ int i = listen(TheSocket, SOMAXCONN);
+ if (i >= 0)
+ {
+ IsListenSocket = true;
+ }
+
+ return i;
+}
+
+int TCPSocket::Connect(SockAddr* address)
+{
+ int retval;
+
+ retval = connect(TheSocket, (struct sockaddr *) &address->Addr6, sizeof(address->Addr6));
+ if (retval < 0)
+ {
+ int errsv = errno;
+ // EINPROGRESS should not be checked on windows but should
+ // be checked on POSIX platforms.
+ if (errsv == EWOULDBLOCK || errsv == EINPROGRESS)
+ {
+ IsConnecting = true;
+ return 0;
+ }
+
+ OVR::LogText( "TCPSocket::Connect failed:Error code - %d\n", errsv );
+ }
+
+ return retval;
+}
+
+int TCPSocket::Send(const void* pData, int bytes)
+{
+ if (bytes <= 0)
+ {
+ return 0;
+ }
+ else
+ {
+ return (int)send(TheSocket, (const char*)pData, bytes, 0);
+ }
+}
+
+
+//// TCPSocketPollState
+
+TCPSocketPollState::TCPSocketPollState()
+{
+ FD_ZERO(&readFD);
+ FD_ZERO(&exceptionFD);
+ FD_ZERO(&writeFD);
+ largestDescriptor = INVALID_SOCKET;
+}
+
+bool TCPSocketPollState::IsValid() const
+{
+ return largestDescriptor != INVALID_SOCKET;
+}
+
+void TCPSocketPollState::Add(TCPSocket* tcpSocket)
+{
+ if (!tcpSocket)
+ {
+ return;
+ }
+
+ SocketHandle handle = tcpSocket->GetSocketHandle();
+
+ if (handle == INVALID_SOCKET)
+ {
+ return;
+ }
+
+ if (largestDescriptor == INVALID_SOCKET ||
+ largestDescriptor < handle)
+ {
+ largestDescriptor = handle;
+ }
+
+ FD_SET(handle, &readFD);
+ FD_SET(handle, &exceptionFD);
+
+ if (tcpSocket->IsConnecting)
+ {
+ FD_SET(handle, &writeFD);
+ }
+}
+
+bool TCPSocketPollState::Poll(long usec, long seconds)
+{
+ timeval tv;
+ tv.tv_sec = seconds;
+ tv.tv_usec = (int)usec;
+
+ return select(largestDescriptor + 1, &readFD, &writeFD, &exceptionFD, &tv) > 0;
+}
+
+void TCPSocketPollState::HandleEvent(TCPSocket* tcpSocket, SocketEvent_TCP* eventHandler)
+{
+ if (!tcpSocket || !eventHandler)
+ {
+ return;
+ }
+
+ SocketHandle handle = tcpSocket->GetSocketHandle();
+
+ if (tcpSocket->IsConnecting && FD_ISSET(handle, &writeFD))
+ {
+ tcpSocket->IsConnecting = false;
+ eventHandler->TCP_OnConnected(tcpSocket);
+ }
+
+ if (FD_ISSET(handle, &readFD))
+ {
+ if (!tcpSocket->IsListenSocket)
+ {
+ static const int BUFF_SIZE = 8096;
+ char data[BUFF_SIZE];
+
+ int bytesRead = (int)recv(handle, data, BUFF_SIZE, 0);
+ if (bytesRead > 0)
+ {
+ tcpSocket->OnRecv(eventHandler, (UByte*)data, bytesRead);
+ }
+ else // Disconnection event:
+ {
+ tcpSocket->IsConnecting = false;
+ eventHandler->TCP_OnClosed(tcpSocket);
+ }
+ }
+ else
+ {
+ struct sockaddr_storage sockAddr;
+ socklen_t sockAddrSize = sizeof(sockAddr);
+
+ SocketHandle newSock = accept(handle, (sockaddr*)&sockAddr, (socklen_t*)&sockAddrSize);
+ if (newSock > 0)
+ {
+ SockAddr sa(&sockAddr);
+ eventHandler->TCP_OnAccept(tcpSocket, &sa, newSock);
+ }
+ }
+ }
+
+ if (FD_ISSET(handle, &exceptionFD))
+ {
+ tcpSocket->IsConnecting = false;
+ eventHandler->TCP_OnClosed(tcpSocket);
+ }
+}
+
+
+}} // namespace OVR::Net
diff --git a/LibOVR/Src/Net/OVR_Unix_Socket.h b/LibOVR/Src/Net/OVR_Unix_Socket.h
new file mode 100644
index 0000000..90ee515
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_Unix_Socket.h
@@ -0,0 +1,152 @@
+/************************************************************************************
+
+PublicHeader: n/a
+Filename : OVR_Unix_Socket.h
+Content : Berkley sockets networking implementation
+Created : July 1, 2014
+Authors : Kevin Jenkins
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Unix_Socket_h
+#define OVR_Unix_Socket_h
+
+#include "OVR_Socket.h"
+#include "OVR_BitStream.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <fcntl.h>
+
+namespace OVR { namespace Net {
+
+//-----------------------------------------------------------------------------
+// SockAddr
+
+// Abstraction for IPV6 socket address, with various convenience functions
+class SockAddr
+{
+public:
+ SockAddr();
+ SockAddr(SockAddr* sa);
+ SockAddr(sockaddr_storage* sa);
+ SockAddr(sockaddr_in6* sa);
+ SockAddr(const char* hostAddress, UInt16 port, int sockType);
+
+public:
+ void Set(const sockaddr_storage* sa);
+ void Set(const sockaddr_in6* sa);
+ void Set(const char* hostAddress, UInt16 port, int sockType); // SOCK_DGRAM or SOCK_STREAM
+
+ UInt16 GetPort();
+
+ String ToString(bool writePort, char portDelineator) const;
+
+ bool IsLocalhost() const;
+
+ void Serialize(BitStream* bs);
+ bool Deserialize(BitStream);
+
+ bool operator==( const SockAddr& right ) const;
+ bool operator!=( const SockAddr& right ) const;
+ bool operator >( const SockAddr& right ) const;
+ bool operator <( const SockAddr& right ) const;
+
+public:
+ sockaddr_in6 Addr6;
+};
+
+
+//-----------------------------------------------------------------------------
+// UDP Socket
+
+// Windows version of TCP socket
+class UDPSocket : public UDPSocketBase
+{
+public:
+ UDPSocket();
+ virtual ~UDPSocket();
+
+public:
+ virtual SocketHandle Bind(BerkleyBindParameters* pBindParameters);
+ virtual int Send(const void* pData, int bytes, SockAddr* address);
+ virtual void Poll(SocketEvent_UDP* eventHandler);
+
+protected:
+ static const int RecvBufSize = 1048576;
+ UByte* RecvBuf;
+
+ virtual void OnRecv(SocketEvent_UDP* eventHandler, UByte* pData,
+ int bytesRead, SockAddr* address);
+};
+
+
+//-----------------------------------------------------------------------------
+// TCP Socket
+
+// Windows version of TCP socket
+class TCPSocket : public TCPSocketBase
+{
+ friend class TCPSocketPollState;
+
+public:
+ TCPSocket();
+ TCPSocket(SocketHandle boundHandle, bool isListenSocket);
+ virtual ~TCPSocket();
+
+public:
+ virtual SocketHandle Bind(BerkleyBindParameters* pBindParameters);
+ virtual int Listen();
+ virtual int Connect(SockAddr* address);
+ virtual int Send(const void* pData, int bytes);
+
+protected:
+ virtual void OnRecv(SocketEvent_TCP* eventHandler, UByte* pData,
+ int bytesRead);
+
+public:
+ bool IsConnecting; // Is in the process of connecting?
+};
+
+
+//-----------------------------------------------------------------------------
+// TCPSocketPollState
+
+// Polls multiple blocking TCP sockets at once
+class TCPSocketPollState
+{
+ fd_set readFD, exceptionFD, writeFD;
+ SocketHandle largestDescriptor;
+
+public:
+ TCPSocketPollState();
+ bool IsValid() const;
+ void Add(TCPSocket* tcpSocket);
+ bool Poll(long usec = 30000, long seconds = 0);
+ void HandleEvent(TCPSocket* tcpSocket, SocketEvent_TCP* eventHandler);
+};
+
+
+}} // OVR::Net
+
+#endif
diff --git a/LibOVR/Src/Net/OVR_Win32_Socket.cpp b/LibOVR/Src/Net/OVR_Win32_Socket.cpp
new file mode 100644
index 0000000..edc6ade
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_Win32_Socket.cpp
@@ -0,0 +1,608 @@
+/************************************************************************************
+
+Filename : OVR_Win32_Socket.cpp
+Content : Windows-specific socket-based networking implementation
+Created : June 10, 2014
+Authors : Kevin Jenkins
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "OVR_Win32_Socket.h"
+#include "../Kernel/OVR_Std.h"
+#include "../Kernel/OVR_Allocator.h"
+#include "../Kernel/OVR_Threads.h" // Thread::MSleep
+#include "../Kernel/OVR_Log.h"
+
+#include <Winsock2.h>
+
+namespace OVR { namespace Net {
+
+
+//-----------------------------------------------------------------------------
+// WSAStartupSingleton
+
+class WSAStartupSingleton
+{
+public:
+ static void AddRef(void);
+ static void Deref(void);
+
+protected:
+ static int RefCount;
+};
+
+
+// Local data
+int WSAStartupSingleton::RefCount = 0;
+
+
+// Implementation
+void WSAStartupSingleton::AddRef()
+{
+ if (++RefCount == 1)
+ {
+ WSADATA winsockInfo;
+ const int errCode = WSAStartup(MAKEWORD(2, 2), &winsockInfo);
+ OVR_ASSERT(errCode == 0);
+
+ // If an error code is returned
+ if (errCode != 0)
+ {
+ LogError("{ERR-007w} [Socket] Unable to initialize Winsock %d", errCode);
+ }
+ }
+}
+
+void WSAStartupSingleton::Deref()
+{
+ OVR_ASSERT(RefCount > 0);
+
+ if (RefCount > 0)
+ {
+ if (--RefCount == 0)
+ {
+ WSACleanup();
+ RefCount = 0;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// BerkleySocket
+
+void BerkleySocket::Close()
+{
+ if (TheSocket != INVALID_SOCKET)
+ {
+ closesocket(TheSocket);
+ TheSocket = INVALID_SOCKET;
+ }
+}
+
+int32_t BerkleySocket::GetSockname(SockAddr *pSockAddrOut)
+{
+ struct sockaddr_in6 sa;
+ memset(&sa,0,sizeof(sa));
+ int size = sizeof(sa);
+ int32_t i = getsockname(TheSocket, (sockaddr*) &sa, &size);
+ if (i>=0)
+ {
+ pSockAddrOut->Set(&sa);
+ }
+ return i;
+}
+
+
+//-----------------------------------------------------------------------------
+// BitStream overloads for SockAddr
+
+BitStream& operator<<(BitStream& out, SockAddr& in)
+{
+ out.WriteBits((const unsigned char*) &in.Addr6, sizeof(in.Addr6)*8, true);
+ return out;
+}
+
+BitStream& operator>>(BitStream& in, SockAddr& out)
+{
+ bool success = in.ReadBits((unsigned char*) &out.Addr6, sizeof(out.Addr6)*8, true);
+ OVR_ASSERT(success);
+ OVR_UNUSED(success);
+ return in;
+}
+
+
+//-----------------------------------------------------------------------------
+// SockAddr
+
+SockAddr::SockAddr()
+{
+ WSAStartupSingleton::AddRef();
+
+ // Zero out the address to squelch static analysis tools
+ ZeroMemory(&Addr6, sizeof(Addr6));
+}
+
+SockAddr::SockAddr(SockAddr* address)
+{
+ WSAStartupSingleton::AddRef();
+ Set(&address->Addr6);
+}
+
+SockAddr::SockAddr(sockaddr_storage* storage)
+{
+ WSAStartupSingleton::AddRef();
+ Set(storage);
+}
+
+SockAddr::SockAddr(sockaddr_in6* address)
+{
+ WSAStartupSingleton::AddRef();
+ Set(address);
+}
+
+SockAddr::SockAddr(const char* hostAddress, uint16_t port, int sockType)
+{
+ WSAStartupSingleton::AddRef();
+ Set(hostAddress, port, sockType);
+}
+
+void SockAddr::Set(const sockaddr_storage* storage)
+{
+ memcpy(&Addr6, storage, sizeof(Addr6));
+}
+
+void SockAddr::Set(const sockaddr_in6* address)
+{
+ memcpy(&Addr6, address, sizeof(Addr6));
+}
+
+void SockAddr::Set(const char* hostAddress, uint16_t port, int sockType)
+{
+ memset(&Addr6, 0, sizeof(Addr6));
+
+ struct addrinfo hints;
+
+ // make sure the struct is empty
+ memset(&hints, 0, sizeof (addrinfo));
+
+ hints.ai_socktype = sockType; // SOCK_DGRAM or SOCK_STREAM
+ hints.ai_flags = AI_PASSIVE; // fill in my IP for me
+ hints.ai_family = AF_UNSPEC ;
+
+ // FIXME See OVR_Unix_Socket implementation and man pages for getaddrinfo.
+ // ai_protocol is expecting to be either IPPROTO_UDP and IPPROTO_TCP.
+ // But this has been working on windows so I'm leaving it be for
+ // now instead of introducing another variable.
+ hints.ai_protocol = IPPROTO_IPV6;
+
+ struct addrinfo* servinfo = NULL; // will point to the results
+
+ char portStr[32];
+ OVR_itoa(port, portStr, sizeof(portStr), 10);
+ int errcode = getaddrinfo(hostAddress, portStr, &hints, &servinfo);
+
+ if (0 != errcode)
+ {
+ OVR::LogError("{ERR-008w} getaddrinfo error: %s", gai_strerror(errcode));
+ }
+
+ OVR_ASSERT(servinfo);
+
+ if (servinfo)
+ {
+ memcpy(&Addr6, servinfo->ai_addr, sizeof(Addr6));
+
+ freeaddrinfo(servinfo);
+ }
+}
+
+uint16_t SockAddr::GetPort()
+{
+ return htons(Addr6.sin6_port);
+}
+
+String SockAddr::ToString(bool writePort, char portDelineator) const
+{
+ char dest[INET6_ADDRSTRLEN + 1];
+
+ int ret = getnameinfo((struct sockaddr*)&Addr6,
+ sizeof(struct sockaddr_in6),
+ dest,
+ INET6_ADDRSTRLEN,
+ NULL,
+ 0,
+ NI_NUMERICHOST);
+ if (ret != 0)
+ {
+ dest[0] = '\0';
+ }
+
+ if (writePort)
+ {
+ unsigned char ch[2];
+ ch[0]=portDelineator;
+ ch[1]=0;
+ OVR_strcat(dest, 16, (const char*) ch);
+ OVR_itoa(ntohs(Addr6.sin6_port), dest+strlen(dest), 16, 10);
+ }
+
+ return String(dest);
+}
+bool SockAddr::IsLocalhost() const
+{
+ static const unsigned char localhost_bytes[] =
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
+
+ return memcmp(Addr6.sin6_addr.s6_addr, localhost_bytes, 16) == 0;
+}
+bool SockAddr::operator==( const SockAddr& right ) const
+{
+ return memcmp(&Addr6, &right.Addr6, sizeof(Addr6)) == 0;
+}
+
+bool SockAddr::operator!=( const SockAddr& right ) const
+{
+ return !(*this == right);
+}
+
+bool SockAddr::operator>( const SockAddr& right ) const
+{
+ return memcmp(&Addr6, &right.Addr6, sizeof(Addr6)) > 0;
+}
+
+bool SockAddr::operator<( const SockAddr& right ) const
+{
+ return memcmp(&Addr6, &right.Addr6, sizeof(Addr6)) < 0;
+}
+
+static bool SetSocketOptions(SocketHandle sock)
+{
+ int result = 0;
+ int sock_opt;
+
+ // This doubles the max throughput rate
+ sock_opt = 1024 * 256;
+ result |= setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)& sock_opt, sizeof (sock_opt));
+
+ // Immediate hard close. Don't linger the socket, or recreating the socket quickly on Vista fails.
+ sock_opt = 0;
+ result |= setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *)& sock_opt, sizeof (sock_opt));
+
+ // This doesn't make much difference: 10% maybe
+ sock_opt = 1024 * 16;
+ result |= setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)& sock_opt, sizeof (sock_opt));
+
+ // If all the setsockopt() returned 0 there were no failures, so return true for success, else false
+ return result == 0;
+}
+
+void _Ioctlsocket(SocketHandle sock, unsigned long nonblocking)
+{
+ ioctlsocket(sock, FIONBIO, &nonblocking);
+}
+
+static SocketHandle BindShared(int ai_family, int ai_socktype, BerkleyBindParameters* pBindParameters)
+{
+ SocketHandle sock;
+
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof (addrinfo)); // make sure the struct is empty
+ hints.ai_family = ai_family;
+ hints.ai_socktype = ai_socktype;
+ hints.ai_flags = AI_PASSIVE; // fill in my IP for me
+ struct addrinfo *servinfo=0, *aip; // will point to the results
+ char portStr[32];
+ OVR_itoa(pBindParameters->Port, portStr, sizeof(portStr), 10);
+
+ int errcode = 0;
+ if (!pBindParameters->Address.IsEmpty())
+ errcode = getaddrinfo(pBindParameters->Address.ToCStr(), portStr, &hints, &servinfo);
+ else
+ errcode = getaddrinfo(0, portStr, &hints, &servinfo);
+
+ if (0 != errcode)
+ {
+ OVR::LogError("{ERR-020w} getaddrinfo error: %s", gai_strerror(errcode));
+ }
+
+ for (aip = servinfo; aip != NULL; aip = aip->ai_next)
+ {
+ // Open socket. The address type depends on what
+ // getaddrinfo() gave us.
+ sock = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
+ if (sock != INVALID_SOCKET)
+ {
+ if (bind(sock, aip->ai_addr, (int)aip->ai_addrlen) != SOCKET_ERROR)
+ {
+ // The actual socket is always non-blocking
+ // I control blocking or not using WSAEventSelect
+ _Ioctlsocket(sock, 1);
+ freeaddrinfo(servinfo);
+ return sock;
+ }
+
+ closesocket(sock);
+ }
+ }
+
+ if (servinfo) { freeaddrinfo(servinfo); }
+ return INVALID_SOCKET;
+}
+
+
+//-----------------------------------------------------------------------------
+// UDPSocket
+
+UDPSocket::UDPSocket()
+{
+ WSAStartupSingleton::AddRef();
+ RecvBuf = new uint8_t[RecvBufSize];
+}
+
+UDPSocket::~UDPSocket()
+{
+ WSAStartupSingleton::Deref();
+ delete[] RecvBuf;
+}
+
+SocketHandle UDPSocket::Bind(BerkleyBindParameters *pBindParameters)
+{
+ SocketHandle s = BindShared(AF_INET6, SOCK_DGRAM, pBindParameters);
+ if (s == INVALID_SOCKET)
+ return s;
+
+ Close();
+ TheSocket = s;
+ SetSocketOptions(TheSocket);
+
+ return TheSocket;
+}
+
+void UDPSocket::OnRecv(SocketEvent_UDP* eventHandler, uint8_t* pData, int bytesRead, SockAddr* address)
+{
+ eventHandler->UDP_OnRecv(this, pData, bytesRead, address);
+}
+
+int UDPSocket::Send(const void* pData, int bytes, SockAddr* address)
+{
+ return sendto(TheSocket, (const char*)pData, bytes, 0, (const sockaddr*)&address->Addr6, sizeof(address->Addr6));
+}
+
+void UDPSocket::Poll(SocketEvent_UDP *eventHandler)
+{
+ struct sockaddr_storage win32_addr;
+ socklen_t fromlen;
+ int bytesRead;
+
+ // FIXME: Implement blocking poll wait for UDP
+
+ // While some bytes are read,
+ while (fromlen = sizeof(win32_addr), // Must set fromlen each time
+ bytesRead = recvfrom(TheSocket, (char*)RecvBuf, RecvBufSize, 0, (sockaddr*)&win32_addr, &fromlen),
+ bytesRead > 0)
+ {
+ SockAddr address(&win32_addr); // Wrap address
+
+ OnRecv(eventHandler, RecvBuf, bytesRead, &address);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// TCPSocket
+
+TCPSocket::TCPSocket()
+{
+ IsConnecting = false;
+ IsListenSocket = false;
+ WSAStartupSingleton::AddRef();
+}
+TCPSocket::TCPSocket(SocketHandle boundHandle, bool isListenSocket)
+{
+ TheSocket = boundHandle;
+ IsListenSocket = isListenSocket;
+ IsConnecting = false;
+ WSAStartupSingleton::AddRef();
+ SetSocketOptions(TheSocket);
+
+ // The actual socket is always non-blocking
+ _Ioctlsocket(TheSocket, 1);
+}
+
+TCPSocket::~TCPSocket()
+{
+ WSAStartupSingleton::Deref();
+}
+
+void TCPSocket::OnRecv(SocketEvent_TCP* eventHandler, uint8_t* pData, int bytesRead)
+{
+ eventHandler->TCP_OnRecv(this, pData, bytesRead);
+}
+
+SocketHandle TCPSocket::Bind(BerkleyBindParameters* pBindParameters)
+{
+ SocketHandle s = BindShared(AF_INET6, SOCK_STREAM, pBindParameters);
+ if (s == INVALID_SOCKET)
+ return s;
+
+ Close();
+
+ SetBlockingTimeout(pBindParameters->blockingTimeout);
+ TheSocket = s;
+
+ SetSocketOptions(TheSocket);
+
+ return TheSocket;
+}
+
+int TCPSocket::Listen()
+{
+ if (IsListenSocket)
+ {
+ return 0;
+ }
+
+ int i = listen(TheSocket, SOMAXCONN);
+ if (i >= 0)
+ {
+ IsListenSocket = true;
+ }
+
+ return i;
+}
+
+int TCPSocket::Connect(SockAddr* address)
+{
+ int retval;
+
+ retval = connect(TheSocket, (struct sockaddr *) &address->Addr6, sizeof(address->Addr6));
+ if (retval < 0)
+ {
+ DWORD dwIOError = WSAGetLastError();
+ if (dwIOError == WSAEWOULDBLOCK)
+ {
+ IsConnecting = true;
+ return 0;
+ }
+
+ printf( "TCPSocket::Connect failed:Error code - %d\n", dwIOError );
+ }
+
+ return retval;
+}
+
+int TCPSocket::Send(const void* pData, int bytes)
+{
+ if (bytes <= 0)
+ {
+ return 0;
+ }
+ else
+ {
+ return send(TheSocket, (const char*)pData, bytes, 0);
+ }
+}
+
+
+//// TCPSocketPollState
+
+TCPSocketPollState::TCPSocketPollState()
+{
+ FD_ZERO(&readFD);
+ FD_ZERO(&exceptionFD);
+ FD_ZERO(&writeFD);
+ largestDescriptor = INVALID_SOCKET;
+}
+
+bool TCPSocketPollState::IsValid() const
+{
+ return largestDescriptor != INVALID_SOCKET;
+}
+
+void TCPSocketPollState::Add(TCPSocket* tcpSocket)
+{
+ if (!tcpSocket)
+ {
+ return;
+ }
+
+ SocketHandle handle = tcpSocket->GetSocketHandle();
+
+ if (largestDescriptor == INVALID_SOCKET ||
+ largestDescriptor < handle)
+ {
+ largestDescriptor = handle;
+ }
+
+ FD_SET(handle, &readFD);
+ FD_SET(handle, &exceptionFD);
+
+ if (tcpSocket->IsConnecting)
+ {
+ FD_SET(handle, &writeFD);
+ }
+}
+
+bool TCPSocketPollState::Poll(long usec, long seconds)
+{
+ timeval tv;
+ tv.tv_sec = seconds;
+ tv.tv_usec = usec;
+
+ return (int)select((int)largestDescriptor + 1, &readFD, &writeFD, &exceptionFD, &tv) > 0;
+}
+
+void TCPSocketPollState::HandleEvent(TCPSocket* tcpSocket, SocketEvent_TCP* eventHandler)
+{
+ if (!tcpSocket || !eventHandler)
+ {
+ return;
+ }
+
+ SocketHandle handle = tcpSocket->GetSocketHandle();
+
+ if (tcpSocket->IsConnecting && FD_ISSET(handle, &writeFD))
+ {
+ tcpSocket->IsConnecting = false;
+ eventHandler->TCP_OnConnected(tcpSocket);
+ }
+
+ if (FD_ISSET(handle, &readFD))
+ {
+ if (!tcpSocket->IsListenSocket)
+ {
+ static const int BUFF_SIZE = 8096;
+ char data[BUFF_SIZE];
+
+ int bytesRead = recv(handle, data, BUFF_SIZE, 0);
+ if (bytesRead > 0)
+ {
+ tcpSocket->OnRecv(eventHandler, (uint8_t*)data, bytesRead);
+ }
+ else // Disconnection event:
+ {
+ tcpSocket->IsConnecting = false;
+ eventHandler->TCP_OnClosed(tcpSocket);
+ }
+ }
+ else
+ {
+ struct sockaddr_storage sockAddr;
+ socklen_t sockAddrSize = sizeof(sockAddr);
+
+ SocketHandle newSock = accept(handle, (sockaddr*)&sockAddr, (socklen_t*)&sockAddrSize);
+ if (newSock != INVALID_SOCKET)
+ {
+ SockAddr sa(&sockAddr);
+ eventHandler->TCP_OnAccept(tcpSocket, &sa, newSock);
+ }
+ }
+ }
+
+ if (FD_ISSET(handle, &exceptionFD))
+ {
+ tcpSocket->IsConnecting = false;
+ eventHandler->TCP_OnClosed(tcpSocket);
+ }
+}
+
+
+}} // namespace OVR::Net
diff --git a/LibOVR/Src/Net/OVR_Win32_Socket.h b/LibOVR/Src/Net/OVR_Win32_Socket.h
new file mode 100644
index 0000000..ac66869
--- /dev/null
+++ b/LibOVR/Src/Net/OVR_Win32_Socket.h
@@ -0,0 +1,151 @@
+/************************************************************************************
+
+PublicHeader: n/a
+Filename : OVR_Win32_Socket.h
+Content : Windows-specific socket-based networking implementation
+Created : June 10, 2014
+Authors : Kevin Jenkins
+
+Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.2
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Win32_Socket_h
+#define OVR_Win32_Socket_h
+
+#include "OVR_Socket.h"
+#include "OVR_BitStream.h"
+
+#include <WinSock2.h>
+#include <WS2tcpip.h>
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <io.h>
+
+namespace OVR { namespace Net {
+
+
+//-----------------------------------------------------------------------------
+// SockAddr
+
+// Abstraction for IPV6 socket address, with various convenience functions
+class SockAddr
+{
+public:
+ SockAddr();
+ SockAddr(SockAddr* sa);
+ SockAddr(sockaddr_storage* sa);
+ SockAddr(sockaddr_in6* sa);
+ SockAddr(const char* hostAddress, uint16_t port, int sockType);
+
+public:
+ void Set(const sockaddr_storage* sa);
+ void Set(const sockaddr_in6* sa);
+ void Set(const char* hostAddress, uint16_t port, int sockType); // SOCK_DGRAM or SOCK_STREAM
+
+ uint16_t GetPort();
+
+ String ToString(bool writePort, char portDelineator) const;
+ bool IsLocalhost() const;
+
+ void Serialize(BitStream* bs);
+ bool Deserialize(BitStream);
+
+ bool operator==( const SockAddr& right ) const;
+ bool operator!=( const SockAddr& right ) const;
+ bool operator >( const SockAddr& right ) const;
+ bool operator <( const SockAddr& right ) const;
+
+public:
+ sockaddr_in6 Addr6;
+};
+
+
+//-----------------------------------------------------------------------------
+// UDP Socket
+
+// Windows version of TCP socket
+class UDPSocket : public UDPSocketBase
+{
+public:
+ UDPSocket();
+ virtual ~UDPSocket();
+
+public:
+ virtual SocketHandle Bind(BerkleyBindParameters* pBindParameters);
+ virtual int Send(const void* pData, int bytes, SockAddr* address);
+ virtual void Poll(SocketEvent_UDP* eventHandler);
+
+protected:
+ static const int RecvBufSize = 1048576;
+ uint8_t* RecvBuf;
+
+ virtual void OnRecv(SocketEvent_UDP* eventHandler, uint8_t* pData,
+ int bytesRead, SockAddr* address);
+};
+
+
+//-----------------------------------------------------------------------------
+// TCP Socket
+
+// Windows version of TCP socket
+class TCPSocket : public TCPSocketBase
+{
+ friend class TCPSocketPollState;
+
+public:
+ TCPSocket();
+ TCPSocket(SocketHandle boundHandle, bool isListenSocket);
+ virtual ~TCPSocket();
+
+public:
+ virtual SocketHandle Bind(BerkleyBindParameters* pBindParameters);
+ virtual int Listen();
+ virtual int Connect(SockAddr* address);
+ virtual int Send(const void* pData, int bytes);
+
+protected:
+ virtual void OnRecv(SocketEvent_TCP* eventHandler, uint8_t* pData,
+ int bytesRead);
+
+public:
+ bool IsConnecting; // Is in the process of connecting?
+};
+
+
+//-----------------------------------------------------------------------------
+// TCPSocketPollState
+
+// Polls multiple blocking TCP sockets at once
+class TCPSocketPollState
+{
+ fd_set readFD, exceptionFD, writeFD;
+ SocketHandle largestDescriptor;
+
+public:
+ TCPSocketPollState();
+ bool IsValid() const;
+ void Add(TCPSocket* tcpSocket);
+ bool Poll(long usec = 30000, long seconds = 0);
+ void HandleEvent(TCPSocket* tcpSocket, SocketEvent_TCP* eventHandler);
+};
+
+
+}} // OVR::Net
+
+#endif