Engine directory for ticket #1

This commit is contained in:
DavidWyand-GG 2012-09-19 11:15:01 -04:00
parent 352279af7a
commit 7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions

View file

@ -0,0 +1,182 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _BITMATRIX_H_
#define _BITMATRIX_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _BITVECTOR_H_
#include "core/bitVector.h"
#endif
/// A matrix of bits.
///
/// This class manages an array of bits. There are no limitations on the
/// size of the bit matrix (beyond available memory).
///
/// @note This class is currently unused.
class BitMatrix
{
U32 mWidth;
U32 mHeight;
U32 mRowByteWidth;
U8* mBits;
U32 mSize;
BitVector mColFlags;
BitVector mRowFlags;
public:
/// Create a new bit matrix.
///
/// @param width Width of matrix in bits.
/// @param height Height of matrix in bits.
BitMatrix(const U32 width, const U32 height);
~BitMatrix();
/// @name Setters
/// @{
/// Set all the bits in the matrix to false.
void clearAllBits();
/// Set all the bits in the matrix to true.
void setAllBits();
/// Set a bit at a given location in the matrix.
void setBit(const U32 x, const U32 y);
/// Clear a bit at a given location in the matrix.
void clearBit(const U32 x, const U32 y);
/// @}
/// @name Queries
/// @{
/// Is the specified bit set?
bool isSet(const U32 x, const U32 y) const;
/// Is any bit in the given column set?
bool isAnySetCol(const U32 x);
/// Is any bit in the given row set?
bool isAnySetRow(const U32 y);
/// @}
};
inline BitMatrix::BitMatrix(const U32 width, const U32 height)
: mColFlags(width),
mRowFlags(height)
{
AssertFatal(width != 0 && height != 0, "Error, w/h must be non-zero");
mWidth = width;
mHeight = height;
mRowByteWidth = (width + 7) >> 3;
mSize = mRowByteWidth * mHeight;
mBits = new U8[mSize];
}
inline BitMatrix::~BitMatrix()
{
mWidth = 0;
mHeight = 0;
mRowByteWidth = 0;
mSize = 0;
delete [] mBits;
mBits = NULL;
}
inline void BitMatrix::clearAllBits()
{
AssertFatal(mBits != NULL, "Error, clearing after deletion");
dMemset(mBits, 0x00, mSize);
mColFlags.clear();
mRowFlags.clear();
}
inline void BitMatrix::setAllBits()
{
AssertFatal(mBits != NULL, "Error, setting after deletion");
dMemset(mBits, 0xFF, mSize);
mColFlags.set();
mRowFlags.set();
}
inline void BitMatrix::setBit(const U32 x, const U32 y)
{
AssertFatal(x < mWidth && y < mHeight, "Error, out of bounds bit!");
U8* pRow = &mBits[y * mRowByteWidth];
U8* pByte = &pRow[x >> 3];
*pByte |= 1 << (x & 0x7);
mColFlags.set(x);
mRowFlags.set(y);
}
inline void BitMatrix::clearBit(const U32 x, const U32 y)
{
AssertFatal(x < mWidth && y < mHeight, "Error, out of bounds bit!");
U8* pRow = &mBits[y * mRowByteWidth];
U8* pByte = &pRow[x >> 3];
*pByte &= ~(1 << (x & 0x7));
}
inline bool BitMatrix::isSet(const U32 x, const U32 y) const
{
AssertFatal(x < mWidth && y < mHeight, "Error, out of bounds bit!");
U8* pRow = &mBits[y * mRowByteWidth];
U8* pByte = &pRow[x >> 3];
return (*pByte & (1 << (x & 0x7))) != 0;
}
inline bool BitMatrix::isAnySetCol(const U32 x)
{
AssertFatal(x < mWidth, "Error, out of bounds column!");
return mColFlags.test(x);
}
inline bool BitMatrix::isAnySetRow(const U32 y)
{
AssertFatal(y < mHeight, "Error, out of bounds row!");
return mRowFlags.test(y);
}
#endif // _H_BITMATRIX_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,59 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _BITRENDER_H_
#define _BITRENDER_H_
#ifndef _MMATH_H_
#include "math/mMath.h"
#endif
/// Functions for rendering to 1bpp bitmaps.
///
/// Used primarily for fast shadow rendering.
struct BitRender
{
/// Render a triangle to a bitmap of 1-bit per pixel and size dim X dim.
static void render(const Point2I *, const Point2I *, const Point2I *, S32 dim, U32 * bits);
/// Render a number of triangle strips to 1-bit per pixel bmp of size dim by dim.
static void render_strips(const U8 * draw, S32 numDraw, S32 szDraw, const U16 * indices, const Point2I * points, S32 dim, U32 * bits);
/// Render a number of triangles to 1-bit per pixel bmp of size dim by dim.
static void render_tris(const U8 * draw, S32 numDraw, S32 szDraw, const U16 * indices, const Point2I * points, S32 dim, U32 * bits);
/// @name Render Bits
/// These are used to convert a 1bpp bitmap to an 8bpp bitmap.
///
/// @see Shadow::endRenderToBitmap
/// @{
/// Render bits to the bitmap.
static void bitTo8Bit(U32 * bits, U32 * eightBits, S32 dim);
/// Render bits to the bitmap, with gaussian pass.
static void bitTo8Bit_3(U32 * bits, U32 * eightBits, S32 dim);
/// @}
};
#endif // _BIT_RENDER_H_

View file

@ -0,0 +1,88 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _BITSET_H_
#define _BITSET_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
/// A convenience class to manipulate a set of bits.
///
/// Notice that bits are accessed directly, ie, by passing
/// a variable with the relevant bit set or not, instead of
/// passing the index of the relevant bit.
class BitSet32
{
private:
/// Internal representation of bitset.
U32 mbits;
public:
BitSet32() { mbits = 0; }
BitSet32(const BitSet32& in_rCopy) { mbits = in_rCopy.mbits; }
BitSet32(const U32 in_mask) { mbits = in_mask; }
operator U32() const { return mbits; }
U32 getMask() const { return mbits; }
/// Set all bits to true.
void set() { mbits = 0xFFFFFFFFUL; }
/// Set the specified bit(s) to true.
void set(const U32 m) { mbits |= m; }
/// Masked-set the bitset; ie, using s as the mask and then setting the masked bits
/// to b.
void set(BitSet32 s, bool b) { mbits = (mbits&~(s.mbits))|(b?s.mbits:0); }
/// Clear all bits.
void clear() { mbits = 0; }
/// Clear the specified bit(s).
void clear(const U32 m) { mbits &= ~m; }
/// Toggle the specified bit(s).
void toggle(const U32 m) { mbits ^= m; }
/// Are any of the specified bit(s) set?
bool test(const U32 m) const { return (mbits & m) != 0; }
/// Are ALL the specified bit(s) set?
bool testStrict(const U32 m) const { return (mbits & m) == m; }
/// @name Operator Overloads
/// @{
BitSet32& operator =(const U32 m) { mbits = m; return *this; }
BitSet32& operator|=(const U32 m) { mbits |= m; return *this; }
BitSet32& operator&=(const U32 m) { mbits &= m; return *this; }
BitSet32& operator^=(const U32 m) { mbits ^= m; return *this; }
BitSet32 operator|(const U32 m) const { return BitSet32(mbits | m); }
BitSet32 operator&(const U32 m) const { return BitSet32(mbits & m); }
BitSet32 operator^(const U32 m) const { return BitSet32(mbits ^ m); }
/// @}
};
#endif //_NBITSET_H_

View file

@ -0,0 +1,116 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/bitVector.h"
void BitVector::_resize( U32 sizeInBits, bool copyBits )
{
if ( sizeInBits != 0 )
{
U32 newSize = calcByteSize( sizeInBits );
if ( mByteSize < newSize )
{
U8 *newBits = new U8[newSize];
if( copyBits )
dMemcpy( newBits, mBits, mByteSize );
delete [] mBits;
mBits = newBits;
mByteSize = newSize;
}
}
else
{
delete [] mBits;
mBits = NULL;
mByteSize = 0;
}
mSize = sizeInBits;
}
void BitVector::combineOR( const BitVector &other )
{
AssertFatal( mSize == other.mSize, "BitVector::combineOR - Vectors differ in size!" );
for ( U32 i=0; i < mSize; i++ )
{
bool b = test(i) | other.test(i);
set( i, b );
}
}
bool BitVector::_test( const BitVector& vector, bool all ) const
{
AssertFatal( mByteSize == vector.mByteSize, "BitVector::_test - Vectors differ in size!" );
AssertFatal( mByteSize % 4 == 0, "BitVector::_test - Vector not DWORD aligned!" );
const U32 numDWORDS = mByteSize / 4;
const U32* bits1 = reinterpret_cast< const U32* >( mBits );
const U32* bits2 = reinterpret_cast< const U32* >( vector.mBits );
for( U32 i = 0; i < numDWORDS; ++ i )
{
if( !( bits1[ i ] & bits2[ i ] ) )
continue;
else if( bits2[ i ] && all )
return false;
else
return true;
}
return false;
}
bool BitVector::testAll() const
{
const U32 remaider = mSize % 8;
const U32 testBytes = mSize / 8;
for ( U32 i=0; i < testBytes; i++ )
if ( mBits[i] != 0xFF )
return false;
if ( remaider == 0 )
return true;
const U8 mask = (U8)0xFF >> ( 8 - remaider );
return ( mBits[testBytes] & mask ) == mask;
}
bool BitVector::testAllClear() const
{
const U32 remaider = mSize % 8;
const U32 testBytes = mSize / 8;
for ( U32 i=0; i < testBytes; i++ )
if ( mBits[i] != 0 )
return false;
if ( remaider == 0 )
return true;
const U8 mask = (U8)0xFF >> ( 8 - remaider );
return ( mBits[testBytes] & mask ) == 0;
}

View file

@ -0,0 +1,220 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _BITVECTOR_H_
#define _BITVECTOR_H_
/// Manage a vector of bits of arbitrary size.
class BitVector
{
protected:
/// The array of bytes that stores our bits.
U8* mBits;
/// The allocated size of the bit array.
U32 mByteSize;
/// The size of the vector in bits.
U32 mSize;
/// Returns a size in bytes which is 32bit aligned
/// and can hold all the requested bits.
static U32 calcByteSize( const U32 numBits );
/// Internal function which resizes the bit array.
void _resize( U32 sizeInBits, bool copyBits );
bool _test( const BitVector& vector, bool all ) const;
public:
/// Default constructor which creates an bit
/// vector with a bit size of zero.
BitVector();
/// Constructs a bit vector with the desired size.
/// @note The resulting vector is not cleared.
BitVector( U32 sizeInBits );
/// Destructor.
~BitVector();
/// @name Size Management
/// @{
/// Return true if the bit vector is empty.
bool empty() const { return ( mSize == 0 ); }
/// Resizes the bit vector.
/// @note The new bits in the vector are not cleared and
/// contain random garbage bits.
void setSize( U32 sizeInBits );
/// Returns the size in bits.
U32 getSize() const { return mSize; }
/// Returns the 32bit aligned size in bytes.
U32 getByteSize() const { return mByteSize; }
/// Returns the bits.
const U8* getBits() const { return mBits; }
U8* getBits() { return mBits; }
/// @}
/// Copy the content of another bit vector.
void copy( const BitVector &from );
/// @name Mutators
/// Note that bits are specified by index, unlike BitSet32.
/// @{
/// Set the specified bit.
void set(U32 bit);
/// Set the specified bit on or off.
void set(U32 bit, bool on );
/// Set all the bits.
void set();
/// Clear the specified bit.
void clear(U32 bit);
/// Clear all the bits.
void clear();
/// Does an OR operation between BitVectors.
void combineOR( const BitVector &other );
/// Test that the specified bit is set.
bool test(U32 bit) const;
/// Test this vector's bits against all the corresponding bits
/// in @a vector and return true if any of the bits that are
/// set in @a vector are also set in this vector.
///
/// @param vector Bit vector of the same size.
bool testAny( const BitVector& vector ) const { return _test( vector, false ); }
/// Test this vector's bits against all the corresponding bits
/// in @a vector and return true if all of the bits that are
/// set in @a vector are also set in this vector.
///
/// @param vector Bit vector of the same size.
bool testAll( const BitVector& vector ) const { return _test( vector, true ); }
/// Return true if all bits are set.
bool testAll() const;
/// Return true if all bits are clear.
bool testAllClear() const;
/// @}
};
inline BitVector::BitVector()
{
mBits = NULL;
mByteSize = 0;
mSize = 0;
}
inline BitVector::BitVector( U32 sizeInBits )
{
mBits = NULL;
mByteSize = 0;
mSize = 0;
setSize( sizeInBits );
}
inline BitVector::~BitVector()
{
delete [] mBits;
mBits = NULL;
mByteSize = 0;
mSize = 0;
}
inline U32 BitVector::calcByteSize( U32 numBits )
{
// Make sure that we are 32 bit aligned
return (((numBits + 0x7) >> 3) + 0x3) & ~0x3;
}
inline void BitVector::setSize( const U32 sizeInBits )
{
_resize( sizeInBits, true );
}
inline void BitVector::clear()
{
if (mSize != 0)
dMemset( mBits, 0x00, getByteSize() );
}
inline void BitVector::copy( const BitVector &from )
{
_resize( from.getSize(), false );
if (mSize != 0)
dMemcpy( mBits, from.getBits(), getByteSize() );
}
inline void BitVector::set()
{
if (mSize != 0)
dMemset(mBits, 0xFF, getByteSize() );
}
inline void BitVector::set(U32 bit)
{
AssertFatal(bit < mSize, "BitVector::set - Error, out of range bit!");
mBits[bit >> 3] |= U8(1 << (bit & 0x7));
}
inline void BitVector::set(U32 bit, bool on )
{
AssertFatal(bit < mSize, "BitVector::set - Error, out of range bit!");
if ( on )
mBits[bit >> 3] |= U8(1 << (bit & 0x7));
else
mBits[bit >> 3] &= U8(~(1 << (bit & 0x7)));
}
inline void BitVector::clear(U32 bit)
{
AssertFatal(bit < mSize, "BitVector::clear - Error, out of range bit!");
mBits[bit >> 3] &= U8(~(1 << (bit & 0x7)));
}
inline bool BitVector::test(U32 bit) const
{
AssertFatal(bit < mSize, "BitVector::test - Error, out of range bit!");
return (mBits[bit >> 3] & U8(1 << (bit & 0x7))) != 0;
}
#endif //_BITVECTOR_H_

View file

@ -0,0 +1,108 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _BITVECTORW_H_
#define _BITVECTORW_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
/// @see BitVector
class BitVectorW
{
U32 mNumEntries;
U32 mBitWidth;
U32 mBitMask;
U8 * mDataPtr;
public:
BitVectorW() {mDataPtr=NULL; setDims(0,0);}
~BitVectorW() {if(mDataPtr) delete [] mDataPtr;}
U32 bitWidth() const {return mBitWidth;}
U32 numEntries() const {return mNumEntries;}
U8 * dataPtr() const {return mDataPtr;}
U32 getU17(U32 idx) const; // get and set for bit widths
void setU17(U32 idx, U32 val); // of 17 or less
U32 numBytes() const;
void setDims(U32 sz, U32 w);
};
//-------------------------------------------------------------------------------------
inline U32 BitVectorW::numBytes() const
{
if (mNumEntries > 0)
return (mBitWidth * mNumEntries) + 32 >> 3;
else
return 0;
}
// Alloc the data - note it does work for a bit width of zero (lookups return zero)
inline void BitVectorW::setDims(U32 size, U32 width)
{
if (mDataPtr)
delete [] mDataPtr;
if (size > 0 && width <= 17)
{
mBitWidth = width;
mNumEntries = size;
mBitMask = (1 << width) - 1;
U32 dataSize = numBytes();
mDataPtr = new U8 [dataSize];
dMemset(mDataPtr, 0, dataSize);
}
else
{
mDataPtr = NULL;
mBitWidth = mBitMask = mNumEntries = 0;
}
}
//-------------------------------------------------------------------------------------
// For coding ease, the get and set methods might read or write an extra byte or two.
// If more or less max bit width is ever needed, add or remove the x[] expressions.
inline U32 BitVectorW::getU17(U32 i) const
{
if (mDataPtr) {
register U8 * x = &mDataPtr[(i *= mBitWidth) >> 3];
return (U32(*x) + (U32(x[1])<<8) + (U32(x[2])<<16) >> (i&7)) & mBitMask;
}
return 0;
}
inline void BitVectorW::setU17(U32 i, U32 value)
{
if (mDataPtr) {
register U8 * x = &mDataPtr[(i *= mBitWidth) >> 3];
register U32 mask = mBitMask << (i &= 7);
x[0] = (x[0] & (~mask >> 0)) | ((value <<= i) & (mask >> 0));
x[1] = (x[1] & (~mask >> 8)) | ((value >> 8) & (mask >> 8));
x[2] = (x[2] & (~mask >> 16)) | ((value >> 16) & (mask >> 16));
}
}
#endif //_BITVECTORW_H_

View file

@ -0,0 +1,40 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/color.h"
const ColorF ColorF::ZERO( 0, 0, 0, 0 );
const ColorF ColorF::ONE( 1, 1, 1, 1 );
const ColorF ColorF::WHITE( 1, 1, 1 );
const ColorF ColorF::BLACK( 0, 0, 0 );
const ColorF ColorF::RED( 1, 0, 0 );
const ColorF ColorF::GREEN( 0, 1, 0 );
const ColorF ColorF::BLUE( 0, 0, 1 );
const ColorI ColorI::ZERO( 0, 0, 0, 0 );
const ColorI ColorI::ONE( 255, 255, 255, 255 );
const ColorI ColorI::WHITE( 255, 255, 255 );
const ColorI ColorI::BLACK( 0, 0, 0 );
const ColorI ColorI::RED( 255, 0, 0 );
const ColorI ColorI::GREEN( 0, 255, 0 );
const ColorI ColorI::BLUE( 0, 0, 255 );

608
Engine/source/core/color.h Normal file
View file

@ -0,0 +1,608 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _COLOR_H_
#define _COLOR_H_
#ifndef _MPOINT3_H_
#include "math/mPoint3.h"
#endif
#ifndef _MPOINT4_H_
#include "math/mPoint4.h"
#endif
class ColorI;
class ColorF
{
public:
F32 red;
F32 green;
F32 blue;
F32 alpha;
public:
ColorF() { }
ColorF(const ColorF& in_rCopy);
ColorF(const F32 in_r,
const F32 in_g,
const F32 in_b,
const F32 in_a = 1.0f);
void set(const F32 in_r,
const F32 in_g,
const F32 in_b,
const F32 in_a = 1.0f);
ColorF& operator*=(const ColorF& in_mul); // Can be useful for lighting
ColorF operator*(const ColorF& in_mul) const;
ColorF& operator+=(const ColorF& in_rAdd);
ColorF operator+(const ColorF& in_rAdd) const;
ColorF& operator-=(const ColorF& in_rSub);
ColorF operator-(const ColorF& in_rSub) const;
ColorF& operator*=(const F32 in_mul);
ColorF operator*(const F32 in_mul) const;
ColorF& operator/=(const F32 in_div);
ColorF operator/(const F32 in_div) const;
ColorF operator-() const;
bool operator==(const ColorF&) const;
bool operator!=(const ColorF&) const;
operator F32*() { return &red; }
operator const F32*() const { return &red; }
operator Point3F() const { return Point3F( red, green, blue ); }
operator Point4F() const { return Point4F( red, green, blue, alpha ); }
U32 getARGBPack() const;
U32 getRGBAPack() const;
U32 getABGRPack() const;
operator ColorI() const;
void interpolate(const ColorF& in_rC1,
const ColorF& in_rC2,
const F32 in_factor);
bool isValidColor() const { return (red >= 0.0f && red <= 1.0f) &&
(green >= 0.0f && green <= 1.0f) &&
(blue >= 0.0f && blue <= 1.0f) &&
(alpha >= 0.0f && alpha <= 1.0f); }
void clamp();
static const ColorF ZERO;
static const ColorF ONE;
static const ColorF WHITE;
static const ColorF BLACK;
static const ColorF RED;
static const ColorF GREEN;
static const ColorF BLUE;
};
//-------------------------------------- ColorI's are missing some of the operations
// present in ColorF since they cannot recover
// properly from over/underflow.
class ColorI
{
public:
U8 red;
U8 green;
U8 blue;
U8 alpha;
public:
ColorI() { }
ColorI(const ColorI& in_rCopy);
ColorI(const U8 in_r,
const U8 in_g,
const U8 in_b,
const U8 in_a = U8(255));
ColorI(const ColorI& in_rCopy, const U8 in_a);
void set(const U8 in_r,
const U8 in_g,
const U8 in_b,
const U8 in_a = U8(255));
void set(const ColorI& in_rCopy,
const U8 in_a);
ColorI& operator*=(const F32 in_mul);
ColorI operator*(const F32 in_mul) const;
ColorI operator+(const ColorI& in_rAdd) const;
ColorI& operator+=(const ColorI& in_rAdd);
ColorI& operator*=(const S32 in_mul);
ColorI& operator/=(const S32 in_mul);
ColorI operator*(const S32 in_mul) const;
ColorI operator/(const S32 in_mul) const;
bool operator==(const ColorI&) const;
bool operator!=(const ColorI&) const;
void interpolate(const ColorI& in_rC1,
const ColorI& in_rC2,
const F32 in_factor);
U32 getARGBPack() const;
U32 getRGBAPack() const;
U32 getABGRPack() const;
U32 getBGRPack() const;
U32 getRGBPack() const;
U32 getRGBEndian() const;
U32 getARGBEndian() const;
U16 get565() const;
U16 get4444() const;
operator ColorF() const;
operator const U8*() const { return &red; }
static const ColorI ZERO;
static const ColorI ONE;
static const ColorI WHITE;
static const ColorI BLACK;
static const ColorI RED;
static const ColorI GREEN;
static const ColorI BLUE;
};
//------------------------------------------------------------------------------
//-------------------------------------- INLINES (ColorF)
//
inline void ColorF::set(const F32 in_r,
const F32 in_g,
const F32 in_b,
const F32 in_a)
{
red = in_r;
green = in_g;
blue = in_b;
alpha = in_a;
}
inline ColorF::ColorF(const ColorF& in_rCopy)
{
red = in_rCopy.red;
green = in_rCopy.green;
blue = in_rCopy.blue;
alpha = in_rCopy.alpha;
}
inline ColorF::ColorF(const F32 in_r,
const F32 in_g,
const F32 in_b,
const F32 in_a)
{
set(in_r, in_g, in_b, in_a);
}
inline ColorF& ColorF::operator*=(const ColorF& in_mul)
{
red *= in_mul.red;
green *= in_mul.green;
blue *= in_mul.blue;
alpha *= in_mul.alpha;
return *this;
}
inline ColorF ColorF::operator*(const ColorF& in_mul) const
{
return ColorF(red * in_mul.red,
green * in_mul.green,
blue * in_mul.blue,
alpha * in_mul.alpha);
}
inline ColorF& ColorF::operator+=(const ColorF& in_rAdd)
{
red += in_rAdd.red;
green += in_rAdd.green;
blue += in_rAdd.blue;
alpha += in_rAdd.alpha;
return *this;
}
inline ColorF ColorF::operator+(const ColorF& in_rAdd) const
{
return ColorF(red + in_rAdd.red,
green + in_rAdd.green,
blue + in_rAdd.blue,
alpha + in_rAdd.alpha);
}
inline ColorF& ColorF::operator-=(const ColorF& in_rSub)
{
red -= in_rSub.red;
green -= in_rSub.green;
blue -= in_rSub.blue;
alpha -= in_rSub.alpha;
return *this;
}
inline ColorF ColorF::operator-(const ColorF& in_rSub) const
{
return ColorF(red - in_rSub.red,
green - in_rSub.green,
blue - in_rSub.blue,
alpha - in_rSub.alpha);
}
inline ColorF& ColorF::operator*=(const F32 in_mul)
{
red *= in_mul;
green *= in_mul;
blue *= in_mul;
alpha *= in_mul;
return *this;
}
inline ColorF ColorF::operator*(const F32 in_mul) const
{
return ColorF(red * in_mul,
green * in_mul,
blue * in_mul,
alpha * in_mul);
}
inline ColorF& ColorF::operator/=(const F32 in_div)
{
AssertFatal(in_div != 0.0f, "Error, div by zero...");
F32 inv = 1.0f / in_div;
red *= inv;
green *= inv;
blue *= inv;
alpha *= inv;
return *this;
}
inline ColorF ColorF::operator/(const F32 in_div) const
{
AssertFatal(in_div != 0.0f, "Error, div by zero...");
F32 inv = 1.0f / in_div;
return ColorF(red * inv,
green * inv,
blue * inv,
alpha * inv);
}
inline ColorF ColorF::operator-() const
{
return ColorF(-red, -green, -blue, -alpha);
}
inline bool ColorF::operator==(const ColorF& in_Cmp) const
{
return (red == in_Cmp.red && green == in_Cmp.green && blue == in_Cmp.blue && alpha == in_Cmp.alpha);
}
inline bool ColorF::operator!=(const ColorF& in_Cmp) const
{
return (red != in_Cmp.red || green != in_Cmp.green || blue != in_Cmp.blue || alpha != in_Cmp.alpha);
}
inline U32 ColorF::getARGBPack() const
{
return (U32(alpha * 255.0f + 0.5) << 24) |
(U32(red * 255.0f + 0.5) << 16) |
(U32(green * 255.0f + 0.5) << 8) |
(U32(blue * 255.0f + 0.5) << 0);
}
inline U32 ColorF::getRGBAPack() const
{
return ( U32( red * 255.0f + 0.5) << 0 ) |
( U32( green * 255.0f + 0.5) << 8 ) |
( U32( blue * 255.0f + 0.5) << 16 ) |
( U32( alpha * 255.0f + 0.5) << 24 );
}
inline U32 ColorF::getABGRPack() const
{
return (U32(alpha * 255.0f + 0.5) << 24) |
(U32(blue * 255.0f + 0.5) << 16) |
(U32(green * 255.0f + 0.5) << 8) |
(U32(red * 255.0f + 0.5) << 0);
}
inline void ColorF::interpolate(const ColorF& in_rC1,
const ColorF& in_rC2,
const F32 in_factor)
{
F32 f2 = 1.0f - in_factor;
red = (in_rC1.red * f2) + (in_rC2.red * in_factor);
green = (in_rC1.green * f2) + (in_rC2.green * in_factor);
blue = (in_rC1.blue * f2) + (in_rC2.blue * in_factor);
alpha = (in_rC1.alpha * f2) + (in_rC2.alpha * in_factor);
}
inline void ColorF::clamp()
{
if (red > 1.0f)
red = 1.0f;
else if (red < 0.0f)
red = 0.0f;
if (green > 1.0f)
green = 1.0f;
else if (green < 0.0f)
green = 0.0f;
if (blue > 1.0f)
blue = 1.0f;
else if (blue < 0.0f)
blue = 0.0f;
if (alpha > 1.0f)
alpha = 1.0f;
else if (alpha < 0.0f)
alpha = 0.0f;
}
//------------------------------------------------------------------------------
//-------------------------------------- INLINES (ColorI)
//
inline void ColorI::set(const U8 in_r,
const U8 in_g,
const U8 in_b,
const U8 in_a)
{
red = in_r;
green = in_g;
blue = in_b;
alpha = in_a;
}
inline void ColorI::set(const ColorI& in_rCopy,
const U8 in_a)
{
red = in_rCopy.red;
green = in_rCopy.green;
blue = in_rCopy.blue;
alpha = in_a;
}
inline ColorI::ColorI(const ColorI& in_rCopy)
{
red = in_rCopy.red;
green = in_rCopy.green;
blue = in_rCopy.blue;
alpha = in_rCopy.alpha;
}
inline ColorI::ColorI(const U8 in_r,
const U8 in_g,
const U8 in_b,
const U8 in_a)
{
set(in_r, in_g, in_b, in_a);
}
inline ColorI::ColorI(const ColorI& in_rCopy,
const U8 in_a)
{
set(in_rCopy, in_a);
}
inline ColorI& ColorI::operator*=(const F32 in_mul)
{
red = U8((F32(red) * in_mul) + 0.5f);
green = U8((F32(green) * in_mul) + 0.5f);
blue = U8((F32(blue) * in_mul) + 0.5f);
alpha = U8((F32(alpha) * in_mul) + 0.5f);
return *this;
}
inline ColorI& ColorI::operator*=(const S32 in_mul)
{
red = red * in_mul;
green = green * in_mul;
blue = blue * in_mul;
alpha = alpha * in_mul;
return *this;
}
inline ColorI& ColorI::operator/=(const S32 in_mul)
{
red = red / in_mul;
green = green / in_mul;
blue = blue / in_mul;
alpha = alpha / in_mul;
return *this;
}
inline ColorI ColorI::operator+(const ColorI &in_add) const
{
ColorI tmp;
tmp.red = red + in_add.red;
tmp.green = green + in_add.green;
tmp.blue = blue + in_add.blue;
tmp.alpha = alpha + in_add.alpha;
return tmp;
}
inline ColorI ColorI::operator*(const F32 in_mul) const
{
ColorI temp(*this);
temp *= in_mul;
return temp;
}
inline ColorI ColorI::operator*(const S32 in_mul) const
{
ColorI temp(*this);
temp *= in_mul;
return temp;
}
inline ColorI ColorI::operator/(const S32 in_mul) const
{
ColorI temp(*this);
temp /= in_mul;
return temp;
}
inline bool ColorI::operator==(const ColorI& in_Cmp) const
{
return (red == in_Cmp.red && green == in_Cmp.green && blue == in_Cmp.blue && alpha == in_Cmp.alpha);
}
inline bool ColorI::operator!=(const ColorI& in_Cmp) const
{
return (red != in_Cmp.red || green != in_Cmp.green || blue != in_Cmp.blue || alpha != in_Cmp.alpha);
}
inline ColorI& ColorI::operator+=(const ColorI& in_rAdd)
{
red += in_rAdd.red;
green += in_rAdd.green;
blue += in_rAdd.blue;
alpha += in_rAdd.alpha;
return *this;
}
inline void ColorI::interpolate(const ColorI& in_rC1,
const ColorI& in_rC2,
const F32 in_factor)
{
F32 f2= 1.0f - in_factor;
red = U8(((F32(in_rC1.red) * f2) + (F32(in_rC2.red) * in_factor)) + 0.5f);
green = U8(((F32(in_rC1.green) * f2) + (F32(in_rC2.green) * in_factor)) + 0.5f);
blue = U8(((F32(in_rC1.blue) * f2) + (F32(in_rC2.blue) * in_factor)) + 0.5f);
alpha = U8(((F32(in_rC1.alpha) * f2) + (F32(in_rC2.alpha) * in_factor)) + 0.5f);
}
inline U32 ColorI::getARGBPack() const
{
return (U32(alpha) << 24) |
(U32(red) << 16) |
(U32(green) << 8) |
(U32(blue) << 0);
}
inline U32 ColorI::getRGBAPack() const
{
return ( U32( red ) << 0 ) |
( U32( green ) << 8 ) |
( U32( blue ) << 16 ) |
( U32( alpha ) << 24 );
}
inline U32 ColorI::getABGRPack() const
{
return (U32(alpha) << 24) |
(U32(blue) << 16) |
(U32(green) << 8) |
(U32(red) << 0);
}
inline U32 ColorI::getBGRPack() const
{
return (U32(blue) << 16) |
(U32(green) << 8) |
(U32(red) << 0);
}
inline U32 ColorI::getRGBPack() const
{
return (U32(red) << 16) |
(U32(green) << 8) |
(U32(blue) << 0);
}
inline U32 ColorI::getRGBEndian() const
{
#if defined(TORQUE_BIG_ENDIAN)
return(getRGBPack());
#else
return(getBGRPack());
#endif
}
inline U32 ColorI::getARGBEndian() const
{
#if defined(TORQUE_BIG_ENDIAN)
return(getABGRPack());
#else
return(getARGBPack());
#endif
}
inline U16 ColorI::get565() const
{
return U16((U16(red >> 3) << 11) |
(U16(green >> 2) << 5) |
(U16(blue >> 3) << 0));
}
inline U16 ColorI::get4444() const
{
return U16(U16(U16(alpha >> 4) << 12) |
U16(U16(red >> 4) << 8) |
U16(U16(green >> 4) << 4) |
U16(U16(blue >> 4) << 0));
}
//-------------------------------------- INLINE CONVERSION OPERATORS
inline ColorF::operator ColorI() const
{
return ColorI(U8(red * 255.0f + 0.5),
U8(green * 255.0f + 0.5),
U8(blue * 255.0f + 0.5),
U8(alpha * 255.0f + 0.5));
}
inline ColorI::operator ColorF() const
{
const F32 inv255 = 1.0f / 255.0f;
return ColorF(F32(red) * inv255,
F32(green) * inv255,
F32(blue) * inv255,
F32(alpha) * inv255);
}
#endif //_COLOR_H_

View file

@ -0,0 +1,90 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/crc.h"
#include "core/stream/stream.h"
//-----------------------------------------------------------------------------
// simple crc function - generates lookup table on first call
static U32 crcTable[256];
static bool crcTableValid;
static void calculateCRCTable()
{
U32 val;
for(S32 i = 0; i < 256; i++)
{
val = i;
for(S32 j = 0; j < 8; j++)
{
if(val & 0x01)
val = 0xedb88320 ^ (val >> 1);
else
val = val >> 1;
}
crcTable[i] = val;
}
crcTableValid = true;
}
//-----------------------------------------------------------------------------
U32 CRC::calculateCRC(const void * buffer, S32 len, U32 crcVal )
{
// check if need to generate the crc table
if(!crcTableValid)
calculateCRCTable();
// now calculate the crc
char * buf = (char*)buffer;
for(S32 i = 0; i < len; i++)
crcVal = crcTable[(crcVal ^ buf[i]) & 0xff] ^ (crcVal >> 8);
return(crcVal);
}
U32 CRC::calculateCRCStream(Stream *stream, U32 crcVal )
{
// check if need to generate the crc table
if(!crcTableValid)
calculateCRCTable();
// now calculate the crc
stream->setPosition(0);
S32 len = stream->getStreamSize();
U8 buf[4096];
S32 segCount = (len + 4095) / 4096;
for(S32 j = 0; j < segCount; j++)
{
S32 slen = getMin(4096, len - (j * 4096));
stream->read(slen, buf);
crcVal = CRC::calculateCRC(buf, slen, crcVal);
}
stream->setPosition(0);
return(crcVal);
}

49
Engine/source/core/crc.h Normal file
View file

@ -0,0 +1,49 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _CRC_H_
#define _CRC_H_
#ifndef _TORQUE_TYPES_H_
#include "platform/types.h"
#endif
class Stream;
namespace CRC
{
/// Initial value for CRCs
const U32 INITIAL_CRC_VALUE = 0xFFFFFFFF;
/// Value XORd with the CRC to post condition it (ones complement)
const U32 CRC_POSTCOND_VALUE = 0xFFFFFFFF;
/// An invalid CRC
const U32 INVALID_CRC = 0xFFFFFFFF;
U32 calculateCRC(const void * buffer, S32 len, U32 crcVal = INITIAL_CRC_VALUE);
U32 calculateCRCStream(Stream *stream, U32 crcVal = INITIAL_CRC_VALUE);
}
#endif

View file

@ -0,0 +1,97 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/dataChunker.h"
//----------------------------------------------------------------------------
DataChunker::DataChunker(S32 size)
{
mChunkSize = size;
mCurBlock = NULL;
}
DataChunker::~DataChunker()
{
freeBlocks();
}
void *DataChunker::alloc(S32 size)
{
if (size > mChunkSize)
{
DataBlock * temp = new DataBlock(size);
if (mCurBlock)
{
temp->next = mCurBlock->next;
mCurBlock->next = temp;
}
else
{
mCurBlock = temp;
temp->curIndex = mChunkSize;
}
return temp->data;
}
if(!mCurBlock || size + mCurBlock->curIndex > mChunkSize)
{
DataBlock *temp = new DataBlock(mChunkSize);
temp->next = mCurBlock;
temp->curIndex = 0;
mCurBlock = temp;
}
void *ret = mCurBlock->data + mCurBlock->curIndex;
mCurBlock->curIndex += (size + 3) & ~3; // dword align
return ret;
}
DataChunker::DataBlock::DataBlock(S32 size)
{
data = new U8[size];
}
DataChunker::DataBlock::~DataBlock()
{
delete[] data;
}
void DataChunker::freeBlocks(bool keepOne)
{
while(mCurBlock && mCurBlock->next)
{
DataBlock *temp = mCurBlock->next;
delete mCurBlock;
mCurBlock = temp;
}
if (!keepOne)
{
delete mCurBlock;
mCurBlock = NULL;
}
else if (mCurBlock)
mCurBlock->curIndex = 0;
}

View file

@ -0,0 +1,297 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _DATACHUNKER_H_
#define _DATACHUNKER_H_
#ifndef _PLATFORM_H_
# include "platform/platform.h"
#endif
//----------------------------------------------------------------------------
/// Implements a chunked data allocator.
///
/// Calling new/malloc all the time is a time consuming operation. Therefore,
/// we provide the DataChunker, which allocates memory in blocks of
/// chunkSize (by default 16k, see ChunkSize, though it can be set in
/// the constructor), then doles it out as requested, in chunks of up to
/// chunkSize in size.
///
/// It will assert if you try to get more than ChunkSize bytes at a time,
/// and it deals with the logic of allocating new blocks and giving out
/// word-aligned chunks.
///
/// Note that new/free/realloc WILL NOT WORK on memory gotten from the
/// DataChunker. This also only grows (you can call freeBlocks to deallocate
/// and reset things).
class DataChunker
{
public:
enum {
ChunkSize = 16376 ///< Default size of each DataBlock page in the DataChunker
};
/// Return a pointer to a chunk of memory from a pre-allocated block.
///
/// This memory goes away when you call freeBlocks.
///
/// This memory is word-aligned.
/// @param size Size of chunk to return. This must be less than chunkSize or else
/// an assertion will occur.
void *alloc(S32 size);
/// Free all allocated memory blocks.
///
/// This invalidates all pointers returned from alloc().
void freeBlocks(bool keepOne = false);
/// Initialize using blocks of a given size.
///
/// One new block is allocated at constructor-time.
///
/// @param size Size in bytes of the space to allocate for each block.
DataChunker(S32 size=ChunkSize);
~DataChunker();
/// Swaps the memory allocated in one data chunker for another. This can be used to implement
/// packing of memory stored in a DataChunker.
void swap(DataChunker &d)
{
DataBlock *temp = d.mCurBlock;
d.mCurBlock = mCurBlock;
mCurBlock = temp;
}
private:
/// Block of allocated memory.
///
/// <b>This has nothing to do with datablocks as used in the rest of Torque.</b>
struct DataBlock
{
DataBlock* prev;
DataBlock* next; ///< linked list pointer to the next DataBlock for this chunker
U8 *data; ///< allocated pointer for the base of this page
S32 curIndex; ///< current allocation point within this DataBlock
DataBlock(S32 size);
~DataBlock();
};
DataBlock* mFirstBlock;
DataBlock *mCurBlock; ///< current page we're allocating data from. If the
///< data size request is greater than the memory space currently
///< available in the current page, a new page will be allocated.
S32 mChunkSize; ///< The size allocated for each page in the DataChunker
};
//----------------------------------------------------------------------------
template<class T>
class Chunker: private DataChunker
{
public:
Chunker(S32 size = DataChunker::ChunkSize) : DataChunker(size) {};
T* alloc() { return reinterpret_cast<T*>(DataChunker::alloc(S32(sizeof(T)))); }
void clear() { freeBlocks(); }
};
//----------------------------------------------------------------------------
/// This class is similar to the Chunker<> class above. But it allows for multiple
/// types of structs to be stored.
/// CodeReview: This could potentially go into DataChunker directly, but I wasn't sure if
/// CodeReview: That would be polluting it. BTR
class MultiTypedChunker : private DataChunker
{
public:
MultiTypedChunker(S32 size = DataChunker::ChunkSize) : DataChunker(size) {};
/// Use like so: MyType* t = chunker.alloc<MyType>();
template<typename T>
T* alloc() { return reinterpret_cast<T*>(DataChunker::alloc(S32(sizeof(T)))); }
void clear() { freeBlocks(true); }
};
//----------------------------------------------------------------------------
/// Templatized data chunker class with proper construction and destruction of its elements.
///
/// DataChunker just allocates space. This subclass actually constructs/destructs the
/// elements. This class is appropriate for more complex classes.
template<class T>
class ClassChunker: private DataChunker
{
public:
ClassChunker(S32 size = DataChunker::ChunkSize) : DataChunker(size)
{
mElementSize = getMax(U32(sizeof(T)), U32(sizeof(T *)));
mFreeListHead = NULL;
}
/// Allocates and properly constructs in place a new element.
T *alloc()
{
if(mFreeListHead == NULL)
return constructInPlace(reinterpret_cast<T*>(DataChunker::alloc(mElementSize)));
T* ret = mFreeListHead;
mFreeListHead = *(reinterpret_cast<T**>(mFreeListHead));
return constructInPlace(ret);
}
/// Properly destructs and frees an element allocated with the alloc method.
void free(T* elem)
{
destructInPlace(elem);
*(reinterpret_cast<T**>(elem)) = mFreeListHead;
mFreeListHead = elem;
}
void freeBlocks( bool keepOne = false )
{
DataChunker::freeBlocks( keepOne );
mFreeListHead = NULL;
}
private:
S32 mElementSize; ///< the size of each element, or the size of a pointer, whichever is greater
T *mFreeListHead; ///< a pointer to a linked list of freed elements for reuse
};
//----------------------------------------------------------------------------
template<class T>
class FreeListChunker
{
public:
FreeListChunker(DataChunker *inChunker)
: mChunker( inChunker ),
mOwnChunker( false ),
mFreeListHead( NULL )
{
mElementSize = getMax(U32(sizeof(T)), U32(sizeof(T *)));
}
FreeListChunker(S32 size = DataChunker::ChunkSize)
: mFreeListHead( NULL )
{
mChunker = new DataChunker( size );
mOwnChunker = true;
mElementSize = getMax(U32(sizeof(T)), U32(sizeof(T *)));
}
~FreeListChunker()
{
if ( mOwnChunker )
delete mChunker;
}
T *alloc()
{
if(mFreeListHead == NULL)
return reinterpret_cast<T*>(mChunker->alloc(mElementSize));
T* ret = mFreeListHead;
mFreeListHead = *(reinterpret_cast<T**>(mFreeListHead));
return ret;
}
void free(T* elem)
{
*(reinterpret_cast<T**>(elem)) = mFreeListHead;
mFreeListHead = elem;
}
/// Allow people to free all their memory if they want.
void freeBlocks( bool keepOne = false )
{
mChunker->freeBlocks( keepOne );
mFreeListHead = NULL;
}
private:
DataChunker *mChunker;
bool mOwnChunker;
S32 mElementSize;
T *mFreeListHead;
};
class FreeListChunkerUntyped
{
public:
FreeListChunkerUntyped(U32 inElementSize, DataChunker *inChunker)
: mChunker( inChunker ),
mOwnChunker( false ),
mElementSize( inElementSize ),
mFreeListHead( NULL )
{
}
FreeListChunkerUntyped(U32 inElementSize, S32 size = DataChunker::ChunkSize)
: mElementSize( inElementSize ),
mFreeListHead( NULL )
{
mChunker = new DataChunker( size );
mOwnChunker = true;
}
~FreeListChunkerUntyped()
{
if ( mOwnChunker )
delete mChunker;
}
void *alloc()
{
if(mFreeListHead == NULL)
return mChunker->alloc(mElementSize);
void *ret = mFreeListHead;
mFreeListHead = *(reinterpret_cast<void**>(mFreeListHead));
return ret;
}
void free(void* elem)
{
*(reinterpret_cast<void**>(elem)) = mFreeListHead;
mFreeListHead = elem;
}
// Allow people to free all their memory if they want.
void freeBlocks()
{
mChunker->freeBlocks();
// We have to terminate the freelist as well or else we'll run
// into crazy unused memory.
mFreeListHead = NULL;
}
U32 getElementSize() const { return mElementSize; }
private:
DataChunker *mChunker;
bool mOwnChunker;
const U32 mElementSize;
void *mFreeListHead;
};
#endif

288
Engine/source/core/dnet.cpp Normal file
View file

@ -0,0 +1,288 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/stream/bitStream.h"
#include "core/dnet.h"
#include "core/strings/stringFunctions.h"
#include "console/consoleTypes.h"
bool gLogToConsole = false;
S32 gNetBitsReceived = 0;
enum NetPacketType
{
DataPacket,
PingPacket,
AckPacket,
InvalidPacketType,
};
static const char *packetTypeNames[] =
{
"DataPacket",
"PingPacket",
"AckPacket",
};
//-----------------------------------------------------------------
//-----------------------------------------------------------------
//-----------------------------------------------------------------
ConsoleFunction(DNetSetLogging, void, 2, 2, "(bool enabled)"
"@brief Enables logging of the connection protocols\n\n"
"When enabled a lot of network debugging information is sent to the console.\n"
"@param enabled True to enable, false to disable\n"
"@ingroup Networking")
{
TORQUE_UNUSED(argc);
gLogToConsole = dAtob(argv[1]);
}
ConnectionProtocol::ConnectionProtocol()
{
mLastSeqRecvd = 0;
mHighestAckedSeq = 0;
mLastSendSeq = 0; // start sending at 1
mAckMask = 0;
mLastRecvAckAck = 0;
}
void ConnectionProtocol::buildSendPacketHeader(BitStream *stream, S32 packetType)
{
S32 ackByteCount = ((mLastSeqRecvd - mLastRecvAckAck + 7) >> 3);
AssertFatal(ackByteCount <= 4, "Too few ack bytes!");
// S32 headerSize = 3 + ackByteCount;
if(packetType == DataPacket)
mLastSendSeq++;
stream->writeFlag(true);
stream->writeInt(mConnectSequence & 1, 1);
stream->writeInt(mLastSendSeq, 9);
stream->writeInt(mLastSeqRecvd, 9);
stream->writeInt(packetType, 2);
stream->writeInt(ackByteCount, 3);
stream->writeInt(mAckMask, ackByteCount * 8);
// if we're resending this header, we can't advance the
// sequence recieved (in case this packet drops and the prev one
// goes through)
if(gLogToConsole)
Con::printf("build hdr %d %d", mLastSendSeq, packetType);
if(packetType == DataPacket)
mLastSeqRecvdAtSend[mLastSendSeq & 0x1F] = mLastSeqRecvd;
}
void ConnectionProtocol::sendPingPacket()
{
U8 buffer[16];
BitStream bs(buffer, 16);
buildSendPacketHeader(&bs, PingPacket);
if(gLogToConsole)
Con::printf("send ping %d", mLastSendSeq);
sendPacket(&bs);
}
void ConnectionProtocol::sendAckPacket()
{
U8 buffer[16];
BitStream bs(buffer, 16);
buildSendPacketHeader(&bs, AckPacket);
if(gLogToConsole)
Con::printf("send ack %d", mLastSendSeq);
sendPacket(&bs);
}
// packets are read directly into the data portion of
// connection notify packets... makes the events easier to post into
// the system.
void ConnectionProtocol::processRawPacket(BitStream *pstream)
{
// read in the packet header:
// Fixed packet header: 3 bytes
//
// 1 bit game packet flag
// 1 bit connect sequence
// 9 bits packet seq number
// 9 bits ackstart seq number
// 2 bits packet type
// 2 bits ack byte count
//
// type is:
// 00 data packet
// 01 ping packet
// 02 ack packet
// next 1-4 bytes are ack flags
//
// header len is 4-9 bytes
// average case 4 byte header
gNetBitsReceived = pstream->getStreamSize();
pstream->readFlag(); // get rid of the game info packet bit
U32 pkConnectSeqBit = pstream->readInt(1);
U32 pkSequenceNumber = pstream->readInt(9);
U32 pkHighestAck = pstream->readInt(9);
U32 pkPacketType = pstream->readInt(2);
S32 pkAckByteCount = pstream->readInt(3);
// check connection sequence bit
if(pkConnectSeqBit != (mConnectSequence & 1))
return;
if(pkAckByteCount > 4 || pkPacketType >= InvalidPacketType)
return;
S32 pkAckMask = pstream->readInt(8 * pkAckByteCount);
// verify packet ordering and acking and stuff
// check if the 9-bit sequence is within the packet window
// (within 31 packets of the last received sequence number).
pkSequenceNumber |= (mLastSeqRecvd & 0xFFFFFE00);
// account for wrap around
if(pkSequenceNumber < mLastSeqRecvd)
pkSequenceNumber += 0x200;
if(pkSequenceNumber > mLastSeqRecvd + 31)
{
// the sequence number is outside the window... must be out of order
// discard.
return;
}
pkHighestAck |= (mHighestAckedSeq & 0xFFFFFE00);
// account for wrap around
if(pkHighestAck < mHighestAckedSeq)
pkHighestAck += 0x200;
if(pkHighestAck > mLastSendSeq)
{
// the ack number is outside the window... must be an out of order
// packet, discard.
return;
}
if(gLogToConsole)
{
for(U32 i = mLastSeqRecvd+1; i < pkSequenceNumber; i++)
Con::printf("Not recv %d", i);
Con::printf("Recv %d %s", pkSequenceNumber, packetTypeNames[pkPacketType]);
}
// shift up the ack mask by the packet difference
// this essentially nacks all the packets dropped
mAckMask <<= pkSequenceNumber - mLastSeqRecvd;
// if this packet is a data packet (i.e. not a ping packet or an ack packet), ack it
if(pkPacketType == DataPacket)
mAckMask |= 1;
// do all the notifies...
for(U32 i = mHighestAckedSeq+1; i <= pkHighestAck; i++)
{
bool packetTransmitSuccess = pkAckMask & (1 << (pkHighestAck - i));
handleNotify(packetTransmitSuccess);
if(gLogToConsole)
Con::printf("Ack %d %d", i, packetTransmitSuccess);
if(packetTransmitSuccess)
{
mLastRecvAckAck = mLastSeqRecvdAtSend[i & 0x1F];
if(!mConnectionEstablished)
{
mConnectionEstablished = true;
handleConnectionEstablished();
}
}
}
// the other side knows more about its window than we do.
if(pkSequenceNumber - mLastRecvAckAck > 32)
mLastRecvAckAck = pkSequenceNumber - 32;
mHighestAckedSeq = pkHighestAck;
// first things first...
// ackback any pings or accept connects
if(pkPacketType == PingPacket)
{
// send an ack to the other side
// the ack will have the same packet sequence as our last sent packet
// if the last packet we sent was the connection accepted packet
// we must resend that packet
sendAckPacket();
}
keepAlive(); // notification that the connection is ok
// note: handlePacket() may delete the connection if an error occurs.
if(mLastSeqRecvd != pkSequenceNumber)
{
mLastSeqRecvd = pkSequenceNumber;
if(pkPacketType == DataPacket)
handlePacket(pstream);
}
}
bool ConnectionProtocol::windowFull()
{
return mLastSendSeq - mHighestAckedSeq >= 30;
}
void ConnectionProtocol::writeDemoStartBlock(ResizeBitStream *stream)
{
for(U32 i = 0; i < 32; i++)
stream->write(mLastSeqRecvdAtSend[i]);
stream->write(mLastSeqRecvd);
stream->write(mHighestAckedSeq);
stream->write(mLastSendSeq);
stream->write(mAckMask);
stream->write(mConnectSequence);
stream->write(mLastRecvAckAck);
stream->write(mConnectionEstablished);
}
bool ConnectionProtocol::readDemoStartBlock(BitStream *stream)
{
for(U32 i = 0; i < 32; i++)
stream->read(&mLastSeqRecvdAtSend[i]);
stream->read(&mLastSeqRecvd);
stream->read(&mHighestAckedSeq);
stream->read(&mLastSendSeq);
stream->read(&mAckMask);
stream->read(&mConnectSequence);
stream->read(&mLastRecvAckAck);
stream->read(&mConnectionEstablished);
return true;
}

83
Engine/source/core/dnet.h Normal file
View file

@ -0,0 +1,83 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _DNET_H_
#define _DNET_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _EVENT_H_
#include "platform/event.h"
#endif
#include "platform/platformNet.h"
class BitStream;
class ResizeBitStream;
/// The base class for Torque's networking protocol.
///
/// This implements a sliding window connected message stream over an unreliable transport (UDP). It
/// provides a simple notify protocol to allow subclasses to be aware of what packets were sent
/// succesfully and which failed.
///
/// Basically, a window size of 32 is provided, and each packet contains in the header a bitmask,
/// acknowledging the receipt (or failure to receive) of the last 32 packets.
///
/// @see NetConnection, @ref NetProtocol
class ConnectionProtocol
{
protected:
U32 mLastSeqRecvdAtSend[32];
U32 mLastSeqRecvd;
U32 mHighestAckedSeq;
U32 mLastSendSeq;
U32 mAckMask;
U32 mConnectSequence;
U32 mLastRecvAckAck;
bool mConnectionEstablished;
public:
ConnectionProtocol();
void buildSendPacketHeader(BitStream *bstream, S32 packetType = 0);
void sendPingPacket();
void sendAckPacket();
void setConnectionEstablished() { mConnectionEstablished = true; }
bool windowFull();
bool connectionEstablished();
void setConnectSequence(U32 connectSeq) { mConnectSequence = connectSeq; }
virtual void writeDemoStartBlock(ResizeBitStream *stream);
virtual bool readDemoStartBlock(BitStream *stream);
virtual void processRawPacket(BitStream *bstream);
virtual Net::Error sendPacket(BitStream *bstream) = 0;
virtual void keepAlive() = 0;
virtual void handleConnectionEstablished() = 0;
virtual void handleNotify(bool recvd) = 0;
virtual void handlePacket(BitStream *bstream) = 0;
};
#endif

View file

@ -0,0 +1,502 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/fileObject.h"
#include "console/engineAPI.h"
IMPLEMENT_CONOBJECT(FileObject);
ConsoleDocClass( FileObject,
"@brief This class is responsible opening, reading, creating, and saving file contents.\n\n"
"FileObject acts as the interface with OS level files. You create a new FileObject and pass into it "
"a file's path and name. The FileObject class supports three distinct operations for working with files:\n\n"
"<table border='1' cellpadding='1'>"
"<tr><th>Operation</th><th>%FileObject Method</th><th>Description</th></tr>"
"<tr><td>Read</td><td>openForRead()</td><td>Open the file for reading</td></tr>"
"<tr><td>Write</td><td>openForWrite()</td><td>Open the file for writing to and replace its contents (if any)</td></tr>"
"<tr><td>Append</td><td>openForAppend()</td><td>Open the file and start writing at its end</td></tr>"
"</table>\n\n"
"Before you may work with a file you need to use one of the three above methods on the FileObject.\n\n"
"@tsexample\n"
"// Create a file object for writing\n"
"%fileWrite = new FileObject();\n\n"
"// Open a file to write to, if it does not exist it will be created\n"
"%result = %fileWrite.OpenForWrite(\"./test.txt\");\n\n"
"if ( %result )\n"
"{\n"
" // Write a line to the text files\n"
" %fileWrite.writeLine(\"READ. READ CODE. CODE\");\n"
"}\n\n"
"// Close the file when finished\n"
"%fileWrite.close();\n\n"
"// Cleanup the file object\n"
"%fileWrite.delete();\n\n\n"
"// Create a file object for reading\n"
"%fileRead = new FileObject();\n\n"
"// Open a text file, if it exists\n"
"%result = %fileRead.OpenForRead(\"./test.txt\");\n\n"
"if ( %result )\n"
"{\n"
" // Read in the first line\n"
" %line = %fileRead.readline();\n\n"
" // Print the line we just read\n"
" echo(%line);\n"
"}\n\n"
"// Close the file when finished\n"
"%fileRead.close();\n\n"
"// Cleanup the file object\n"
"%fileRead.delete();\n"
"@endtsexample\n\n"
"@ingroup FileSystem\n"
);
bool FileObject::isEOF()
{
return mCurPos == mBufferSize;
}
FileObject::FileObject()
{
mFileBuffer = NULL;
mBufferSize = 0;
mCurPos = 0;
stream = NULL;
}
FileObject::~FileObject()
{
SAFE_DELETE_ARRAY(mFileBuffer);
SAFE_DELETE(stream);
}
void FileObject::close()
{
SAFE_DELETE(stream);
SAFE_DELETE_ARRAY(mFileBuffer);
mFileBuffer = NULL;
mBufferSize = mCurPos = 0;
}
bool FileObject::openForWrite(const char *fileName, const bool append)
{
char buffer[1024];
Con::expandScriptFilename( buffer, sizeof( buffer ), fileName );
close();
if( !buffer[ 0 ] )
return false;
if((stream = FileStream::createAndOpen( fileName, append ? Torque::FS::File::WriteAppend : Torque::FS::File::Write )) == NULL)
return false;
stream->setPosition( stream->getStreamSize() );
return( true );
}
bool FileObject::openForRead(const char* /*fileName*/)
{
AssertFatal(false, "Error, not yet implemented!");
return false;
}
bool FileObject::readMemory(const char *fileName)
{
char buffer[1024];
Con::expandScriptFilename( buffer, sizeof( buffer ), fileName );
close();
void *data = NULL;
U32 dataSize = 0;
Torque::FS::ReadFile(buffer, data, dataSize, true);
if(data == NULL)
return false;
mBufferSize = dataSize;
mFileBuffer = (U8 *)data;
mCurPos = 0;
return true;
}
const U8 *FileObject::readLine()
{
if(!mFileBuffer)
return (U8 *) "";
U32 tokPos = mCurPos;
for(;;)
{
if(mCurPos == mBufferSize)
break;
if(mFileBuffer[mCurPos] == '\r')
{
mFileBuffer[mCurPos++] = 0;
if(mFileBuffer[mCurPos] == '\n')
mCurPos++;
break;
}
if(mFileBuffer[mCurPos] == '\n')
{
mFileBuffer[mCurPos++] = 0;
break;
}
mCurPos++;
}
return mFileBuffer + tokPos;
}
void FileObject::peekLine( U8* line, S32 length )
{
if(!mFileBuffer)
{
line[0] = '\0';
return;
}
// Copy the line into the buffer. We can't do this like readLine because
// we can't modify the file buffer.
S32 i = 0;
U32 tokPos = mCurPos;
while( ( tokPos != mBufferSize ) && ( mFileBuffer[tokPos] != '\r' ) && ( mFileBuffer[tokPos] != '\n' ) && ( i < ( length - 1 ) ) )
line[i++] = mFileBuffer[tokPos++];
line[i++] = '\0';
//if( i == length )
//Con::warnf( "FileObject::peekLine - The line contents could not fit in the buffer (size %d). Truncating.", length );
}
void FileObject::writeLine(const U8 *line)
{
stream->write(dStrlen((const char *) line), line);
stream->write(2, "\r\n");
}
void FileObject::writeObject( SimObject* object, const U8* objectPrepend )
{
if( objectPrepend == NULL )
stream->write(2, "\r\n");
else
stream->write(dStrlen((const char *) objectPrepend), objectPrepend );
object->write( *stream, 0 );
}
DefineEngineMethod( FileObject, openForRead, bool, ( const char* filename ),,
"@brief Open a specified file for reading\n\n"
"There is no limit as to what kind of file you can read. Any format and data contained within is accessible, not just text\n\n"
"@param filename Path, name, and extension of file to be read"
"@tsexample\n"
"// Create a file object for reading\n"
"%fileRead = new FileObject();\n\n"
"// Open a text file, if it exists\n"
"%result = %fileRead.OpenForRead(\"./test.txt\");\n"
"@endtsexample\n\n"
"@return True if file was successfully opened, false otherwise\n")
{
return object->readMemory(filename);
}
DefineEngineMethod( FileObject, openForWrite, bool, ( const char* filename ),,
"@brief Open a specified file for writing\n\n"
"There is no limit as to what kind of file you can write. Any format and data is allowable, not just text\n\n"
"@param filename Path, name, and extension of file to write to"
"@tsexample\n"
"// Create a file object for writing\n"
"%fileWrite = new FileObject();\n\n"
"// Open a file to write to, if it does not exist it will be created\n"
"%result = %fileWrite.OpenForWrite(\"./test.txt\");\n"
"@endtsexample\n\n"
"@return True if file was successfully opened, false otherwise\n")
{
return object->openForWrite(filename);
}
DefineEngineMethod( FileObject, openForAppend, bool, ( const char* filename ),,
"@brief Open a specified file for writing, adding data to the end of the file\n\n"
"There is no limit as to what kind of file you can write. Any format and data is allowable, not just text. Unlike openForWrite(), "
"which will erase an existing file if it is opened, openForAppend() preserves data in an existing file and adds to it.\n\n"
"@param filename Path, name, and extension of file to append to"
"@tsexample\n"
"// Create a file object for writing\n"
"%fileWrite = new FileObject();\n\n"
"// Open a file to write to, if it does not exist it will be created\n"
"// If it does exist, whatever we write will be added to the end\n"
"%result = %fileWrite.OpenForAppend(\"./test.txt\");\n"
"@endtsexample\n\n"
"@return True if file was successfully opened, false otherwise\n")
{
return object->openForWrite(filename, true);
}
DefineEngineMethod( FileObject, isEOF, bool, (),,
"@brief Determines if the parser for this FileObject has reached the end of the file\n\n"
"@tsexample\n"
"// Create a file object for reading\n"
"%fileRead = new FileObject();\n\n"
"// Open a text file, if it exists\n"
"%fileRead.OpenForRead(\"./test.txt\");\n\n"
"// Keep reading until we reach the end of the file\n"
"while( !%fileRead.isEOF() )\n"
"{\n"
" %line = %fileRead.readline();\n"
" echo(%line);\n"
"}\n\n"
"// Made it to the end\n"
"echo(\"Finished reading file\");\n"
"@endtsexample\n\n"
"@return True if the parser has reached the end of the file, false otherwise\n")
{
return object->isEOF();
}
DefineEngineMethod( FileObject, readLine, const char*, (),,
"@brief Read a line from file.\n\n"
"Emphasis on *line*, as in you cannot parse individual characters or chunks of data. "
"There is no limitation as to what kind of data you can read.\n\n"
"@tsexample\n"
"// Create a file object for reading\n"
"%fileRead = new FileObject();\n\n"
"// Open a text file, if it exists\n"
"%fileRead.OpenForRead(\"./test.txt\");\n\n"
"// Read in the first line\n"
"%line = %fileRead.readline();\n\n"
"// Print the line we just read\n"
"echo(%line);\n"
"@endtsexample\n\n"
"@return String containing the line of data that was just read\n")
{
return (const char *) object->readLine();
}
DefineEngineMethod( FileObject, peekLine, const char*, (),,
"@brief Read a line from the file without moving the stream position.\n\n"
"Emphasis on *line*, as in you cannot parse individual characters or chunks of data. "
"There is no limitation as to what kind of data you can read. Unlike readLine, the parser does not move forward after reading.\n\n"
"@param filename Path, name, and extension of file to be read"
"@tsexample\n"
"// Create a file object for reading\n"
"%fileRead = new FileObject();\n\n"
"// Open a text file, if it exists\n"
"%fileRead.OpenForRead(\"./test.txt\");\n\n"
"// Peek the first line\n"
"%line = %fileRead.peekLine();\n\n"
"// Print the line we just peeked\n"
"echo(%line);\n"
"// If we peek again...\n"
"%line = %fileRead.peekLine();\n\n"
"// We will get the same output as the first time\n"
"// since the stream did not move forward\n"
"echo(%line);\n"
"@endtsexample\n\n"
"@return String containing the line of data that was just peeked\n")
{
char *line = Con::getReturnBuffer( 512 );
object->peekLine( (U8*)line, 512 );
return line;
}
DefineEngineMethod( FileObject, writeLine, void, ( const char* text ),,
"@brief Write a line to the file, if it was opened for writing.\n\n"
"There is no limit as to what kind of text you can write. Any format and data is allowable, not just text. "
"Be careful of what you write, as whitespace, current values, and literals will be preserved.\n\n"
"@param text The data we are writing out to file."
"@tsexample\n"
"// Create a file object for writing\n"
"%fileWrite = new FileObject();\n\n"
"// Open a file to write to, if it does not exist it will be created\n"
"%fileWrite.OpenForWrite(\"./test.txt\");\n\n"
"// Write a line to the text files\n"
"%fileWrite.writeLine(\"READ. READ CODE. CODE\");\n\n"
"@endtsexample\n\n"
"@return True if file was successfully opened, false otherwise\n")
{
object->writeLine((const U8 *) text);
}
DefineEngineMethod( FileObject, close, void, (),,
"@brief Close the file.\n\n"
"It is EXTREMELY important that you call this function when you are finished reading or writing to a file. "
"Failing to do so is not only a bad programming practice, but could result in bad data or corrupt files. "
"Remember: Open, Read/Write, Close, Delete...in that order!\n\n"
"@tsexample\n"
"// Create a file object for reading\n"
"%fileRead = new FileObject();\n\n"
"// Open a text file, if it exists\n"
"%fileRead.OpenForRead(\"./test.txt\");\n\n"
"// Peek the first line\n"
"%line = %fileRead.peekLine();\n\n"
"// Print the line we just peeked\n"
"echo(%line);\n"
"// If we peek again...\n"
"%line = %fileRead.peekLine();\n\n"
"// We will get the same output as the first time\n"
"// since the stream did not move forward\n"
"echo(%line);\n\n"
"// Close the file when finished\n"
"%fileWrite.close();\n\n"
"// Cleanup the file object\n"
"%fileWrite.delete();\n"
"@endtsexample\n\n")
{
object->close();
}
static ConsoleDocFragment _FileObjectwriteObject1(
"@brief Write an object to a text file.\n\n"
"Unlike a simple writeLine using specified strings, this function writes an entire object "
"to file, preserving its type, name, and properties. This is similar to the save() functionality of "
"the SimObject class, but with a bit more control.\n\n"
"@param object The SimObject being written to file, properties, name, and all.\n"
"@tsexample\n"
"// Let's assume this SpawnSphere was created and currently\n"
"// exists in the running level\n"
"new SpawnSphere(TestSphere)\n"
"{\n"
" spawnClass = \"Player\";\n"
" spawnDatablock = \"DefaultPlayerData\";\n"
" autoSpawn = \"1\";\n"
" radius = \"5\";\n"
" sphereWeight = \"1\";\n"
" indoorWeight = \"1\";\n"
" outdoorWeight = \"1\";\n"
" dataBlock = \"SpawnSphereMarker\";\n"
" position = \"-42.222 1.4845 4.80334\";\n"
" rotation = \"0 0 -1 108\";\n"
" scale = \"1 1 1\";\n"
" canSaveDynamicFields = \"1\";\n"
"};\n\n"
"// Create a file object for writing\n"
"%fileWrite = new FileObject();\n\n"
"// Open a file to write to, if it does not exist it will be created\n"
"%fileWrite.OpenForWrite(\"./spawnSphers.txt\");\n\n"
"// Write out the TestSphere\n"
"%fileWrite.writeObject(TestSphere);\n\n"
"// Close the text file\n"
"%fileWrite.close();\n\n"
"// Cleanup\n"
"%fileWrite.delete();\n"
"@endtsexample\n\n\n",
"FileObject",
"void writeObject( SimObject* object);");
static ConsoleDocFragment _FileObjectwriteObject2(
"@brief Write an object to a text file, with some data added first.\n\n"
"Unlike a simple writeLine using specified strings, this function writes an entire object "
"to file, preserving its type, name, and properties. This is similar to the save() functionality of "
"the SimObject class, but with a bit more control.\n\n"
"@param object The SimObject being written to file, properties, name, and all.\n"
"@param prepend Data or text that is written out before the SimObject.\n\n"
"@tsexample\n"
"// Let's assume this SpawnSphere was created and currently\n"
"// exists in the running level\n"
"new SpawnSphere(TestSphere)\n"
"{\n"
" spawnClass = \"Player\";\n"
" spawnDatablock = \"DefaultPlayerData\";\n"
" autoSpawn = \"1\";\n"
" radius = \"5\";\n"
" sphereWeight = \"1\";\n"
" indoorWeight = \"1\";\n"
" outdoorWeight = \"1\";\n"
" dataBlock = \"SpawnSphereMarker\";\n"
" position = \"-42.222 1.4845 4.80334\";\n"
" rotation = \"0 0 -1 108\";\n"
" scale = \"1 1 1\";\n"
" canSaveDynamicFields = \"1\";\n"
"};\n\n"
"// Create a file object for writing\n"
"%fileWrite = new FileObject();\n\n"
"// Open a file to write to, if it does not exist it will be created\n"
"%fileWrite.OpenForWrite(\"./spawnSphers.txt\");\n\n"
"// Write out the TestSphere, with a prefix\n"
"%fileWrite.writeObject(TestSphere, \"$mySphere = \");\n\n"
"// Close the text file\n"
"%fileWrite.close();\n\n"
"// Cleanup\n"
"%fileWrite.delete();\n"
"@endtsexample\n\n\n",
"FileObject",
"void writeObject( SimObject* object, string prepend);");
ConsoleMethod( FileObject, writeObject, void, 3, 4, "FileObject.writeObject(SimObject, object prepend)"
"@hide")
{
SimObject* obj = Sim::findObject( argv[2] );
if( !obj )
{
Con::printf("FileObject::writeObject - Invalid Object!");
return;
}
char *objName = NULL;
if( argc == 4 )
objName = (char*)argv[3];
object->writeObject( obj, (const U8*)objName );
}

View file

@ -0,0 +1,58 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _FILEOBJECT_H_
#define _FILEOBJECT_H_
#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
#ifndef _FILESTREAM_H_
#include "core/stream/fileStream.h"
#endif
class FileObject : public SimObject
{
typedef SimObject Parent;
U8 *mFileBuffer;
U32 mBufferSize;
U32 mCurPos;
FileStream *stream;
public:
FileObject();
~FileObject();
bool openForWrite(const char *fileName, const bool append = false);
bool openForRead(const char *fileName);
bool readMemory(const char *fileName);
const U8 *buffer() { return mFileBuffer; }
const U8 *readLine();
void peekLine(U8 *line, S32 length);
bool isEOF();
void writeLine(const U8 *line);
void close();
void writeObject( SimObject* object, const U8* objectPrepend = NULL );
DECLARE_CONOBJECT(FileObject);
};
#endif

140
Engine/source/core/fileio.h Normal file
View file

@ -0,0 +1,140 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _FILEIO_H_
#define _FILEIO_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
class File
{
public:
/// What is the status of our file handle?
enum Status
{
Ok = 0, ///< Ok!
IOError, ///< Read or Write error
EOS, ///< End of Stream reached (mostly for reads)
IllegalCall, ///< An unsupported operation used. Always accompanied by AssertWarn
Closed, ///< Tried to operate on a closed stream (or detached filter)
UnknownError ///< Catchall
};
/// How are we accessing the file?
enum AccessMode
{
Read = 0, ///< Open for read only, starting at beginning of file.
Write = 1, ///< Open for write only, starting at beginning of file; will blast old contents of file.
ReadWrite = 2, ///< Open for read-write.
WriteAppend = 3 ///< Write-only, starting at end of file.
};
/// Flags used to indicate what we can do to the file.
enum Capability
{
FileRead = BIT(0),
FileWrite = BIT(1)
};
private:
void *handle; ///< Pointer to the file handle.
Status currentStatus; ///< Current status of the file (Ok, IOError, etc.).
U32 capability; ///< Keeps track of file capabilities.
File(const File&); ///< This is here to disable the copy constructor.
File& operator=(const File&); ///< This is here to disable assignment.
public:
File(); ///< Default constructor
virtual ~File(); ///< Destructor
/// Opens a file for access using the specified AccessMode
///
/// @returns The status of the file
Status open(const char *filename, const AccessMode openMode);
/// Gets the current position in the file
///
/// This is in bytes from the beginning of the file.
U32 getPosition() const;
/// Sets the current position in the file.
///
/// You can set either a relative or absolute position to go to in the file.
///
/// @code
/// File *foo;
///
/// ... set up file ...
///
/// // Go to byte 32 in the file...
/// foo->setPosition(32);
///
/// // Now skip back 20 bytes...
/// foo->setPosition(-20, false);
///
/// // And forward 17...
/// foo->setPosition(17, false);
/// @endcode
///
/// @returns The status of the file
Status setPosition(S32 position, bool absolutePos = true);
/// Returns the size of the file
U32 getSize() const;
/// Make sure everything that's supposed to be written to the file gets written.
///
/// @returns The status of the file.
Status flush();
/// Closes the file
///
/// @returns The status of the file.
Status close();
/// Gets the status of the file
Status getStatus() const;
/// Reads "size" bytes from the file, and dumps data into "dst".
/// The number of actual bytes read is returned in bytesRead
/// @returns The status of the file
Status read(U32 size, char *dst, U32 *bytesRead = NULL);
/// Writes "size" bytes into the file from the pointer "src".
/// The number of actual bytes written is returned in bytesWritten
/// @returns The status of the file
Status write(U32 size, const char *src, U32 *bytesWritten = NULL);
/// Returns whether or not this file is capable of the given function.
bool hasCapability(Capability cap) const;
const void* getHandle() { return handle; }
protected:
Status setStatus(); ///< Called after error encountered.
Status setStatus(Status status); ///< Setter for the current status.
};
#endif // _FILE_IO_H_

View file

@ -0,0 +1,78 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/filterStream.h"
FilterStream::~FilterStream()
{
//
}
bool FilterStream::_read(const U32 in_numBytes, void* out_pBuffer)
{
AssertFatal(getStream() != NULL, "Error no stream to pass to");
bool success = getStream()->read(in_numBytes, out_pBuffer);
setStatus(getStream()->getStatus());
return success;
}
bool FilterStream::_write(const U32, const void*)
{
AssertFatal(false, "No writing allowed to filter");
return false;
}
bool FilterStream::hasCapability(const Capability in_streamCap) const
{
// Fool the compiler. We know better...
FilterStream* ncThis = const_cast<FilterStream*>(this);
AssertFatal(ncThis->getStream() != NULL, "Error no stream to pass to");
return ncThis->getStream()->hasCapability(in_streamCap);
}
U32 FilterStream::getPosition() const
{
// Fool the compiler. We know better...
FilterStream* ncThis = const_cast<FilterStream*>(this);
AssertFatal(ncThis->getStream() != NULL, "Error no stream to pass to");
return ncThis->getStream()->getPosition();
}
bool FilterStream::setPosition(const U32 in_newPosition)
{
AssertFatal(getStream() != NULL, "Error no stream to pass to");
return getStream()->setPosition(in_newPosition);
}
U32 FilterStream::getStreamSize()
{
AssertFatal(getStream() != NULL, "Error no stream to pass to");
return getStream()->getStreamSize();
}

View file

@ -0,0 +1,56 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _FILTERSTREAM_H_
#define _FILTERSTREAM_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _STREAM_H_
#include "core/stream/stream.h"
#endif
class FilterStream : public Stream
{
public:
virtual ~FilterStream();
virtual bool attachStream(Stream* io_pSlaveStream) = 0;
virtual void detachStream() = 0;
virtual Stream* getStream() = 0;
// Mandatory overrides. By default, these are simply passed to
// whatever is returned from getStream();
protected:
bool _read(const U32 in_numBytes, void* out_pBuffer);
bool _write(const U32 in_numBytes, const void* in_pBuffer);
public:
bool hasCapability(const Capability) const;
U32 getPosition() const;
bool setPosition(const U32 in_newPosition);
U32 getStreamSize();
};
#endif //_FILTERSTREAM_H_

View file

@ -0,0 +1,37 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/frameAllocator.h"
#include "console/console.h"
U8* FrameAllocator::smBuffer = NULL;
U32 FrameAllocator::smWaterMark = 0;
U32 FrameAllocator::smHighWaterMark = 0;
#ifdef TORQUE_DEBUG
U32 FrameAllocator::smMaxFrameAllocation = 0;
ConsoleFunction(getMaxFrameAllocation, S32, 1,1, "getMaxFrameAllocation();")
{
return FrameAllocator::getMaxFrameAllocation();
}
#endif

View file

@ -0,0 +1,320 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _FRAMEALLOCATOR_H_
#define _FRAMEALLOCATOR_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
/// This #define is used by the FrameAllocator to align starting addresses to
/// be byte aligned to this value. This is important on the 360 and possibly
/// on other platforms as well. Use this #define anywhere alignment is needed.
///
/// NOTE: Do not change this value per-platform unless you have a very good
/// reason for doing so. It has the potential to cause inconsistencies in
/// memory which is allocated and expected to be contiguous.
#define FRAMEALLOCATOR_BYTE_ALIGNMENT 4
/// Temporary memory pool for per-frame allocations.
///
/// In the course of rendering a frame, it is often necessary to allocate
/// many small chunks of memory, then free them all in a batch. For instance,
/// say we're allocating storage for some vertex calculations:
///
/// @code
/// // Get FrameAllocator memory...
/// U32 waterMark = FrameAllocator::getWaterMark();
/// F32 * ptr = (F32*)FrameAllocator::alloc(sizeof(F32)*2*targetMesh->vertsPerFrame);
///
/// ... calculations ...
///
/// // Free frameAllocator memory
/// FrameAllocator::setWaterMark(waterMark);
/// @endcode
class FrameAllocator
{
static U8* smBuffer;
static U32 smHighWaterMark;
static U32 smWaterMark;
#ifdef TORQUE_DEBUG
static U32 smMaxFrameAllocation;
#endif
public:
inline static void init(const U32 frameSize);
inline static void destroy();
inline static void* alloc(const U32 allocSize);
inline static void setWaterMark(const U32);
inline static U32 getWaterMark();
inline static U32 getHighWaterMark();
#ifdef TORQUE_DEBUG
static U32 getMaxFrameAllocation() { return smMaxFrameAllocation; }
#endif
};
void FrameAllocator::init(const U32 frameSize)
{
#ifdef FRAMEALLOCATOR_DEBUG_GUARD
AssertISV( false, "FRAMEALLOCATOR_DEBUG_GUARD has been removed because it allows non-contiguous memory allocation by the FrameAllocator, and this is *not* ok." );
#endif
AssertFatal(smBuffer == NULL, "Error, already initialized");
smBuffer = new U8[frameSize];
smWaterMark = 0;
smHighWaterMark = frameSize;
}
void FrameAllocator::destroy()
{
AssertFatal(smBuffer != NULL, "Error, not initialized");
delete [] smBuffer;
smBuffer = NULL;
smWaterMark = 0;
smHighWaterMark = 0;
}
void* FrameAllocator::alloc(const U32 allocSize)
{
U32 _allocSize = allocSize;
AssertFatal(smBuffer != NULL, "Error, no buffer!");
AssertFatal(smWaterMark + _allocSize <= smHighWaterMark, "Error alloc too large, increase frame size!");
smWaterMark = ( smWaterMark + ( FRAMEALLOCATOR_BYTE_ALIGNMENT - 1 ) ) & (~( FRAMEALLOCATOR_BYTE_ALIGNMENT - 1 ));
// Sanity check.
AssertFatal( !( smWaterMark & ( FRAMEALLOCATOR_BYTE_ALIGNMENT - 1 ) ), "Frame allocation is not on a specified byte boundry." );
U8* p = &smBuffer[smWaterMark];
smWaterMark += _allocSize;
#ifdef TORQUE_DEBUG
if (smWaterMark > smMaxFrameAllocation)
smMaxFrameAllocation = smWaterMark;
#endif
return p;
}
void FrameAllocator::setWaterMark(const U32 waterMark)
{
AssertFatal(waterMark < smHighWaterMark, "Error, invalid waterMark");
smWaterMark = waterMark;
}
U32 FrameAllocator::getWaterMark()
{
return smWaterMark;
}
U32 FrameAllocator::getHighWaterMark()
{
return smHighWaterMark;
}
/// Helper class to deal with FrameAllocator usage.
///
/// The purpose of this class is to make it simpler and more reliable to use the
/// FrameAllocator. Simply use it like this:
///
/// @code
/// FrameAllocatorMarker mem;
///
/// char *buff = (char*)mem.alloc(100);
/// @endcode
///
/// When you leave the scope you defined the FrameAllocatorMarker in, it will
/// automatically restore the watermark on the FrameAllocator. In situations
/// with complex branches, this can be a significant headache remover, as you
/// don't have to remember to reset the FrameAllocator on every posssible branch.
class FrameAllocatorMarker
{
U32 mMarker;
public:
FrameAllocatorMarker()
{
mMarker = FrameAllocator::getWaterMark();
}
~FrameAllocatorMarker()
{
FrameAllocator::setWaterMark(mMarker);
}
void* alloc(const U32 allocSize) const
{
return FrameAllocator::alloc(allocSize);
}
template<typename T>
T* alloc(const U32 numElements) const
{
return reinterpret_cast<T *>(FrameAllocator::alloc(numElements * sizeof(T)));
}
};
/// Class for temporary variables that you want to allocate easily using
/// the FrameAllocator. For example:
/// @code
/// FrameTemp<char> tempStr(32); // NOTE! This parameter is NOT THE SIZE IN BYTES. See constructor docs.
/// dStrcat( tempStr, SomeOtherString );
/// tempStr[2] = 'l';
/// Con::printf( tempStr );
/// Con::printf( "Foo: %s", ~tempStr );
/// @endcode
///
/// This will automatically handle getting and restoring the watermark of the
/// FrameAllocator when it goes out of scope. You should notice the strange
/// operator in front of tempStr on the printf call. This is normally a unary
/// operator for ones-complement, but in this class it will simply return the
/// memory of the allocation. It's the same as doing (const char *)tempStr
/// in the above case. The reason why it is necessary for the second printf
/// and not the first is because the second one is taking a variable arg
/// list and so it isn't getting the cast so that it's cast operator can
/// properly return the memory instead of the FrameTemp object itself.
///
/// @note It is important to note that this object is designed to just be a
/// temporary array of a dynamic size. Some wierdness may occur if you try
/// to perform crazy pointer stuff with it using regular operators on it.
template<class T>
class FrameTemp
{
protected:
U32 mWaterMark;
T *mMemory;
U32 mNumObjectsInMemory;
public:
/// Constructor will store the FrameAllocator watermark and allocate the memory off
/// of the FrameAllocator.
///
/// @note It is important to note that, unlike the FrameAllocatorMarker and the
/// FrameAllocator itself, the argument to allocate is NOT the size in bytes,
/// doing:
/// @code
/// FrameTemp<F64> f64s(5);
/// @endcode
/// Is the same as
/// @code
/// F64 *f64s = new F64[5];
/// @endcode
///
/// @param count The number of objects to allocate
FrameTemp( const U32 count = 1 ) : mNumObjectsInMemory( count )
{
AssertFatal( count > 0, "Allocating a FrameTemp with less than one instance" );
mWaterMark = FrameAllocator::getWaterMark();
mMemory = reinterpret_cast<T *>( FrameAllocator::alloc( sizeof( T ) * count ) );
for( int i = 0; i < mNumObjectsInMemory; i++ )
constructInPlace<T>( &mMemory[i] );
}
/// Destructor restores the watermark
~FrameTemp()
{
// Call destructor
for( int i = 0; i < mNumObjectsInMemory; i++ )
destructInPlace<T>( &mMemory[i] );
FrameAllocator::setWaterMark( mWaterMark );
}
/// NOTE: This will return the memory, NOT perform a ones-complement
T* operator ~() { return mMemory; };
/// NOTE: This will return the memory, NOT perform a ones-complement
const T* operator ~() const { return mMemory; };
/// NOTE: This will dereference the memory, NOT do standard unary plus behavior
T& operator +() { return *mMemory; };
/// NOTE: This will dereference the memory, NOT do standard unary plus behavior
const T& operator +() const { return *mMemory; };
T& operator *() { return *mMemory; };
const T& operator *() const { return *mMemory; };
T** operator &() { return &mMemory; };
const T** operator &() const { return &mMemory; };
operator T*() { return mMemory; }
operator const T*() const { return mMemory; }
operator T&() { return *mMemory; }
operator const T&() const { return *mMemory; }
operator T() { return *mMemory; }
operator const T() const { return *mMemory; }
T& operator []( U32 i ) { return mMemory[ i ]; }
const T& operator []( U32 i ) const { return mMemory[ i ]; }
T& operator []( S32 i ) { return mMemory[ i ]; }
const T& operator []( S32 i ) const { return mMemory[ i ]; }
/// @name Vector-like Interface
/// @{
T *address() const { return mMemory; }
dsize_t size() const { return mNumObjectsInMemory; }
/// @}
};
//-----------------------------------------------------------------------------
// FrameTemp specializations for types with no constructor/destructor
#define FRAME_TEMP_NC_SPEC(type) \
template<> \
inline FrameTemp<type>::FrameTemp( const U32 count ) \
{ \
AssertFatal( count > 0, "Allocating a FrameTemp with less than one instance" ); \
mWaterMark = FrameAllocator::getWaterMark(); \
mMemory = reinterpret_cast<type *>( FrameAllocator::alloc( sizeof( type ) * count ) ); \
} \
template<>\
inline FrameTemp<type>::~FrameTemp() \
{ \
FrameAllocator::setWaterMark( mWaterMark ); \
} \
FRAME_TEMP_NC_SPEC(char);
FRAME_TEMP_NC_SPEC(float);
FRAME_TEMP_NC_SPEC(double);
FRAME_TEMP_NC_SPEC(bool);
FRAME_TEMP_NC_SPEC(int);
FRAME_TEMP_NC_SPEC(short);
FRAME_TEMP_NC_SPEC(unsigned char);
FRAME_TEMP_NC_SPEC(unsigned int);
FRAME_TEMP_NC_SPEC(unsigned short);
#undef FRAME_TEMP_NC_SPEC
//-----------------------------------------------------------------------------
#endif // _H_FRAMEALLOCATOR_

View file

@ -0,0 +1,111 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/iTickable.h"
// The statics
U32 ITickable::smLastTick = 0;
U32 ITickable::smLastTime = 0;
U32 ITickable::smLastDelta = 0;
U32 ITickable::smTickShift = 5;
U32 ITickable::smTickMs = ( 1 << smTickShift );
F32 ITickable::smTickSec = ( F32( ITickable::smTickMs ) / 1000.f );
U32 ITickable::smTickMask = ( smTickMs - 1 );
//------------------------------------------------------------------------------
ITickable::ITickable() : mProcessTick( true )
{
getProcessList().push_back( this );
}
//------------------------------------------------------------------------------
ITickable::~ITickable()
{
for( ProcessListIterator i = getProcessList().begin(); i != getProcessList().end(); i++ )
{
if( (*i) == this )
{
getProcessList().erase( i );
return;
}
}
}
//------------------------------------------------------------------------------
void ITickable::init( const U32 tickShift )
{
// Sanity!
AssertFatal( tickShift == 0 || tickShift <= 31, "ITickable::init() - Invalid 'tickShift' parameter!" );
// Calculate tick constants.
smTickShift = tickShift;
smTickMs = ( 1 << smTickShift );
smTickSec = ( F32( smTickMs ) / 1000.f );
smTickMask = ( smTickMs - 1 );
}
//------------------------------------------------------------------------------
Vector<ITickable *>& ITickable::getProcessList()
{
// This helps to avoid the static initialization order fiasco
static Vector<ITickable *> smProcessList( __FILE__, __LINE__ ); ///< List of tick controls
return smProcessList;
}
//------------------------------------------------------------------------------
bool ITickable::advanceTime( U32 timeDelta )
{
U32 targetTime = smLastTime + timeDelta;
U32 targetTick = ( targetTime + smTickMask ) & ~smTickMask;
U32 tickCount = ( targetTick - smLastTick ) >> smTickShift;
// Advance objects
if( tickCount )
for( ; smLastTick != targetTick; smLastTick += smTickMs )
for( ProcessListIterator i = getProcessList().begin(); i != getProcessList().end(); i++ )
if( (*i)->isProcessingTicks() )
(*i)->processTick();
smLastDelta = ( smTickMs - ( targetTime & smTickMask ) ) & smTickMask;
F32 dt = smLastDelta / F32( smTickMs );
// Now interpolate objects that want ticks
for( ProcessListIterator i = getProcessList().begin(); i != getProcessList().end(); i++ )
if( (*i)->isProcessingTicks() )
(*i)->interpolateTick( dt );
// Inform ALL objects that time was advanced
dt = F32( timeDelta ) / 1000.f;
for( ProcessListIterator i = getProcessList().begin(); i != getProcessList().end(); i++ )
(*i)->advanceTime( dt );
smLastTime = targetTime;
return tickCount != 0;
}

View file

@ -0,0 +1,171 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _ITICKABLE_H_
#define _ITICKABLE_H_
#include "core/util/tVector.h"
/// This interface allows you to let any object be ticked. You use it like so:
/// @code
/// class FooClass : public SimObject, public virtual ITickable
/// {
/// // You still mark SimObject as Parent
/// typdef SimObject Parent;
/// private:
/// ...
///
/// protected:
/// // These three methods are the interface for ITickable
/// virtual void interpolateTick( F32 delta );
/// virtual void processTick();
/// virtual void advanceTime( F32 timeDelta );
///
/// public:
/// ...
/// };
/// @endcode
/// Please note the three methods you must implement to use ITickable, but don't
/// worry. If you forget, the compiler will tell you so. Also note that the
/// typedef for Parent should NOT BE SET to ITickable, the compiler will <i>probably</i>
/// also tell you if you forget that. Last, but assuridly not least is that you note
/// the way that the inheretance is done: public <b>virtual</b> ITickable
/// It is very important that you keep the virtual keyword in there, otherwise
/// proper behavior is not guarenteed. You have been warned.
///
/// The point of a tickable object is that the object gets ticks at a fixed rate
/// which is one tick every 32ms. This means, also, that if an object doesn't get
/// updated for 64ms, that the next update it will get two-ticks. Basically it
/// comes down to this. You are assured to get one tick per 32ms of time passing
/// provided that isProcessingTicks returns true when ITickable calls it.
///
/// isProcessingTicks is a virtual method and you can (should you want to)
/// override it and put some extended functionality to decide if you want to
/// recieve tick-notification or not.
///
/// The other half of this is that you get time-notification from advanceTime.
/// advanceTime lets you know when time passes regardless of the return value
/// of isProcessingTicks. The object WILL get the advanceTime call every single
/// update. The argument passed to advanceTime is the time since the last call
/// to advanceTime. Updates are not based on the 32ms tick time. Updates are
/// dependant on framerate. So you may get 200 advanceTime calls in a second, or you
/// may only get 20. There is no way of assuring consistant calls of advanceTime
/// like there is with processTick. Both are useful for different things, and
/// it is important to understand the differences between them.
///
/// Interpolation is the last part of the ITickable interface. It is called
/// every update, as long as isProcessingTicks evaluates to true on the object.
/// This is used to interpolate between 32ms ticks. The argument passed to
/// interpolateTick is the time since the last call to processTick. You can see
/// in the code for ITickable::advanceTime that before a tick occurs it calls
/// interpolateTick(0) on every object. This is to tell objects which do interpolate
/// between ticks to reset their interpolation because they are about to get a
/// new tick.
///
/// This is an extremely powerful interface when used properly. An example of a class
/// that properly uses this interface is GuiTickCtrl. The documentation for that
/// class describes why it was created and why it was important that it use
/// a consistant update frequency for its effects.
/// @see GuiTickCtrl
///
/// @todo Support processBefore/After and move the GameBase processing over to use ITickable
class ITickable
{
private:
static U32 smLastTick; ///< Time of the last tick that occurred
static U32 smLastTime; ///< Last time value at which advanceTime was called
static U32 smLastDelta; ///< Last delta value for advanceTime
static U32 smTickShift; ///< Shift value to control how often Ticks occur
static U32 smTickMs; ///< Number of milliseconds per tick, 32 in this case
static F32 smTickSec; ///< Fraction of a second per tick
static U32 smTickMask;
// This just makes life easy
typedef Vector<ITickable *>::iterator ProcessListIterator;
/// Returns a reference to the list of all ITickable objects.
static Vector<ITickable *>& getProcessList();
bool mProcessTick; ///< Set to true if this object wants tick processing
protected:
/// This method is called every frame and lets the control interpolate between
/// ticks so you can smooth things as long as isProcessingTicks returns true
/// when it is called on the object
virtual void interpolateTick( F32 delta ) = 0;
/// This method is called once every 32ms if isProcessingTicks returns true
/// when called on the object
virtual void processTick() = 0;
/// This method is called once every frame regardless of the return value of
/// isProcessingTicks and informs the object of the passage of time.
/// @param timeDelta Time increment in seconds.
virtual void advanceTime( F32 timeDelta ) = 0;
public:
/// Constructor
/// This will add the object to the process list
ITickable();
/// Destructor
/// Remove this object from the process list
virtual ~ITickable();
/// Is this object wanting to receive tick notifications
/// @returns True if object wants tick notifications
bool isProcessingTicks() const { return mProcessTick; };
/// Sets this object as either tick processing or not
/// @param tick True if this object should process ticks
virtual void setProcessTicks( bool tick = true );
/// Initialise the ITickable system.
static void init( const U32 tickShift );
/// Gets the Tick bit-shift.
static U32 getTickShift() { return smTickShift; }
/// Gets the Tick (ms)
static U32 getTickMs() { return smTickMs; }
/// Gets the Tick (seconds)
static F32 getTickSec() { return smTickSec; }
/// Gets the Tick mask.
static U32 getTickMask() { return smTickMask; }
//------------------------------------------------------------------------------
/// This is called in clientProcess to advance the time for all ITickable
/// objects.
/// @param timeDelta Time increment in milliseconds.
/// @returns True if any ticks were sent
/// @see clientProcess
static bool advanceTime( U32 timeDelta );
};
//------------------------------------------------------------------------------
inline void ITickable::setProcessTicks( bool tick /* = true */ )
{
mProcessTick = tick;
}
#endif

View file

@ -0,0 +1,36 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/idGenerator.h"
void IdGenerator::reclaim()
{
// attempt to keep the pool vector as small as possible by reclaiming
// pool entries back into the nextIdBlock variable
while (!mPool.empty() && (mPool.last() == (mNextId-1)) )
{
mNextId--;
mPool.pop_back();
}
}

View file

@ -0,0 +1,93 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _IDGENERATOR_H_
#define _IDGENERATOR_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
class IdGenerator
{
private:
U32 mIdBlockBase;
U32 mIdRangeSize;
Vector<U32> mPool;
U32 mNextId;
void reclaim();
public:
IdGenerator(U32 base, U32 numIds)
{
VECTOR_SET_ASSOCIATION(mPool);
mIdBlockBase = base;
mIdRangeSize = numIds;
mNextId = mIdBlockBase;
}
void reset()
{
mPool.clear();
mNextId = mIdBlockBase;
}
U32 alloc()
{
// fist check the pool:
if(!mPool.empty())
{
U32 id = mPool.last();
mPool.pop_back();
reclaim();
return id;
}
if(mIdRangeSize && mNextId >= mIdBlockBase + mIdRangeSize)
return 0;
return mNextId++;
}
void free(U32 id)
{
AssertFatal(id >= mIdBlockBase, "IdGenerator::alloc: invalid id, id does not belong to this IdGenerator.")
if(id == mNextId - 1)
{
mNextId--;
reclaim();
}
else
mPool.push_back(id);
}
U32 numIdsUsed()
{
return mNextId - mIdBlockBase - mPool.size();
}
};
#endif

View file

@ -0,0 +1,511 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/memVolume.h"
#include "core/crc.h"
#include "core/frameAllocator.h"
#include "core/util/str.h"
#include "core/strings/stringFunctions.h"
#include "platform/platformVolume.h"
namespace Torque
{
namespace Mem
{
// Multiple MemFile's can reference the same path, so this is here to contain
// the actual data at a Path.
struct MemFileData
{
MemFileData(MemFileSystem* fs, const Path& path)
{
mPath = path;
mBufferSize = 1024;
mFileSize = 0;
mBuffer = dMalloc(mBufferSize);
dMemset(mBuffer, 0, mBufferSize);
mModified = Time::getCurrentTime();
mLastAccess = mModified;
mFileSystem = fs;
}
~MemFileData()
{
dFree(mBuffer);
}
bool getAttributes(FileNode::Attributes* attr)
{
attr->name = mPath;
attr->flags = FileNode::File;
attr->size = mFileSize;
attr->mtime = mModified;
attr->atime = mLastAccess;
return true;
}
FileNodeRef resolve(const Path& path)
{
// Is it me?
String sThisPath(mPath);
String sTargetPath(path);
if (sThisPath == sTargetPath)
return new MemFile(mFileSystem, this);
// Nope
return NULL;
}
Path mPath;
void* mBuffer;
U32 mBufferSize; // This is the size of the memory buffer >= mFileSize
U32 mFileSize; // This is the size of the "file" <= mBufferSize
Time mModified; // Last modified
Time mLastAccess; // Last access
MemFileSystem* mFileSystem;
};
struct MemDirectoryData
{
Path mPath;
MemFileSystem* mFileSystem;
Vector<MemFileData*> mFiles;
Vector<MemDirectoryData*> mDirectories;
MemDirectoryData(MemFileSystem* fs, const Path& path)
{
mFileSystem = fs;
mPath = path;
}
~MemDirectoryData()
{
for (U32 i = 0; i < mFiles.size(); i++)
{
delete mFiles[i];
}
for (U32 i = 0; i < mDirectories.size(); i++)
{
delete mDirectories[i];
}
}
bool getAttributes(FileNode::Attributes* attr)
{
attr->name = mPath;
attr->flags = FileNode::Directory;
return true;
}
FileNodeRef resolve(const Path& path)
{
// Is it me?
String sThisPath(mPath);
String sTargetPath(path);
if (sThisPath == sTargetPath)
return new MemDirectory(mFileSystem, this);
// Is it one of my children?
if (sTargetPath.find(sThisPath) == 0)
{
FileNodeRef result;
for (U32 i = 0; i < mDirectories.size() && result.isNull(); i++)
result = mDirectories[i]->resolve(path);
for (U32 i = 0; i < mFiles.size() && result.isNull(); i++)
result = mFiles[i]->resolve(path);
return result;
}
// Nope
return NULL;
}
};
//-----------------------------------------------------------------------------
MemFileSystem::MemFileSystem(String volume)
{
mVolume = volume;
mRootDir = new MemDirectoryData(this, volume);
}
MemFileSystem::~MemFileSystem()
{
delete mRootDir;
}
FileNodeRef MemFileSystem::resolve(const Path& path)
{
return mRootDir->resolve(path);
}
//
MemDirectory* MemFileSystem::getParentDir(const Path& path, FileNodeRef& parentRef)
{
parentRef = mRootDir->resolve(path.getRoot() + ":" + path.getPath());
if (parentRef.isNull())
return NULL;
MemDirectory* result = dynamic_cast<MemDirectory*>(parentRef.getPointer());
return result;
}
FileNodeRef MemFileSystem::create(const Path& path, FileNode::Mode mode)
{
// Already exists
FileNodeRef result = mRootDir->resolve(path);
if (result.isValid())
return result;
// Doesn't exist, try to get parent node.
FileNodeRef parentRef;
MemDirectory* mDir = getParentDir(path, parentRef);
if (mDir)
{
MemDirectoryData* mdd = mDir->mDirectoryData;
switch (mode)
{
case FileNode::File :
{
MemFileData* mfd = new MemFileData(this, path);
mdd->mFiles.push_back(mfd);
return new MemFile(this, mfd);
}
break;
case FileNode::Directory :
{
MemDirectoryData* mfd = new MemDirectoryData(this, path);
mdd->mDirectories.push_back(mfd);
return new MemDirectory(this, mfd);
}
break;
default:
// anything else we ignore
break;
}
}
return NULL;
}
bool MemFileSystem::remove(const Path& path)
{
FileNodeRef parentRef;
MemDirectory* mDir = getParentDir(path, parentRef);
MemDirectoryData* mdd = mDir->mDirectoryData;
for (U32 i = 0; i < mdd->mDirectories.size(); i++)
{
if (mdd->mDirectories[i]->mPath == path)
{
delete mdd->mDirectories[i];
mdd->mDirectories.erase_fast(i);
return true;
}
}
for (U32 i = 0; i < mdd->mFiles.size(); i++)
{
if (mdd->mFiles[i]->mPath == path)
{
delete mdd->mFiles[i];
mdd->mFiles.erase_fast(i);
return true;
}
}
return false;
}
bool MemFileSystem::rename(const Path& from,const Path& to)
{
// Source must exist
FileNodeRef source = mRootDir->resolve(from);
if (source.isNull())
return false;
// Destination must not exist
FileNodeRef dest = mRootDir->resolve(to);
if (source.isValid())
return false;
// Get source parent
FileNodeRef sourceParentRef;
MemDirectory* sourceDir = getParentDir(from, sourceParentRef);
// Get dest parent
FileNodeRef destRef;
MemDirectory* mDir = getParentDir(to, destRef);
// Now move it/rename it
if (dynamic_cast<MemDirectory*>(source.getPointer()))
{
MemDirectoryData* sourcedd;
MemDirectoryData* d = sourceDir->mDirectoryData;
for (U32 i = 0; i < d->mDirectories.size(); i++)
{
if (d->mDirectories[i]->mPath == from)
{
sourcedd = d->mDirectories[i];
d->mDirectories.erase_fast(i);
sourcedd->mPath = to;
mDir->mDirectoryData->mDirectories.push_back(sourcedd);
return true;
}
}
} else {
MemFileData* sourceFile;
MemDirectoryData* d = sourceDir->mDirectoryData;
for (U32 i = 0; i < d->mFiles.size(); i++)
{
if (d->mFiles[i]->mPath == from)
{
sourceFile = d->mFiles[i];
d->mFiles.erase_fast(i);
sourceFile->mPath = to;
mDir->mDirectoryData->mFiles.push_back(sourceFile);
return true;
}
}
}
return false;
}
Path MemFileSystem::mapTo(const Path& path)
{
String file = mVolume;
file = Path::Join(file, '/', path.getPath());
file = Path::Join(file, '/', path.getFileName());
file = Path::Join(file, '.', path.getExtension());
return file;
}
Path MemFileSystem::mapFrom(const Path& path)
{
const String::SizeType volumePathLen = mVolume.length();
String pathStr = path.getFullPath();
if ( mVolume.compare( pathStr, volumePathLen, String::NoCase ))
return Path();
return pathStr.substr( volumePathLen, pathStr.length() - volumePathLen );
}
//-----------------------------------------------------------------------------
MemFile::MemFile(MemFileSystem* fs, MemFileData* fileData)
{
mFileData = fileData;
mStatus = Closed;
mCurrentPos = U32_MAX;
mFileSystem = fs;
}
MemFile::~MemFile()
{
}
Path MemFile::getName() const
{
return mFileData->mPath;
}
FileNode::Status MemFile::getStatus() const
{
return mStatus;
}
bool MemFile::getAttributes(Attributes* attr)
{
return mFileData->getAttributes(attr);
}
U32 MemFile::calculateChecksum()
{
return CRC::calculateCRC(mFileData->mBuffer, mFileData->mFileSize);
}
bool MemFile::open(AccessMode mode)
{
mStatus = Open;
mCurrentPos = 0;
switch (mode)
{
case Read :
case ReadWrite :
mCurrentPos = 0;
break;
case Write :
mCurrentPos = 0;
mFileData->mFileSize = 0;
break;
case WriteAppend :
mCurrentPos = mFileData->mFileSize;
break;
}
return true;
}
bool MemFile::close()
{
mStatus = Closed;
return true;
}
U32 MemFile::getPosition()
{
if (mStatus == Open || mStatus == EndOfFile)
return mCurrentPos;
return 0;
}
U32 MemFile::setPosition(U32 delta, SeekMode mode)
{
if (mStatus != Open && mStatus != EndOfFile)
return 0;
switch (mode)
{
case Begin:
mCurrentPos = delta;
break;
case Current:
mCurrentPos += delta;
break;
case End:
mCurrentPos = mFileData->mFileSize - delta;
break;
}
mStatus = Open;
return mCurrentPos;
}
U32 MemFile::read(void* dst, U32 size)
{
if (mStatus != Open && mStatus != EndOfFile)
return 0;
U32 copyAmount = getMin(size, mFileData->mFileSize - mCurrentPos);
dMemcpy(dst, (U8*) mFileData->mBuffer + mCurrentPos, copyAmount);
mCurrentPos += copyAmount;
mFileData->mLastAccess = Time::getCurrentTime();
if (mCurrentPos == mFileData->mFileSize)
mStatus = EndOfFile;
return copyAmount;
}
U32 MemFile::write(const void* src, U32 size)
{
if ((mStatus != Open && mStatus != EndOfFile) || !size)
return 0;
if (mFileData->mFileSize + size > mFileData->mBufferSize)
{
// Keep doubling our buffer size until we're big enough.
while (mFileData->mFileSize + size > mFileData->mBufferSize)
mFileData->mBufferSize *= 2;
mFileData->mBuffer = dRealloc(mFileData->mBuffer, mFileData->mBufferSize);
if (!mFileData->mBuffer)
{
mStatus = FileSystemFull;
return 0;
}
}
dMemcpy((U8*)mFileData->mBuffer + mCurrentPos, src, size);
mCurrentPos += size;
mFileData->mFileSize = getMax(mFileData->mFileSize, mCurrentPos);
mFileData->mLastAccess = Time::getCurrentTime();
mFileData->mModified = mFileData->mLastAccess;
return size;
}
//-----------------------------------------------------------------------------
MemDirectory::MemDirectory(MemFileSystem* fs, MemDirectoryData* dir)
{
mStatus = Closed;
mDirectoryData = dir;
mFileSystem = fs;
}
MemDirectory::~MemDirectory()
{
}
Path MemDirectory::getName() const
{
return mDirectoryData->mPath;
}
bool MemDirectory::open()
{
mSearchIndex = 0;
mStatus = Open;
return true;
}
bool MemDirectory::close()
{
return true;
}
bool MemDirectory::read(Attributes* entry)
{
if (mStatus != Open)
return false;
if (mSearchIndex < mDirectoryData->mDirectories.size())
{
mDirectoryData->mDirectories[mSearchIndex]->getAttributes(entry);
mSearchIndex++;
return true;
}
AssertFatal(mSearchIndex > mDirectoryData->mDirectories.size(), "This should not happen!");
U32 fileIndex = mSearchIndex - mDirectoryData->mDirectories.size();
if (fileIndex < mDirectoryData->mFiles.size())
{
mDirectoryData->mFiles[mSearchIndex]->getAttributes(entry);
mSearchIndex++;
return true;
}
return false;
}
U32 MemDirectory::calculateChecksum()
{
// Return checksum of current entry
return 0;
}
bool MemDirectory::getAttributes(Attributes* attr)
{
return mDirectoryData->getAttributes(attr);
}
FileNode::Status MemDirectory::getStatus() const
{
return mStatus;
}
} // Namespace Mem
} // Namespace Torque

View file

@ -0,0 +1,132 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _MEMVOLUME_H_
#define _MEMVOLUME_H_
#ifndef _VOLUME_H_
#include "core/volume.h"
#endif
#ifndef _TDICTIONARY_H_
#include "core/util/tDictionary.h"
#endif
namespace Torque
{
using namespace FS;
namespace Mem
{
struct MemFileData;
struct MemDirectoryData;
class MemDirectory;
//-----------------------------------------------------------------------------
class MemFileSystem: public FileSystem
{
public:
MemFileSystem(String volume);
~MemFileSystem();
String getTypeStr() const { return "Mem"; }
FileNodeRef resolve(const Path& path);
FileNodeRef create(const Path& path,FileNode::Mode);
bool remove(const Path& path);
bool rename(const Path& from,const Path& to);
Path mapTo(const Path& path);
Path mapFrom(const Path& path);
private:
String mVolume;
MemDirectoryData* mRootDir;
MemDirectory* getParentDir(const Path& path, FileNodeRef& parentRef);
};
//-----------------------------------------------------------------------------
/// Mem stdio file access.
/// This class makes use the fopen, fread and fwrite for buffered io.
class MemFile: public File
{
public:
MemFile(MemFileSystem* fs, MemFileData* fileData);
virtual ~MemFile();
Path getName() const;
Status getStatus() const;
bool getAttributes(Attributes*);
U32 getPosition();
U32 setPosition(U32,SeekMode);
bool open(AccessMode);
bool close();
U32 read(void* dst, U32 size);
U32 write(const void* src, U32 size);
private:
U32 calculateChecksum();
MemFileSystem* mFileSystem;
MemFileData* mFileData;
Status mStatus;
U32 mCurrentPos;
bool _updateInfo();
void _updateStatus();
};
//-----------------------------------------------------------------------------
class MemDirectory: public Directory
{
public:
MemDirectory(MemFileSystem* fs, MemDirectoryData* dir);
~MemDirectory();
Path getName() const;
Status getStatus() const;
bool getAttributes(Attributes*);
bool open();
bool close();
bool read(Attributes*);
private:
friend class MemFileSystem;
MemFileSystem* mFileSystem;
MemDirectoryData* mDirectoryData;
U32 calculateChecksum();
Status mStatus;
U32 mSearchIndex;
};
} // Namespace
} // Namespace
#endif

View file

@ -0,0 +1,385 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/module.h"
#include "core/util/tVector.h"
#include "core/strings/stringFunctions.h"
//#define DEBUG_SPEW
#define DEBUG_SPEW_LEVEL 2
Module* Module::smFirst;
//-----------------------------------------------------------------------------
bool Module::_constrainedToComeBefore( Module* module, Mode mode )
{
if( module == this )
return false;
for( Dependency* dependency = _getDependencies( mode );
dependency != NULL; dependency = dependency->mNext )
{
Module* depModule = dependency->mModule;
if( !depModule )
{
depModule = ModuleManager::findModule( dependency->mModuleName );
if( !depModule )
{
// Module does not exist. Only emit a warning here so that modules
// can be omitted from a link without requiring the module definitions
// to be adapted.
Platform::outputDebugString( "[ModuleManager] Module %s of '%s' depends on module '%s' which does not exist",
mode == Module::ModeInitialize ? "init" : "shutdown",
module->getName(), dependency->mModuleName );
continue;
}
dependency->mModule = depModule;
}
if( dependency->mType == DependencyBefore )
{
if( depModule == module
|| depModule->_constrainedToComeBefore( module, mode ) )
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
bool Module::_constrainedToComeAfter( Module* module, Mode mode )
{
if( module == this )
return false;
for( Dependency* dependency = _getDependencies( mode );
dependency != NULL; dependency = dependency->mNext )
{
Module* depModule = dependency->mModule;
if( !depModule )
{
depModule = ModuleManager::findModule( dependency->mModuleName );
if( !depModule )
{
// Module does not exist. Only emit a warning here so that modules
// can be omitted from a link without requiring the module definitions
// to be adapted.
Platform::outputDebugString( "[ModuleManager] Module %s of '%s' depends on module '%s' which does not exist",
mode == Module::ModeInitialize ? "init" : "shutdown",
module->getName(), dependency->mModuleName );
continue;
}
dependency->mModule = depModule;
}
if( dependency->mType == DependencyAfter )
{
if( depModule == module
|| depModule->_constrainedToComeAfter( module, mode ) )
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
String ModuleManager::_moduleListToString( Vector< Module* >& moduleList )
{
StringBuilder str;
const U32 numModules = moduleList.size();
bool isFirst = true;
for( U32 i = 0; i < numModules; ++ i )
{
if( !isFirst )
str.append( " -> " );
str.append( moduleList[ i ]->getName() );
isFirst = false;
}
return str.end();
}
//-----------------------------------------------------------------------------
void ModuleManager::_printModuleList( Vector< Module* >& moduleList )
{
Platform::outputDebugString( _moduleListToString( moduleList ) );
}
//-----------------------------------------------------------------------------
void ModuleManager::_insertIntoModuleList( Module::Mode mode, Vector< Module* >& moduleList, Module* module )
{
// If this module is being overridden, switch over to
// the module overriding it.
Module* override;
do
{
override = _findOverrideFor( module );
if( override )
module = override;
}
while( override != NULL );
// If we are already on the list, return.
if( _getIndexOfModuleInList( moduleList, module ) != -1 )
return;
// If we don't have dependencies, just push the module
// to the back of the list.
if( !module->_getDependencies( mode ) )
{
#if defined( DEBUG_SPEW ) && DEBUG_SPEW_LEVEL > 1
Platform::outputDebugString( "[ModuleManager] Appending '%s' to '%s'",
module->getName(), _moduleListToString( moduleList ).c_str() );
#endif
moduleList.push_back( module );
return;
}
// First make sure that all 'after' dependencies are in the list.
#if defined( DEBUG_SPEW ) && DEBUG_SPEW_LEVEL > 1
Platform::outputDebugString( "[ModuleManager] Resolving %s dependencies of '%s'",
mode == Module::ModeInitialize ? "init" : "shutdown",
module->getName() );
#endif
for( Module::Dependency* dependency = module->_getDependencies( mode );
dependency != NULL; dependency = dependency->mNext )
{
if( dependency->mType != Module::DependencyAfter )
continue;
dependency->mModule = findModule( dependency->mModuleName );
if( !dependency->mModule )
continue; // Allow modules to not exist.
if( _getIndexOfModuleInList( moduleList, dependency->mModule ) == -1 )
_insertIntoModuleList( mode, moduleList, dependency->mModule );
}
AssertFatal( _getIndexOfModuleInList( moduleList, module ) == -1,
avar( "ModuleManager::_insertModuleIntoList - Cycle in 'after' %s dependency chain of '%s'",
mode == Module::ModeInitialize ? "init" : "shutdown",
module->getName() ) );
// Now add the module itself.
const U32 numModules = moduleList.size();
for( U32 i = 0; i < numModules; ++ i )
{
const bool thisBeforeCurrent = module->_constrainedToComeBefore( moduleList[ i ], mode );
const bool currentAfterThis = moduleList[ i ]->_constrainedToComeAfter( module, mode );
AssertFatal( !( thisBeforeCurrent && currentAfterThis ),
avar( "ModuleManager::_insertModuleIntoList - Ambiguous %s placement of module '%s' relative to '%s'",
mode == Module::ModeInitialize ? "init" : "shutdown",
module->getName(), moduleList[ i ]->getName() ) );
// If no contraints relate us to this module,
// push us one more position back in the line.
if( !thisBeforeCurrent && !currentAfterThis )
continue;
// If this module is contrained to come before the
// module at our current position but that module does
// not actually have dependencies of its own, make sure
// that module is at the back of the module list so that
// if we have more dependencies, it will not prevent us
// from correctly positioning us in relation to them.
if( thisBeforeCurrent && !moduleList[ i ]->_getDependencies( mode ) && i != numModules - 1 )
{
#if defined( DEBUG_SPEW ) && DEBUG_SPEW_LEVEL > 1
Platform::outputDebugString( "[ModuleManager] Pushing '%s' to back end of chain for resolving '%s'",
moduleList[ i ]->getName(), module->getName() );
#endif
Module* depModule = moduleList[ i ];
moduleList.erase( i );
-- i;
moduleList.push_back( depModule );
continue;
}
// Try the reverse constraint with all remaining modules in the list.
// If there is one for which we have one, then the placement of this
// module is ambiguous.
for( U32 n = i + 1; n < numModules; ++ n )
AssertFatal( !( moduleList[ n ]->_constrainedToComeBefore( module, mode )
|| module->_constrainedToComeAfter( moduleList[ n ], mode ) ),
avar( "ModuleManager::_insertModuleIntoList - Ambiguous %s constraint on module '%s' to come before '%s' yet after '%s'",
mode == Module::ModeInitialize ? "init" : "shutdown",
module->getName(),
moduleList[ i ]->getName(),
moduleList[ n ]->getName() ) );
// Add the module at this position.
#if defined( DEBUG_SPEW ) && DEBUG_SPEW_LEVEL > 1
Platform::outputDebugString( "[ModuleManager] Inserting '%s' at index %i into '%s'",
module->getName(), i, _moduleListToString( moduleList ).c_str() );
#endif
moduleList.insert( i, module );
return;
}
// No constraint-based position. Just append.
#if defined( DEBUG_SPEW ) && DEBUG_SPEW_LEVEL > 1
Platform::outputDebugString( "[ModuleManager] Appending '%s' to '%s'",
module->getName(), _moduleListToString( moduleList ).c_str() );
#endif
moduleList.push_back( module );
}
//-----------------------------------------------------------------------------
Module* ModuleManager::_findOverrideFor( Module* module )
{
const char* name = module->getName();
for( Module* ptr = Module::smFirst; ptr != NULL; ptr = ptr->mNext )
for( Module::Override* override = ptr->mOverrides; override != NULL; override = override->mNext )
if( dStricmp( override->mModuleName, name ) == 0 )
return ptr;
return NULL;
}
//-----------------------------------------------------------------------------
S32 ModuleManager::_getIndexOfModuleInList( Vector< Module* >& moduleList, Module* module )
{
const U32 numModules = moduleList.size();
for( U32 i = 0; i < numModules; ++ i )
if( moduleList[ i ] == module )
return i;
return -1;
}
//-----------------------------------------------------------------------------
S32 ModuleManager::_getIndexOfModuleInList( Vector< Module* >& moduleList, const char* moduleName )
{
const U32 numModules = moduleList.size();
for( U32 i = 0; i < numModules; ++ i )
if( dStricmp( moduleList[ i ]->getName(), moduleName ) == 0 )
return i;
return -1;
}
//-----------------------------------------------------------------------------
void ModuleManager::_createModuleList( Module::Mode mode, Vector< Module* >& moduleList )
{
for( Module* module = Module::smFirst; module != NULL; module = module->mNext )
_insertIntoModuleList( mode, moduleList, module );
}
//-----------------------------------------------------------------------------
void ModuleManager::initializeSystem()
{
Vector< Module* > modules;
_createModuleList( Module::ModeInitialize, modules );
const U32 numModules = modules.size();
for( U32 i = 0; i < numModules; ++ i )
{
Module* module = modules[ i ];
if( !module->mIsInitialized )
{
#ifdef DEBUG_SPEW
Platform::outputDebugString( "[ModuleManager] Initializing %s",
module->getName() );
#endif
module->initialize();
module->mIsInitialized = true;
}
}
}
//-----------------------------------------------------------------------------
void ModuleManager::shutdownSystem()
{
Vector< Module* > modules;
_createModuleList( Module::ModeShutdown, modules );
const U32 numModules = modules.size();
for( U32 i = 0; i < numModules; ++ i )
{
if( modules[ i ]->mIsInitialized )
{
#ifdef DEBUG_SPEW
Platform::outputDebugString( "[ModuleManager] Shutting down %s",
modules[ i ]->getName() );
#endif
modules[ i ]->shutdown();
modules[ i ]->mIsInitialized = false;
}
}
}
//-----------------------------------------------------------------------------
Module* ModuleManager::findModule( const char* name )
{
for( Module* module = Module::smFirst; module != NULL; module = module->mNext )
if( dStricmp( module->getName(), name ) == 0 )
return module;
return NULL;
}

357
Engine/source/core/module.h Normal file
View file

@ -0,0 +1,357 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _MODULE_H_
#define _MODULE_H_
#ifndef _TSINGLETON_H_
#include "core/util/tSingleton.h"
#endif
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
/// @file
/// A system for keeping initialization and shutdown modular while
/// avoiding non-trivial global constructors/destructors.
/// An engine component that requires initialization and/or cleanup.
class Module
{
public:
typedef void Parent;
friend struct ModuleManager;
protected:
struct Dependency;
friend struct Dependency;
enum Mode
{
ModeInitialize,
ModeShutdown
};
/// Direction of a dependency edge.
enum DependencyType
{
DependencyBefore,
DependencyAfter
};
/// Entry in the list of dependencies.
struct Dependency
{
/// Direction of dependence. A "before" dependence goes the reverse direction.
DependencyType mType;
/// Name of the module that this module depends on.
const char* mModuleName;
/// Pointer to module. Filled by init code.
Module* mModule;
/// Next dependency or NULL.
Dependency* mNext;
Dependency( Mode mode, DependencyType type, Module* parentModule, const char* moduleName )
: mType( type ),
mNext( mode == ModeInitialize ? parentModule->mInitDependencies : parentModule->mShutdownDependencies ),
mModuleName( moduleName ),
mModule( NULL )
{
if( mode == ModeInitialize )
parentModule->mInitDependencies = this;
else
parentModule->mShutdownDependencies = this;
}
};
/// Record for module that this module overrides.
struct Override
{
/// Name of module being overridden.
const char* mModuleName;
/// Next override or NULL.
Override* mNext;
Override( Module* parentModule, const char* moduleName )
: mModuleName( moduleName ),
mNext( parentModule->mOverrides )
{
parentModule->mOverrides = this;
}
};
/// Flag to make sure we don't shutdown modules that have not been initialized.
bool mIsInitialized;
/// Next module in the global module list.
Module* mNext;
/// List of modules to which the initialization of this module has dependency relations.
Dependency* mInitDependencies;
/// List of modules to which the shutdown of this module has dependency relations.
Dependency* mShutdownDependencies;
/// List of modules being overriden by this module.
Override* mOverrides;
/// Global list of modules.
static Module* smFirst;
/// Return true if this module is constrained to precede "module" in the given "mode".
bool _constrainedToComeBefore( Module* module, Mode mode );
/// Return true if this module is constrained to follow "module" in the given "mode".
bool _constrainedToComeAfter( Module* module, Mode mode );
///
Dependency* _getDependencies( Mode mode )
{
if( mode == ModeInitialize )
return mInitDependencies;
else
return mShutdownDependencies;
}
Module()
: mNext( smFirst ),
mInitDependencies( NULL ),
mShutdownDependencies( NULL ),
mOverrides( NULL ),
mIsInitialized( false )
{
smFirst = this;
}
public:
/// Return the module name.
virtual const char* getName() const = 0;
/// Initialize the module. This is only called after all modules that this
/// module depends on have been initialized.
virtual void initialize() {}
/// Shut down the module. This is called before any module that this module
/// depends on have been shut down.
virtual void shutdown() {}
};
/// Begin a module definition.
///
/// @code
/// MODULE_BEGIN( MyModule )
///
/// MODULE_INIT_AFTER( Sim )
/// MODULE_INIT_BEFORE( 3D )
/// MODULE_SHUTDOWN_BEFORE( Sim )
///
/// MODULE_INIT
/// {
/// // Init code...
/// }
///
/// MODULE_SHUTDOWN
/// {
/// // Cleanup code...
/// }
///
/// MODULE_END;
/// @endcode
#define MODULE_BEGIN( name ) \
namespace { namespace _ ## name { \
class _ModuleInst : public ::Module { \
public: \
typedef ::Module Parent; \
static _ModuleInst smInstance; \
virtual const char* getName() const { return #name; }
/// Make sure this module is initialized before the module called "name".
///
/// @code
/// MODULE_BEGIN( MyModule )
/// MODULE_INIT_BEFORE( MyOtherModule )
/// MODULE_END;
/// @endcode
#define MODULE_INIT_BEFORE( name ) \
struct _DepInitBefore ## name : public Parent::Dependency \
{ \
_DepInitBefore ## name() \
: Parent::Dependency( ModeInitialize, DependencyBefore, &smInstance, #name ) {} \
} mDepInitBefore ## name;
/// Make sure this module is initialized after the module called "name".
///
/// @code
/// MODULE_BEGIN( MyModule )
/// MODULE_INIT_AFTER( MyOtherModule )
/// MODULE_END;
/// @endcode
#define MODULE_INIT_AFTER( name ) \
struct _DepInitAfter ## name : public Parent::Dependency \
{ \
_DepInitAfter ## name() \
: Parent::Dependency( ModeInitialize, DependencyAfter, &smInstance, #name ) {} \
} mDepInitAfter ## name;
/// Make sure this module is initialized before the module called "name".
///
/// @code
/// MODULE_BEGIN( MyModule )
/// MODULE_SHUTDOWN_BEFORE( MyOtherModule )
/// MODULE_END;
/// @endcode
#define MODULE_SHUTDOWN_BEFORE( name ) \
struct _DepShutdownBefore ## name : public Parent::Dependency \
{ \
_DepShutdownBefore ## name() \
: Parent::Dependency( ModeShutdown, DependencyBefore, &smInstance, #name ) {} \
} mDepShutdownBefore ## name;
/// Make sure this module is initialized after the module called "name".
///
/// @code
/// MODULE_BEGIN( MyModule )
/// MODULE_SHUTDOWN_AFTER( MyOtherModule )
/// MODULE_END;
/// @endcode
#define MODULE_SHUTDOWN_AFTER( name ) \
struct _DepShutdownAfter ## name : public Parent::Dependency \
{ \
_DepShutdownAfter ## name() \
: Parent::Dependency( ModeShutdown, DependencyAfter, &smInstance, #name ) {} \
} mDepShutdownAfter ## name;
/// Replace the given module in both the init and the shutdown sequence.
///
/// @code
/// MODULE_BEGIN( MyMoveManager )
/// MODULE_OVERRIDE( MoveManager )
/// MODULE_END;
/// @endcode
#define MODULE_OVERRIDE( name ) \
struct _Override ## name : public Parent::Override \
{ \
_Override ## name() \
: Parent::Override( &smInstance, #name ) {} \
} mOverride ## name;
/// Define initialization code for the module.
///
/// @code
/// MODULE_BEGIN( MyModule )
/// MODULE_INIT
/// {
/// // Init code goes here.
/// }
/// MODULE_END;
/// @endcode
#define MODULE_INIT \
virtual void initialize()
/// Define cleanup code for the module.
///
/// @code
/// MODULE_BEGIN( MyModule )
/// MODULE_SHUTDOWN
/// {
/// // Cleanup code goes here.
/// }
/// MODULE_END;
/// @endcode
#define MODULE_SHUTDOWN \
virtual void shutdown()
/// Terminate a module definition.
///
/// @code
/// MODULE_BEGIN( MyModule )
/// MODULE_END;
/// @endcode
#define MODULE_END \
}; \
_ModuleInst _ModuleInst::smInstance; \
} }
/// Used to define a function which will be called right
/// after the named module is initialized.
///
/// @code
/// AFTER_MODULE_INIT( Sim )
/// {
/// Con::addVariable( "$myBool", TypeBool, &smMyBool );
/// }
/// @endcode
///
#define AFTER_MODULE_INIT( name ) \
namespace { \
class _AfterModuleInit : public ::Module { \
public: \
typedef ::Module Parent; \
static _AfterModuleInit smInstance; \
virtual const char* getName() const { return "AFTER_MODULE_INIT( " #name " ) in " __FILE__; } \
struct _DepInitAfter : public Parent::Dependency \
{ \
_DepInitAfter() \
: Parent::Dependency( ModeInitialize, DependencyAfter, &smInstance, #name ) {} \
} mDepInitAfter; \
virtual void initialize(); \
}; \
_AfterModuleInit _AfterModuleInit::smInstance; \
} \
void _AfterModuleInit::initialize()
struct ModuleManager
{
/// Initialize all modules registered with the system.
static void initializeSystem();
/// Shutdown all modules registered with the system.
static void shutdownSystem();
/// Return the instance of the module called "name" or NULL if no such module is defined.
static Module* findModule( const char* name );
private:
static Module* _findOverrideFor( Module* module );
static String _moduleListToString( Vector< Module* >& moduleList );
static void _printModuleList( Vector< Module* >& moduleList );
static void _insertIntoModuleList( Module::Mode mode, Vector< Module* >& moduleList, Module* module );
static S32 _getIndexOfModuleInList( Vector< Module* >& moduleList, Module* module );
static S32 _getIndexOfModuleInList( Vector< Module* >& moduleList, const char* moduleName );
static void _createModuleList( Module::Mode mode, Vector< Module* >& moduleList );
};
#endif // !_MODULE_H_

View file

@ -0,0 +1,261 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/ogg/oggInputStream.h"
#include "core/stream/stream.h"
#include "core/util/safeDelete.h"
//#define DEBUG_SPEW
//-----------------------------------------------------------------------------
// OggDecoder implementation.
//-----------------------------------------------------------------------------
OggDecoder::OggDecoder( const ThreadSafeRef< OggInputStream >& stream )
: mOggStream( stream )
{
}
OggDecoder::~OggDecoder()
{
ogg_stream_clear( &mOggStreamState );
}
void OggDecoder::_setStartPage( ogg_page* startPage )
{
ogg_stream_init( &mOggStreamState, ogg_page_serialno( startPage ) );
ogg_stream_pagein( &mOggStreamState, startPage );
}
bool OggDecoder::_readNextPacket( ogg_packet* packet )
{
MutexHandle mutex;
mutex.lock( &mMutex, true );
while( 1 )
{
int result = ogg_stream_packetout( &mOggStreamState, packet );
if( result == 0 )
{
if( !mOggStream->_requestData() )
return false;
}
else if( result < 0 )
return false;
else
{
#ifdef DEBUG_SPEW
Platform::outputDebugString( "[OggDecoder] read packet %i in %s (bytes: %i, bos: %s, eos: %s)",
( U32 ) packet->packetno,
getName(),
( U32 ) packet->bytes,
packet->b_o_s ? "1" : "0",
packet->e_o_s ? "1" : "0" );
#endif
return true;
}
}
}
bool OggDecoder::_nextPacket()
{
MutexHandle mutex;
mutex.lock( &mMutex, true );
ogg_packet packet;
do
{
if( !_readNextPacket( &packet ) )
return false;
}
while( !_packetin( &packet ) );
return true;
}
//-----------------------------------------------------------------------------
// OggInputStream implementation.
//-----------------------------------------------------------------------------
OggInputStream::OggInputStream( Stream* stream )
: mStream( stream ),
mIsAtEnd( false )
{
ogg_sync_init( &mOggSyncState );
VECTOR_SET_ASSOCIATION( mConstructors );
VECTOR_SET_ASSOCIATION( mDecoders );
}
OggInputStream::~OggInputStream()
{
_freeDecoders();
ogg_sync_clear( &mOggSyncState );
if( mStream )
SAFE_DELETE( mStream );
}
OggDecoder* OggInputStream::getDecoder( const String& name ) const
{
for( U32 i = 0; i < mDecoders.size(); ++ i )
if( name.equal( mDecoders[ i ]->getName(), String::NoCase ) )
return mDecoders[ i ];
return NULL;
}
bool OggInputStream::isAtEnd()
{
MutexHandle mutex;
mutex.lock( &mMutex, true );
return mIsAtEnd;
}
bool OggInputStream::init()
{
if( !mStream->hasCapability( Stream::StreamPosition ) )
return false;
mStream->setPosition( 0 );
// Read all beginning-of-stream pages and construct decoders
// for all streams we recognize.
while( 1 )
{
// Read next page.
ogg_page startPage;
_pullNextPage( &startPage );
// If not a beginning-of-stream page, push it to the decoders
// and stop reading headers.
if( !ogg_page_bos( &startPage ) )
{
_pushNextPage( &startPage );
break;
}
// Try the list of constructors for one that consumes
// the page.
for( U32 i = 0; i < mConstructors.size(); ++ i )
{
OggDecoder* decoder = mConstructors[ i ]( this );
if( decoder->_detect( &startPage ) )
mDecoders.push_back( decoder );
else
delete decoder;
}
}
// Initialize decoders and let all them finish up header processing.
for( U32 i = 0; i < mDecoders.size(); ++ i )
if( !mDecoders[ i ]->_init() )
{
delete mDecoders[ i ];
mDecoders.erase( i );
-- i;
}
if( !mDecoders.size() )
return false;
return true;
}
void OggInputStream::_freeDecoders()
{
for( U32 i = 0; i < mDecoders.size(); ++ i )
delete mDecoders[ i ];
mDecoders.clear();
}
bool OggInputStream::_pullNextPage( ogg_page* page)
{
// Read another page.
while( ogg_sync_pageout( &mOggSyncState, page ) != 1 )
{
enum { BUFFER_SIZE = 4096 };
// Read more data.
char* buffer = ogg_sync_buffer( &mOggSyncState, BUFFER_SIZE );
const U32 oldPos = mStream->getPosition();
mStream->read( BUFFER_SIZE, buffer );
const U32 numBytes = mStream->getPosition() - oldPos;
if( numBytes )
ogg_sync_wrote( &mOggSyncState, numBytes );
else
return false;
}
#ifdef DEBUG_SPEW
Platform::outputDebugString( "[OggInputStream] pulled next page (header: %i, body: %i)",
page->header_len, page->body_len );
#endif
return true;
}
void OggInputStream::_pushNextPage( ogg_page* page )
{
for( U32 i = 0; i < mDecoders.size(); ++ i )
{
MutexHandle mutex;
mutex.lock( &mDecoders[ i ]->mMutex, true );
ogg_stream_pagein( &mDecoders[ i ]->mOggStreamState, page );
}
}
bool OggInputStream::_requestData()
{
// Lock at this level to ensure correct ordering of page writes.
// Technically, the proper place to lock would be _pullNextPage
// but then it could happen that one thread pushes a page before
// another thread gets to push a page that has been read earlier.
MutexHandle mutex;
mutex.lock( &mMutex, true );
ogg_page nextPage;
if( !_pullNextPage( &nextPage ) )
{
mIsAtEnd = true;
return false;
}
_pushNextPage( &nextPage );
return true;
}

View file

@ -0,0 +1,173 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _OGGINPUTSTREAM_H_
#define _OGGINPUTSTREAM_H_
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
#ifndef _TYPETRAITS_H_
#include "platform/typetraits.h"
#endif
#ifndef _PLATFORM_THREADS_MUTEX_H_
#include "platform/threads/mutex.h"
#endif
#ifndef _THREADSAFEREFCOUNT_H_
#include "platform/threads/threadSafeRefCount.h"
#endif
#include "ogg/ogg.h"
class Stream;
class OggInputStream;
/// Single substream in a multiplexed OGG stream.
class OggDecoder
{
public:
typedef void Parent;
friend class OggInputStream;
protected:
/// The Ogg container stream.
OggInputStream* mOggStream;
/// The Ogg bitstream.
ogg_stream_state mOggStreamState;
/// Lock for synchronizing access to Ogg stream state.
Mutex mMutex;
/// Read the next packet in the stream.
/// @return false if there is no next packet.
bool _readNextPacket( ogg_packet* packet );
///
bool _nextPacket();
///
virtual bool _detect( ogg_page* startPage ) = 0;
///
virtual bool _init() = 0;
///
virtual bool _packetin( ogg_packet* packet ) = 0;
///
void _setStartPage( ogg_page* startPage );
public:
///
OggDecoder( const ThreadSafeRef< OggInputStream >& stream );
virtual ~OggDecoder();
/// Return the serial number of the Ogg bitstream.
U32 getStreamSerialNo() const { return mOggStreamState.serialno; }
///
virtual const char* getName() const = 0;
};
/// A multiplexed OGG input stream feeding into stream decoders.
class OggInputStream : public ThreadSafeRefCount< OggInputStream >
{
public:
typedef void Parent;
friend class OggDecoder; // _requestData
protected:
typedef OggDecoder* ( *Constructor )( const ThreadSafeRef< OggInputStream >& stream );
template< typename T >
struct _SpellItOutForGCC
{
static OggDecoder* _fn( const ThreadSafeRef< OggInputStream >& stream )
{
return constructSingle< T* >( stream );
}
};
///
bool mIsAtEnd;
///
Stream* mStream;
///
Vector< Constructor > mConstructors;
///
Vector< OggDecoder* > mDecoders;
///
ogg_sync_state mOggSyncState;
///
Mutex mMutex;
/// Pull the next page from the OGG stream.
bool _pullNextPage( ogg_page* page );
/// Push the given page to the attached decoder streams.
void _pushNextPage( ogg_page* page );
///
bool _requestData();
///
void _freeDecoders();
public:
///
/// @note Ownership of "stream" is transferred to OggInputStream.
OggInputStream( Stream* stream );
~OggInputStream();
/// Register a decoder class with the stream.
template< class T >
void addDecoder()
{
mConstructors.push_back( &_SpellItOutForGCC< T >::_fn );
}
///
OggDecoder* getDecoder( const String& name ) const;
///
bool init();
///
bool isAtEnd();
};
#endif // !_OGGINPUTSTREAM_H_

View file

@ -0,0 +1,694 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/ogg/oggTheoraDecoder.h"
#include "gfx/gfxFormatUtils.h"
#include "math/mMathFn.h"
#include "console/console.h"
//#define DEBUG_SPEW
//-----------------------------------------------------------------------------
// Lookup tables for the transcoders.
//
// The Y, Cb, and Cr tables are used by both the SSE2 and the generic transcoder.
// For the SSE2 code, the data must be 16 byte aligned.
//
// The clamping table is only used by the generic transcoder. The SSE2 transcoder
// uses instructions to implicitly clamp out-of-range values.
dALIGN( static S32 sRGBY[ 256 ][ 4 ] );
dALIGN( static S32 sRGBCb[ 256 ][ 4 ] );
dALIGN( static S32 sRGBCr[ 256 ][ 4 ] );
static U8 sClampBuff[ 1024 ];
static U8* sClamp = sClampBuff + 384;
static void initLookupTables()
{
static bool sGenerated = false;
if( !sGenerated )
{
for( S32 i = 0; i < 256; ++ i )
{
// Y.
sRGBY[ i ][ 0 ] = ( 298 * ( i - 16 ) ) >> 8; // B
sRGBY[ i ][ 1 ] = ( 298 * ( i - 16 ) ) >> 8; // G
sRGBY[ i ][ 2 ] = ( 298 * ( i - 16 ) ) >> 8; // R
sRGBY[ i ][ 3 ] = 0xff; // A
// Cb.
sRGBCb[ i ][ 0 ] = ( 516 * ( i - 128 ) + 128 ) >> 8; // B
sRGBCb[ i ][ 1 ] = - ( ( 100 * ( i - 128 ) + 128 ) >> 8 ); // G
// Cr.
sRGBCr[ i ][ 1 ] = - ( ( 208 * ( i - 128 ) + 128 ) >> 8 ); // B
sRGBCr[ i ][ 2 ] = ( 409 * ( i - 128 ) + 128 ) >> 8; // R
}
// Setup clamping table for generic transcoder.
for( S32 i = -384; i < 640; ++ i )
sClamp[ i ] = mClamp( i, 0, 0xFF );
sGenerated = true;
}
}
static inline S32 sampleG( U8* pCb, U8* pCr )
{
return sRGBCr[ *pCr ][ 1 ] + sRGBCr[ *pCb ][ 1 ];
}
//=============================================================================
// OggTheoraDecoder.
//=============================================================================
//-----------------------------------------------------------------------------
OggTheoraDecoder::OggTheoraDecoder( const ThreadSafeRef< OggInputStream >& stream )
: Parent( stream ),
#ifdef TORQUE_DEBUG
mLock( 0 ),
#endif
mTheoraSetup( NULL ),
mTheoraDecoder( NULL ),
mTranscoder( TRANSCODER_Auto )
{
// Initialize.
th_info_init( &mTheoraInfo );
th_comment_init( &mTheoraComment );
initLookupTables();
}
//-----------------------------------------------------------------------------
OggTheoraDecoder::~OggTheoraDecoder()
{
// Free packets on the freelist.
OggTheoraFrame* packet;
while( mFreePackets.tryPopFront( packet ) )
destructSingle( packet );
// Clean up libtheora structures.
if( mTheoraDecoder )
th_decode_free( mTheoraDecoder );
if( mTheoraSetup )
th_setup_free( mTheoraSetup );
th_comment_clear( &mTheoraComment );
th_info_clear( &mTheoraInfo );
}
//-----------------------------------------------------------------------------
bool OggTheoraDecoder::_detect( ogg_page* startPage )
{
_setStartPage( startPage );
// Read first header packet.
ogg_packet nextPacket;
if( !_readNextPacket( &nextPacket )
|| th_decode_headerin( &mTheoraInfo, &mTheoraComment, &mTheoraSetup, &nextPacket ) < 0 )
{
th_comment_clear( &mTheoraComment );
th_info_clear( &mTheoraInfo );
return false;
}
return true;
}
//-----------------------------------------------------------------------------
bool OggTheoraDecoder::_init()
{
ogg_packet nextPacket;
// Read header packets.
bool haveTheoraHeader = true;
while( 1 )
{
if( !_readNextPacket( &nextPacket ) )
{
haveTheoraHeader = false;
break;
}
int result = th_decode_headerin( &mTheoraInfo, &mTheoraComment, &mTheoraSetup, &nextPacket );
if( result < 0 )
{
haveTheoraHeader = false;
break;
}
else if( result == 0 )
break;
}
// Fail if we have no valid and complete Theora header.
if( !haveTheoraHeader )
{
th_comment_clear( &mTheoraComment );
th_info_clear( &mTheoraInfo );
Con::errorf( "OggTheoraDecoder::_init() - incorrect or corrupt Theora headers" );
return false;
}
// Init the decoder.
mTheoraDecoder = th_decode_alloc( &mTheoraInfo, mTheoraSetup );
// Feed the first video packet to the decoder.
ogg_int64_t granulePos;
th_decode_packetin( mTheoraDecoder, &nextPacket, &granulePos );
mCurrentFrameTime = th_granule_time( mTheoraDecoder, granulePos );
mCurrentFrameNumber = 0;
mFrameDuration = 1.f / getFramesPerSecond();
// Make sure we have a valid pitch.
if( !mPacketFormat.mPitch )
mPacketFormat.mPitch = getFrameWidth() * GFXFormatInfo( mPacketFormat.mFormat ).getBytesPerPixel();
return true;
}
//-----------------------------------------------------------------------------
bool OggTheoraDecoder::_packetin( ogg_packet* packet )
{
ogg_int64_t granulePos;
if( th_decode_packetin( mTheoraDecoder, packet, &granulePos ) != 0 )
return false;
// See if we should drop this frame.
//RDTODO: if we have fallen too far behind, start skipping pages
F32 granuleTime = th_granule_time( mTheoraDecoder, granulePos );
mCurrentFrameTime = granuleTime;
mCurrentFrameNumber ++;
bool dropThisFrame = false;
TimeSourceRef timeSource = mTimeSource;
if( timeSource )
{
F32 currentTick = F32( timeSource->getPosition() ) / 1000.f;
if( currentTick >= ( mCurrentFrameTime + mFrameDuration ) )
dropThisFrame = true;
}
#ifdef DEBUG_SPEW
Platform::outputDebugString( "[OggTheoraDecoder] new frame %i at %f sec%s",
U32( th_granule_frame( mTheoraDecoder, granulePos ) ),
granuleTime,
dropThisFrame ? " !! DROPPED !!" : "" );
#endif
return !dropThisFrame;
}
//-----------------------------------------------------------------------------
U32 OggTheoraDecoder::read( OggTheoraFrame** buffer, U32 num )
{
#ifdef TORQUE_DEBUG
AssertFatal( dCompareAndSwap( mLock, 0, 1 ), "OggTheoraDecoder::read() - simultaneous reads not thread-safe" );
#endif
U32 numRead = 0;
for( U32 i = 0; i < num; ++ i )
{
// Read and decode a packet.
if( !_nextPacket() )
return numRead; // End of stream.
// Decode the frame to Y'CbCr.
th_ycbcr_buffer ycbcr;
th_decode_ycbcr_out( mTheoraDecoder, ycbcr );
// Allocate a packet.
const U32 width = getFrameWidth();
const U32 height = getFrameHeight();
OggTheoraFrame* packet;
if( !mFreePackets.tryPopFront( packet ) )
packet = constructSingle< OggTheoraFrame* >( mPacketFormat.mPitch * height );
packet->mFrameNumber = mCurrentFrameNumber;
packet->mFrameTime = mCurrentFrameTime;
packet->mFrameDuration = mFrameDuration;
// Transcode the packet.
#if ( defined( TORQUE_COMPILER_GCC ) || defined( TORQUE_COMPILER_VISUALC ) ) && defined( TORQUE_CPU_X86 )
if( ( mTranscoder == TRANSCODER_Auto || mTranscoder == TRANSCODER_SSE2420RGBA ) &&
getDecoderPixelFormat() == PIXEL_FORMAT_420 &&
Platform::SystemInfo.processor.properties & CPU_PROP_SSE2 &&
mPacketFormat.mFormat == GFXFormatR8G8B8A8 &&
mTheoraInfo.pic_x == 0 &&
mTheoraInfo.pic_y == 0 )
{
_transcode420toRGBA_SSE2( ycbcr, ( U8* ) packet->data, width, height, mPacketFormat.mPitch );
}
else
#endif
{
// Use generic transcoder.
_transcode( ycbcr, ( U8* ) packet->data, width, height );
}
buffer[ i ] = packet;
++ numRead;
}
#ifdef TORQUE_DEBUG
AssertFatal( dCompareAndSwap( mLock, 1, 0 ), "" );
#endif
return numRead;
}
//-----------------------------------------------------------------------------
void OggTheoraDecoder::_transcode( th_ycbcr_buffer ycbcr, U8* buffer, const U32 width, const U32 height )
{
#define ycbcrToRGB( rgb, pY, pCb, pCr, G ) \
{ \
GFXPackPixel( \
mPacketFormat.mFormat, \
rgb, \
sClamp[ sRGBY[ *pY ][ 2 ] + sRGBCr[ *pCr ][ 2 ] ], \
sClamp[ sRGBY[ *pY ][ 1 ] + G ], \
sClamp[ sRGBY[ *pY ][ 0 ] + sRGBCb[ *pCb ][ 0 ] ], \
255 \
); \
}
// Determine number of chroma samples per 4-pixel luma block.
U32 numChromaSamples = 4;
EPixelFormat pixelFormat = getDecoderPixelFormat();
if( pixelFormat == PIXEL_FORMAT_422 )
numChromaSamples = 2;
else if( pixelFormat == OggTheoraDecoder::PIXEL_FORMAT_420 )
numChromaSamples = 1;
// Convert and copy the pixels. Deal with all three
// possible plane configurations.
const U32 pictOffsetY = _getPictureOffset( ycbcr, 0 );
const U32 pictOffsetU = _getPictureOffset( ycbcr, 1 );
const U32 pictOffsetV = _getPictureOffset( ycbcr, 2 );
for( U32 y = 0; y < height; y += 2 )
{
U8* dst0 = buffer + y * mPacketFormat.mPitch;
U8* dst1 = dst0 + mPacketFormat.mPitch;
U8* pY0 = _getPixelPtr( ycbcr, 0, pictOffsetY, 0, y );
U8* pY1 = _getPixelPtr( ycbcr, 0, pictOffsetY, 0, y + 1 );
U8* pU0 = _getPixelPtr( ycbcr, 1, pictOffsetU, 0, y );
U8* pU1 = _getPixelPtr( ycbcr, 1, pictOffsetU, 0, y + 1 );
U8* pV0 = _getPixelPtr( ycbcr, 2, pictOffsetV, 0, y );
U8* pV1 = _getPixelPtr( ycbcr, 2, pictOffsetV, 0, y + 1 );
for( U32 x = 0; x < width; x += 2 )
{
// Pixel 0x0.
S32 G = sampleG( pU0, pV0 );
ycbcrToRGB( dst0, pY0, pU0, pV0, G );
++ pY0;
if( numChromaSamples == 4 )
{
++ pU0;
++ pV0;
}
// Pixel 0x1.
if( numChromaSamples == 4 )
G = sampleG( pU0, pV0 );
ycbcrToRGB( dst0, pY0, pU0, pV0, G );
++ pY0;
++ pU0;
++ pV0;
// Pixel 1x0.
if( numChromaSamples != 1 )
G = sampleG( pU1, pV1 );
ycbcrToRGB( dst1, pY1, pU1, pV1, G );
++ pY1;
if( numChromaSamples == 4 )
{
++ pU1;
++ pV1;
}
// Pixel 1x1.
if( numChromaSamples == 4 )
G = sampleG( pU1, pV1 );
ycbcrToRGB( dst1, pY1, pU1, pV1, G );
++ pY1;
++ pU1;
++ pV1;
}
}
#undef ycbcrToRGB
}
//-----------------------------------------------------------------------------
void OggTheoraDecoder::_transcode420toRGBA_SSE2( th_ycbcr_buffer ycbcr, U8* buffer, U32 width, U32 height, U32 pitch )
{
AssertFatal( width % 2 == 0, "OggTheoraDecoder::_transcode420toRGBA_SSE2() - width must be multiple of 2" );
AssertFatal( height % 2 == 0, "OggTheoraDecoder::_transcode420toRGBA_SSE2() - height must be multiple of 2" );
unsigned char* ydata = ycbcr[ 0 ].data;
unsigned char* udata = ycbcr[ 1 ].data;
unsigned char* vdata = ycbcr[ 2 ].data;
S32* ycoeff = ( S32* ) sRGBY;
S32* ucoeff = ( S32* ) sRGBCb;
S32* vcoeff = ( S32* ) sRGBCr;
// At the end of a line loop, we need to jump over the padding resulting from the difference
// between pitch and width plus jump a whole scanline as we always operate two scanlines
// at a time.
const U32 stride = pitch - width * 4 + pitch;
// Same thing for the Y channel.
const U32 ystrideDelta = ycbcr[ 0 ].stride - width + ycbcr[ 0 ].stride;
const U32 ypitch = ycbcr[ 0 ].stride;
// U and V only jump a single scanline so we only need to advance by the padding on the
// right. Both planes are half-size.
const U32 ustrideDelta = ycbcr[ 1 ].stride - width / 2;
const U32 vstrideDelta = ycbcr[ 2 ].stride - width / 2;
#if defined( TORQUE_COMPILER_VISUALC ) && defined( TORQUE_CPU_X86 )
__asm
{
mov ecx,height
hloop:
push ecx
mov ecx,width
wloop:
push ecx
xor eax,eax
// Load and accumulate coefficients for U and V in XMM0.
mov esi,udata
mov ebx,ucoeff
mov edx,ydata
mov al,[esi]
xor ecx,ecx
mov edi,vdata
shl eax,4
movdqa xmm0,[ebx+eax]
mov ebx,vcoeff
mov cl,[edi]
mov esi,ycoeff
shl ecx,4
paddd xmm0,[ebx+ecx]
xor eax,eax
xor ebx,ebx
// Load coefficients for Y of the four pixels into XMM1-XMM4.
mov ecx,ypitch
mov al,[edx]
mov bl,[edx+1]
shl eax,4
shl ebx,4
movdqa xmm1,[esi+eax]
movdqa xmm2,[esi+ebx]
xor eax,eax
xor ebx,ebx
mov al,[edx+ecx]
mov bl,[edx+ecx+1]
shl eax,4
shl ebx,4
movdqa xmm3,[esi+eax]
movdqa xmm4,[esi+ebx]
mov edi,buffer
mov ecx,pitch
// Add Cb and Cr on top of Y.
paddd xmm1,xmm0
paddd xmm2,xmm0
paddd xmm3,xmm0
paddd xmm4,xmm0
// Pack pixels together. We need to pack twice per pixel
// to go from 32bits via 16bits to 8bits.
//
// Right now we're simply packing two garbage pixels for the
// second packing operation. An alternative would be to pack the
// four pixels into one XMM register and then do a packed shuffle
// to split out the lower two pixels before the move.
packssdw xmm1,xmm2
packssdw xmm3,xmm4
packuswb xmm1,xmm6
packuswb xmm3,xmm7
// Store pixels.
movq qword ptr [edi],xmm1
movq qword ptr [edi+ecx],xmm3
// Loop width.
pop ecx
add ydata,2
inc udata
inc vdata
add buffer,8
sub ecx,2
jnz wloop
// Loop height.
pop ecx
mov ebx,stride
mov eax,ystrideDelta
mov edi,ustrideDelta
mov esi,vstrideDelta
add buffer,ebx
add ydata,eax
add udata,edi
add vdata,esi
sub ecx,2
jnz hloop
};
#elif defined( TORQUE_COMPILER_GCC ) && defined( TORQUE_CPU_X86 )
asm( "pushal\n" // Save all general-purpose registers.
"movl %0,%%ecx\n" // Load height into ECX.
".hloop_sse:\n"
"pushl %%ecx\n" // Save counter.
"movl %1,%%ecx\n" // Load width into ECX.
".wloop_sse:\n"
"pushl %%ecx\n" // Save counter.
"xorl %%eax,%%eax\n" // Zero out eax for later use.
// Load and accumulate coefficients for U and V in XMM0.
"movl %3,%%esi\n" // Load U pointer into ESI.
"movl %8,%%ebx\n" // Load U coefficient table into EBX.
"movl %2,%%edx\n" // Load Y pointer into EDX.
"movb (%%esi),%%al\n" // Load U into AL.
"xorl %%ecx,%%ecx\n" // Clear ECX.
"movl %4,%%edi\n" // Load V pointer into EDI.
"shll $4,%%eax\n" // Multiply EAX by 16 to index into table.
"movdqa (%%ebx,%%eax),%%xmm0\n" // Load Cb coefficient into XMM0.
"movl %9,%%ebx\n" // Load V coefficients table into EBX.
"movb (%%edi),%%cl\n" // Load V into CL.
"movl %7,%%esi\n" // Load Y coefficients table into ESI.
"shll $4,%%ecx\n" // Multiply ECX by 16 to index into table.
"paddd (%%ebx,%%ecx),%%xmm0\n" // Add Cr coefficient to Cb coefficient.
"xorl %%eax,%%eax\n" // Clear EAX.
"xorl %%ebx,%%ebx\n" // Clear EBX.
// Load coefficients for Y of the four pixels into XMM1-XMM4.
"movl %14,%%ecx\n" // Load Y pitch into ECX (needed later for lower two pixels).
"movb (%%edx),%%al\n" // Load upper-left pixel Y into AL.
"movb 1(%%edx),%%bl\n" // Load upper-right pixel Y into BL.
"shll $4,%%eax\n" // Multiply EAX by 16 to index into table.
"shll $4,%%ebx\n" // Multiply EBX by 16 to index into table.
"movdqa (%%esi,%%eax),%%xmm1\n" // Load coefficient for upper-left pixel Y into XMM1.
"movdqa (%%esi,%%ebx),%%xmm2\n" // Load coefficient for upper-right pixel Y into XMM2.
"xorl %%eax,%%eax\n" // Clear EAX.
"xorl %%ebx,%%ebx\n" // Clear EBX.
"movb (%%edx,%%ecx),%%al\n" // Load lower-left pixel Y into AL.
"movb 1(%%edx,%%ecx),%%bl\n" // Load lower-right pixel Y into AL.
"shll $4,%%eax\n" // Multiply EAX by 16 to index into table.
"shll $4,%%ebx\n" // Multiply EBX by 16 to index into table.
"movdqa (%%esi,%%eax),%%xmm3\n" // Load coefficient for lower-left pixel Y into XMM3.
"movdqa (%%esi,%%ebx),%%xmm4\n" // Load coefficient for lower-right pixel Y into XMM4.
"movl %5,%%edi\n" // Load buffer pointer into EDI (for later use).
"movl %6,%%ecx\n" // Load pitch into ECX (for later use).
// Add Cb and Cr on top of Y.
"paddd %%xmm0,%%xmm1\n" // Add chroma channels to upper-left pixel.
"paddd %%xmm0,%%xmm2\n" // Add chroma channels to upper-right pixel.
"paddd %%xmm0,%%xmm3\n" // Add chroma channels to lower-left pixel.
"paddd %%xmm0,%%xmm4\n" // Add chroma channels to lower-right pixel.
// Pack pixels together. We need to pack twice per pixel
// to go from 32bits via 16bits to 8bits.
//
// Right now we're simply packing two garbage pixels for the
// second packing operation. An alternative would be to pack the
// four pixels into one XMM register and then do a packed shuffle
// to split out the lower two pixels before the move.
"packssdw %%xmm2,%%xmm1\n" // Pack 32bit channels together into 16bit channels on upper two pixels.
"packssdw %%xmm4,%%xmm3\n" // Pack 32bit channels together into 16bit channels on lower two pixels.
"packuswb %%xmm6,%%xmm1\n" // Pack 16bit channels together into 8bit channels on upper two pixels (plus two garbage pixels).
"packuswb %%xmm7,%%xmm3\n" // Pack 16bit channels together into 8bit channels on lower two pixels (plus two garbage pixels).
// Store pixels.
"movq %%xmm1,(%%edi)\n" // Store upper two pixels.
"movq %%xmm3,(%%edi,%%ecx)\n" // Store lower two pixels.
// Loop width.
"popl %%ecx\n" // Restore width counter.
"addl $2,%2\n" // Bump Y pointer by two pixels (1 bpp).
"incl %3\n" // Bump U pointer by one pixel (1 bpp).
"incl %4\n" // Bump V pointer by one pixel (1 bpp).
"addl $8,%5\n" // Bump buffer pointer by two pixels (4 bpp).
"subl $2,%%ecx\n"
"jnz .wloop_sse\n"
// Loop height.
"popl %%ecx\n" // Restore height counter.
"movl %10,%%ebx\n" // Load buffer stride into EBX.
"movl %11,%%eax\n" // Load Y stride delta into EAX.
"movl %12,%%edi\n" // Load U stride delta into EDI.
"movl %13,%%esi\n" // Load V stride delta into ESI.
"addl %%ebx,%5\n" // Bump buffer pointer by stride delta.
"addl %%eax,%2\n" // Bump Y pointer by stride delta.
"addl %%edi,%3\n" // Bump U pointer by stride delta.
"addl %%esi,%4\n" // Bump V pointer by stride delta.
"subl $2,%%ecx\n"
"jnz .hloop_sse\n"
"popal\n"
:
: "m" ( height ), // 0
"m" ( width ), // 1
"m" ( ydata ), // 2
"m" ( udata ), // 3
"m" ( vdata ), // 4
"m" ( buffer ), // 5
"m" ( pitch ), // 6
"m" ( ycoeff ), // 7
"m" ( ucoeff ), // 8
"m" ( vcoeff ), // 9
"m" ( stride ), // 10
"m" ( ystrideDelta ), // 11
"m" ( ustrideDelta ), // 12
"m" ( vstrideDelta ), // 13
"m" ( ypitch ) // 14
);
#endif
}

View file

@ -0,0 +1,267 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _OGGTHEORADECODER_H_
#define _OGGTHEORADECODER_H_
#ifndef _OGGINPUTSTREAM_H_
#include "core/ogg/oggInputStream.h"
#endif
#ifndef _TSTREAM_H_
#include "core/stream/tStream.h"
#endif
#ifndef _RAWDATA_H_
#include "core/util/rawData.h"
#endif
#ifndef _GFXENUMS_H_
#include "gfx/gfxEnums.h"
#endif
#ifndef _THREADSAFEDEQUE_H_
#include "platform/threads/threadSafeDeque.h"
#endif
#include "theora/theoradec.h"
/// A single decoded Theora video frame.
class OggTheoraFrame : public RawData
{
public:
typedef RawData Parent;
OggTheoraFrame() {}
OggTheoraFrame( S8* data, U32 size, bool ownMemory = false )
: Parent( data, size, ownMemory ) {}
/// Serial number of this frame in the stream.
U32 mFrameNumber;
/// Playtime in seconds at which to display this frame.
F32 mFrameTime;
/// Seconds to display this frame.
F32 mFrameDuration;
};
/// Decodes a Theora substream into frame packets.
///
/// Frame packets contain raw pixel data in the set pixel format (default is R8G8B8).
/// Reading on a thread is safe, but remember to keep a reference to the OggInputStream
/// master from the worker thread.
class OggTheoraDecoder : public OggDecoder,
public IInputStream< OggTheoraFrame* >
{
public:
typedef OggDecoder Parent;
/// Y'CbCr pixel format of the source video stream.
/// For informational purposes only. Packet out is determined
/// by PacketFormat.
enum EPixelFormat
{
PIXEL_FORMAT_444, // Full Y, full Cb, full Cr.
PIXEL_FORMAT_422, // Full Y, half-width Cb, half-width Cr.
PIXEL_FORMAT_420, // Full Y, half-widht+height Cb, half-width+height Cr.
PIXEL_FORMAT_Unknown
};
/// Descriptor for surface format that this stream should
/// decode into. This saves an otherwise potentitally necessary
/// swizzling step.
///
/// @note The output channel ordering will be in device format, i.e.
/// least-significant first.
struct PacketFormat
{
/// Pixel format.
GFXFormat mFormat;
/// Bytes per scanline.
U32 mPitch;
/// Default descriptor sets up for RGB.
PacketFormat()
: mFormat( GFXFormatR8G8B8 ),
mPitch( 0 ) {}
///
PacketFormat( GFXFormat format, U32 pitch )
: mFormat( format ),
mPitch( pitch ) {}
};
///
enum ETranscoder
{
TRANSCODER_Auto, ///< Auto-detect from current formats and processor capabilities.
TRANSCODER_Generic, ///< Generic transcoder that handles all source and target formats; 32bit integer + lookup tables.
TRANSCODER_SSE2420RGBA, ///< SSE2 transcoder with fixed 4:2:0 to RGBA conversion; 32bit integer + lookup tables.
};
protected:
typedef IPositionable< U32 >* TimeSourceRef;
/// @name libtheora Data
/// @{
///
th_comment mTheoraComment;
///
th_info mTheoraInfo;
///
th_setup_info* mTheoraSetup;
///
th_dec_ctx* mTheoraDecoder;
/// @}
///
PacketFormat mPacketFormat;
///
F32 mFrameDuration;
///
F32 mCurrentFrameTime;
///
U32 mCurrentFrameNumber;
/// If this is set, the decoder will drop frames that are
/// already outdated with respect to the time source.
///
/// @note Times are in milliseconds and in video time.
TimeSourceRef mTimeSource;
/// Transcoder to use for color space conversion. If the current
/// setting is invalid, will fall back to generic.
ETranscoder mTranscoder;
///
ThreadSafeDeque< OggTheoraFrame* > mFreePackets;
#ifdef TORQUE_DEBUG
U32 mLock;
#endif
/// Generic transcoder going from any of the Y'CbCr pixel formats to
/// any RGB format (that is supported by GFXFormatUtils).
void _transcode( th_ycbcr_buffer ycbcr, U8* buffer, U32 width, U32 height );
/// Transcoder with fixed 4:2:0 to RGBA conversion using SSE2 assembly.
void _transcode420toRGBA_SSE2( th_ycbcr_buffer ycbcr, U8* buffer, U32 width, U32 height, U32 pitch );
// OggDecoder.
virtual bool _detect( ogg_page* startPage );
virtual bool _init();
virtual bool _packetin( ogg_packet* packet );
///
U32 _getPixelOffset( th_ycbcr_buffer buffer, U32 plane, U32 x, U32 y ) const
{
switch( getDecoderPixelFormat() )
{
case PIXEL_FORMAT_444: break;
case PIXEL_FORMAT_422: if( plane != 0 ) x >>= 1; break;
case PIXEL_FORMAT_420: if( plane != 0 ) { x >>= 1; y >>= 1; } break;
default:
AssertFatal( false, "OggTheoraDecoder::_getPixelOffset() - invalid pixel format" );
}
return ( y * buffer[ plane ].stride + x );
}
///
U8* _getPixelPtr( th_ycbcr_buffer buffer, U32 plane, U32 offset, U32 x, U32 y ) const
{
return ( buffer[ plane ].data + offset + _getPixelOffset( buffer, plane, x, y ) );
}
///
U32 _getPictureOffset( th_ycbcr_buffer buffer, U32 plane )
{
return _getPixelOffset( buffer, plane, mTheoraInfo.pic_x, mTheoraInfo.pic_y );
}
public:
///
OggTheoraDecoder( const ThreadSafeRef< OggInputStream >& stream );
~OggTheoraDecoder();
/// Return the width of video image frames in pixels.
/// @note This returns the actual picture width rather than Theora's internal encoded frame width.
U32 getFrameWidth() const { return mTheoraInfo.pic_width; }
/// Return the height of video image frames in pixels.
/// @note This returns the actual picture height rather than Theora's internal encoded frame height.
U32 getFrameHeight() const { return mTheoraInfo.pic_height; }
///
F32 getFramesPerSecond() const { return ( F32( mTheoraInfo.fps_numerator ) / F32( mTheoraInfo.fps_denominator ) ); }
///
EPixelFormat getDecoderPixelFormat() const
{
switch( mTheoraInfo.pixel_fmt )
{
case TH_PF_444: return PIXEL_FORMAT_444;
case TH_PF_422: return PIXEL_FORMAT_422;
case TH_PF_420: return PIXEL_FORMAT_420;
default: return PIXEL_FORMAT_Unknown;
}
}
///
const PacketFormat& getPacketFormat() const { return mPacketFormat; }
///
void setPacketFormat( const PacketFormat& format ) { mPacketFormat = format; }
/// Set the reference time source. Frames will be dropped if the decoder
/// falls behind the time of this source.
///
/// @note The time source must have at least the same lifetime as the decoder.
void setTimeSource( const TimeSourceRef& timeSource ) { mTimeSource = timeSource; }
/// Set the Y'CbCr->RGB transcoder to use.
void setTranscoder( ETranscoder transcoder ) { mTranscoder = transcoder; }
///
void reusePacket( OggTheoraFrame* packet ) { mFreePackets.pushBack( packet ); }
// OggDecoder.
virtual const char* getName() const { return "Theora"; }
// IInputStream.
virtual U32 read( OggTheoraFrame** buffer, U32 num );
};
#endif // !_OGGTHEORADECODER_H_

View file

@ -0,0 +1,195 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/ogg/oggVorbisDecoder.h"
#include "console/console.h"
//#define DEBUG_SPEW
//-----------------------------------------------------------------------------
OggVorbisDecoder::OggVorbisDecoder( const ThreadSafeRef< OggInputStream >& stream )
: Parent( stream )
#ifdef TORQUE_DEBUG
, mLock( 0 )
#endif
{
// Initialize.
vorbis_info_init( &mVorbisInfo );
vorbis_comment_init( &mVorbisComment );
dMemset( &mVorbisBlock, 0, sizeof( mVorbisBlock ) );
dMemset( &mVorbisDspState, 0, sizeof( mVorbisDspState ) );
}
//-----------------------------------------------------------------------------
OggVorbisDecoder::~OggVorbisDecoder()
{
vorbis_block_clear( &mVorbisBlock );
vorbis_dsp_clear( &mVorbisDspState );
vorbis_info_clear( &mVorbisInfo );
vorbis_comment_clear( &mVorbisComment );
}
//-----------------------------------------------------------------------------
bool OggVorbisDecoder::_detect( ogg_page* startPage )
{
_setStartPage( startPage );
// Read initial header packet.
ogg_packet nextPacket;
if( !_readNextPacket( &nextPacket )
|| vorbis_synthesis_headerin( &mVorbisInfo, &mVorbisComment, &nextPacket ) < 0 )
{
vorbis_info_clear( &mVorbisInfo );
vorbis_comment_clear( &mVorbisComment );
return false;
}
return true;
}
//-----------------------------------------------------------------------------
bool OggVorbisDecoder::_init()
{
// Read header packets.
bool haveVorbisHeader = true;
for( U32 i = 0; i < 2; ++ i )
{
ogg_packet nextPacket;
if( !_readNextPacket( &nextPacket ) )
{
haveVorbisHeader = false;
break;
}
int result = vorbis_synthesis_headerin( &mVorbisInfo, &mVorbisComment, &nextPacket );
if( result != 0 )
{
haveVorbisHeader = false;
break;
}
}
// Fail if we don't have a complete and valid Vorbis header.
if( !haveVorbisHeader )
{
vorbis_info_clear( &mVorbisInfo );
vorbis_comment_clear( &mVorbisComment );
Con::errorf( "OggVorbisDecoder::_init() - Incorrect or corrupt Vorbis headers" );
return false;
}
// Init synthesis.
vorbis_synthesis_init( &mVorbisDspState, &mVorbisInfo );
vorbis_block_init( &mVorbisDspState, &mVorbisBlock );
return true;
}
//-----------------------------------------------------------------------------
bool OggVorbisDecoder::_packetin( ogg_packet* packet )
{
return ( vorbis_synthesis( &mVorbisBlock, packet ) == 0 );
}
//-----------------------------------------------------------------------------
U32 OggVorbisDecoder::read( RawData** buffer, U32 num )
{
#ifdef TORQUE_DEBUG
AssertFatal( dCompareAndSwap( mLock, 0, 1 ), "OggVorbisDecoder::read() - simultaneous reads not thread-safe" );
#endif
U32 numRead = 0;
for( U32 i = 0; i < num; ++ i )
{
float** pcmData;
U32 numSamples;
// Read sample data.
while( 1 )
{
numSamples = vorbis_synthesis_pcmout( &mVorbisDspState, &pcmData );
if( numSamples )
break;
else
{
if( !_nextPacket() )
return numRead; // End of stream.
vorbis_synthesis_blockin( &mVorbisDspState, &mVorbisBlock );
}
}
vorbis_synthesis_read( &mVorbisDspState, numSamples );
#ifdef DEBUG_SPEW
Platform::outputDebugString( "[OggVorbisDecoder] read %i samples", numSamples );
#endif
// Allocate a packet.
const U32 numChannels = getNumChannels();
RawData* packet = constructSingle< RawData* >( numSamples * 2 * numChannels ); // Two bytes per channel.
// Convert and copy the samples.
S16* samplePtr = ( S16* ) packet->data;
for( U32 n = 0; n < numSamples; ++ n )
for( U32 c = 0; c < numChannels; ++ c )
{
S32 val = S32( pcmData[ c ][ n ] * 32767.f );
if( val > 32767 )
val = 32767;
else if( val < -34768 )
val = -32768;
*samplePtr = val;
++ samplePtr;
}
// Success.
buffer[ i ] = packet;
numRead ++;
}
#ifdef TORQUE_DEBUG
AssertFatal( dCompareAndSwap( mLock, 1, 0 ), "" );
#endif
return numRead;
}

View file

@ -0,0 +1,91 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _OGGVORBISDECODER_H_
#define _OGGVORBISDECODER_H_
#ifndef _OGGINPUTSTREAM_H_
#include "core/ogg/oggInputStream.h"
#endif
#ifndef _TSTREAM_H_
#include "core/stream/tStream.h"
#endif
#ifndef _RAWDATA_H_
#include "core/util/rawData.h"
#endif
#include "vorbis/codec.h"
/// Decodes a Vorbis substream into sample packets.
///
/// Vorbis samples are always 16bits.
class OggVorbisDecoder : public OggDecoder,
public IInputStream< RawData* >
{
public:
typedef OggDecoder Parent;
protected:
///
vorbis_info mVorbisInfo;
///
vorbis_comment mVorbisComment;
///
vorbis_dsp_state mVorbisDspState;
///
vorbis_block mVorbisBlock;
#ifdef TORQUE_DEBUG
U32 mLock;
#endif
// OggDecoder.
virtual bool _detect( ogg_page* startPage );
virtual bool _init();
virtual bool _packetin( ogg_packet* packet );
public:
///
OggVorbisDecoder( const ThreadSafeRef< OggInputStream >& stream );
~OggVorbisDecoder();
///
U32 getNumChannels() const { return mVorbisInfo.channels; }
///
U32 getSamplesPerSecond() const { return mVorbisInfo.rate; }
// OggDecoder.
virtual const char* getName() const { return "Vorbis"; }
// IInputStream.
virtual U32 read( RawData** buffer, U32 num );
};
#endif // !_OGGVORBISDECODER_H_

View file

@ -0,0 +1,159 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/resizeStream.h"
ResizeFilterStream::ResizeFilterStream()
: m_pStream(NULL),
m_startOffset(0),
m_streamLen(0),
m_currOffset(0),
m_lastBytesRead(0)
{
//
}
ResizeFilterStream::~ResizeFilterStream()
{
detachStream();
}
bool ResizeFilterStream::attachStream(Stream* io_pSlaveStream)
{
AssertFatal(io_pSlaveStream != NULL, "NULL Slave stream?");
m_pStream = io_pSlaveStream;
m_startOffset = 0;
m_streamLen = m_pStream->getStreamSize();
m_currOffset = 0;
setStatus(EOS);
return true;
}
void ResizeFilterStream::detachStream()
{
m_pStream = NULL;
m_startOffset = 0;
m_streamLen = 0;
m_currOffset = 0;
setStatus(Closed);
}
Stream* ResizeFilterStream::getStream()
{
return m_pStream;
}
bool ResizeFilterStream::setStreamOffset(const U32 in_startOffset, const U32 in_streamLen)
{
AssertFatal(m_pStream != NULL, "stream not attached!");
if (m_pStream == NULL)
return false;
U32 start = in_startOffset;
U32 end = in_startOffset + in_streamLen;
U32 actual = m_pStream->getStreamSize();
if (start >= actual || end > actual)
return false;
m_startOffset = start;
m_streamLen = in_streamLen;
m_currOffset = 0;
if (m_streamLen != 0)
setStatus(Ok);
else
setStatus(EOS);
return true;
}
U32 ResizeFilterStream::getPosition() const
{
AssertFatal(m_pStream != NULL, "Error, stream not attached");
if (m_pStream == NULL)
return 0;
return m_currOffset;
}
bool ResizeFilterStream::setPosition(const U32 in_newPosition)
{
AssertFatal(m_pStream != NULL, "Error, stream not attached");
if (m_pStream == NULL)
return false;
if (in_newPosition < m_streamLen) {
m_currOffset = in_newPosition;
return true;
} else {
m_currOffset = m_streamLen;
return false;
}
}
U32 ResizeFilterStream::getStreamSize()
{
AssertFatal(m_pStream != NULL, "Error, stream not attached");
return m_streamLen;
}
bool ResizeFilterStream::_read(const U32 in_numBytes, void* out_pBuffer)
{
AssertFatal(m_pStream != NULL, "Error, stream not attached");
m_lastBytesRead = 0;
if (in_numBytes == 0)
return true;
AssertFatal(out_pBuffer != NULL, "Invalid output buffer");
if (getStatus() == Closed) {
AssertFatal(false, "Attempted read from closed stream");
return false;
}
U32 savePosition = m_pStream->getPosition();
if (m_pStream->setPosition(m_startOffset + m_currOffset) == false)
return false;
U32 actualSize = in_numBytes;
U32 position = m_startOffset + m_currOffset;
if (in_numBytes + position > m_startOffset + m_streamLen)
actualSize = m_streamLen - (position - m_startOffset);
if (actualSize == 0) {
setStatus(EOS);
return false;
}
bool success = m_pStream->read(actualSize, out_pBuffer);
m_currOffset += actualSize;
m_lastBytesRead = actualSize;
setStatus(m_pStream->getStatus());
m_pStream->setPosition(savePosition);
return success;
}

View file

@ -0,0 +1,69 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _RESIZESTREAM_H_
#define _RESIZESTREAM_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _FILTERSTREAM_H_
#include "core/filterStream.h"
#endif
class ResizeFilterStream : public FilterStream, public IStreamByteCount
{
typedef FilterStream Parent;
Stream* m_pStream;
U32 m_startOffset;
U32 m_streamLen;
U32 m_currOffset;
U32 m_lastBytesRead;
public:
ResizeFilterStream();
~ResizeFilterStream();
bool attachStream(Stream* io_pSlaveStream);
void detachStream();
Stream* getStream();
bool setStreamOffset(const U32 in_startOffset,
const U32 in_streamLen);
// Mandatory overrides.
protected:
bool _read(const U32 in_numBytes, void* out_pBuffer);
public:
U32 getPosition() const;
bool setPosition(const U32 in_newPosition);
U32 getStreamSize();
// IStreamByteCount
U32 getLastBytesRead() { return m_lastBytesRead; }
U32 getLastBytesWritten() { return 0; }
};
#endif //_RESIZESTREAM_H_

View file

@ -0,0 +1,110 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/resourceManager.h"
#include "core/volume.h"
#include "console/console.h"
FreeListChunker<ResourceHolderBase> ResourceHolderBase::smHolderFactory;
ResourceBase::Header ResourceBase::smBlank;
U32 ResourceBase::Header::getChecksum() const
{
Torque::FS::FileNodeRef fileRef = Torque::FS::GetFileNode( mPath );
if ( fileRef == NULL )
{
Con::errorf("ResourceBase::getChecksum could not access file: [%s]", mPath.getFullPath().c_str() );
return 0;
}
return fileRef->getChecksum();
}
void ResourceBase::Header::destroySelf()
{
if (this == &smBlank)
return;
if( mNotifyUnload )
mNotifyUnload( getPath(), getResource() );
if ( mResource != NULL )
{
mResource->~ResourceHolderBase();
ResourceHolderBase::smHolderFactory.free( mResource );
}
ResourceManager::get().remove( this );
delete this;
}
void ResourceBase::assign(const ResourceBase &inResource, void* resource)
{
mResourceHeader = inResource.mResourceHeader;
if ( mResourceHeader == NULL || mResourceHeader.getPointer() == &(ResourceBase::smBlank) )
return;
if (mResourceHeader->getSignature())
{
AssertFatal(inResource.mResourceHeader->getSignature() == getSignature(),"Resource::assign: mis-matching signature");
}
else
{
mResourceHeader->mSignature = getSignature();
const Torque::Path path = mResourceHeader->getPath();
if (resource == NULL)
{
if ( !getStaticLoadSignal().trigger(path, &resource) && (resource != NULL) )
{
mResourceHeader->mResource = createHolder(resource);
mResourceHeader->mNotifyUnload = _getNotifyUnloadFn();
_triggerPostLoadSignal();
return;
}
resource = create(path);
}
if (resource)
{
mResourceHeader->mResource = createHolder(resource);
mResourceHeader->mNotifyUnload = _getNotifyUnloadFn();
_triggerPostLoadSignal();
}
else
{
// Failed to create...delete signature so we can attempt to successfully create resource later
Con::warnf("Failed to create resource: [%s]", path.getFullPath().c_str() );
mResourceHeader->mSignature = 0;
}
}
}

View file

@ -0,0 +1,323 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef __RESOURCE_H__
#define __RESOURCE_H__
#ifndef _DATACHUNKER_H_
#include "core/dataChunker.h"
#endif
#ifndef _PATH_H_
#include "core/util/path.h"
#endif
#ifndef _REFBASE_H_
#include "core/util/refBase.h"
#endif
#ifndef _TIMECLASS_H_
#include "core/util/timeClass.h"
#endif
#ifndef _TSIGNAL_H_
#include "core/util/tSignal.h"
#endif
#ifndef _PLATFORMASSERT_H_
#include "platform/platformAssert.h"
#endif
class ResourceManager;
// This is a utility class used by the resource manager.
// The prime responsibility of this class is to delete
// a resource. The base type is never used, but a template
// version is always derived that knows how to delete the
// particular type of resource. Normally, one needs not
// worry about this class. However, if one wants to delete
// a particular resource in a special manner, one may
// override the ResourceHolder<T>::~ResourceHolder method.
class ResourceHolderBase
{
public:
static FreeListChunker<ResourceHolderBase> smHolderFactory;
virtual ~ResourceHolderBase() {}
// Return void pointer to resource data.
void *getResource() const { return mRes; }
protected:
// Construct a resource holder pointing at 'p'.
ResourceHolderBase(void *p) : mRes(p) {}
void *mRes;
};
// All resources are derived from this type. The base type
// is only instantiated by the resource manager
// (the resource manager will return a base resource which a
// derived resource, Resource<T>, will construct itself
// with). Base class handles locking and unlocking and
// provides several virtual functions to be defined by
// derived resource.
class ResourceBase
{
friend class ResourceManager;
protected:
class Header;
public:
typedef U32 Signature;
public:
ResourceBase(Header *header) { mResourceHeader = (header ? header : &smBlank); }
virtual ~ResourceBase() {}
const Torque::Path &getPath() const
{
AssertFatal(mResourceHeader != NULL,"ResourceBase::getPath called on invalid resource");
return mResourceHeader->getPath();
}
U32 getChecksum() const
{
AssertFatal(mResourceHeader != NULL,"ResourceBase::getChecksum called on invalid resource");
return mResourceHeader->getChecksum();
}
protected:
typedef void ( *NotifyUnloadFn )( const Torque::Path& path, void* resource );
class Header : public StrongRefBase
{
public:
Header()
: mSignature(0),
mResource(NULL),
mNotifyUnload( NULL )
{
}
const Torque::Path &getPath() const { return mPath; }
Signature getSignature() const { return mSignature; }
void *getResource() const { return (mResource ? mResource->getResource() : NULL); }
U32 getChecksum() const;
virtual void destroySelf();
private:
friend class ResourceBase;
friend class ResourceManager;
Signature mSignature;
ResourceHolderBase* mResource;
Torque::Path mPath;
NotifyUnloadFn mNotifyUnload;
};
protected:
static Header smBlank;
ResourceBase() : mResourceHeader(&smBlank) {}
StrongRefPtr<Header> mResourceHeader;
void assign(const ResourceBase &inResource, void* resource = NULL);
// The following functions are virtual, but cannot be pure-virtual
// because we need to be able to instantiate this class.
// To be defined by derived class. Creates a resource
// holder of the desired type. Resource template handles
// this, so one should never need to override.
virtual ResourceHolderBase *createHolder(void *)
{
AssertFatal(0,"ResourceBase::createHolder: should not be called");
return NULL;
}
// Create a Resource of desired type using passed path. Derived
// resource class must define this.
virtual void *create(const Torque::Path &path)
{
AssertFatal(0,"ResourceBase::create: should not be called");
return NULL;
}
// Return signature for desired type.
virtual Signature getSignature() const
{
return mResourceHeader->getSignature();
}
virtual Signal<bool(const Torque::Path &, void**)> &getStaticLoadSignal()
{
AssertFatal(0,"ResourceBase::getStaticLoadSignal: should not be called");
static Signal<bool(const Torque::Path &, void**)> sLoadSignal;
return sLoadSignal;
}
virtual void _triggerPostLoadSignal() {}
virtual NotifyUnloadFn _getNotifyUnloadFn() { return ( NotifyUnloadFn ) NULL; }
};
// This is a utility class used by resource manager. Classes derived
// from this template pretty much just know how to delete the template's
// type.
template<class T> class ResourceHolder : public ResourceHolderBase
{
public:
ResourceHolder(T *t) : ResourceHolderBase(t) {}
virtual ~ResourceHolder() { delete ((T*)mRes); }
};
// Resource template. When dealing with resources, this is the
// class that will be used. One creates resources by opening or
// creating them via the resource manager. The resource manager
// returns ResourceBases, which can be used to construct any
// type of resource (see the constructors for this template).
// When instantiating classes using this template, it is always
// necessary to define the create and getSignature methods.
// The createMethod will be responsible for loading a resource
// from disk using passed path.
template<class T> class Resource : public ResourceBase
{
public:
Resource() {}
Resource(const ResourceBase &base) { assign(base); }
void operator=(const ResourceBase & base) { assign(base); }
T* operator->() { return getResource(); }
T& operator*() { return *getResource(); }
operator T*() { return getResource(); }
const T* operator->() const { return getResource(); }
const T& operator*() const { return *getResource(); }
operator const T*() const { return getResource(); }
void setResource(const ResourceBase & base, void* resource) { assign(base, resource); }
static Signature signature();
/// Registering with this signal will give an opportunity to handle resource
/// creation before calling the create() function. This may be used to handle
/// file extensions differently and allow a more plugin-like approach to
/// adding resources. Using this mechanism, one could, for example, override
/// the default methods for loading DTS without touching the main source.
static Signal<bool(const Torque::Path &, void**)> &getLoadSignal()
{
static Signal<bool(const Torque::Path &, void**)> sLoadSignal;
return sLoadSignal;
}
/// Register with this signal to get notified when resources of this type
/// have been loaded.
static Signal< void( Resource< T >& ) >& getPostLoadSignal()
{
static Signal< void( Resource< T >& ) > sPostLoadSignal;
return sPostLoadSignal;
}
/// Register with this signal to get notified when resources of this type
/// are about to get unloaded.
static Signal< void( const Torque::Path&, T* ) >& getUnloadSignal()
{
static Signal< void( const Torque::Path&, T* ) > sUnloadSignal;
return sUnloadSignal;
}
private:
T *getResource() { return (T*)mResourceHeader->getResource(); }
const T *getResource() const { return (T*)mResourceHeader->getResource(); }
Signature getSignature() const { return Resource<T>::signature(); }
ResourceHolderBase *createHolder(void *);
Signal<bool(const Torque::Path &, void**)> &getStaticLoadSignal() { return getLoadSignal(); }
static void _notifyUnload( const Torque::Path& path, void* resource ) { getUnloadSignal().trigger( path, ( T* ) resource ); }
virtual void _triggerPostLoadSignal() { getPostLoadSignal().trigger( *this ); }
virtual NotifyUnloadFn _getNotifyUnloadFn() { return ( NotifyUnloadFn ) &_notifyUnload; }
// These are to be define by instantiated resources
// No generic version is provided...however, since
// base resources are instantiated by resource manager,
// these are not pure virtuals if undefined (but will assert)...
void *create(const Torque::Path &path);
};
template<class T> inline ResourceHolderBase *Resource<T>::createHolder(void *ptr)
{
ResourceHolder<T> *resHolder = (ResourceHolder<T>*)(ResourceHolderBase::smHolderFactory.alloc());
resHolder = constructInPlace(resHolder,(T*)ptr);
return resHolder;
}
//-----------------------------------------------------------------------------
// Load Signal Hooks.
//-----------------------------------------------------------------------------
/// This template may be used to register a load signal as follows:
/// static ResourceRegisterLoadSignal<T> sgAuto( staticLoadFunction );
template <class T>
class ResourceRegisterLoadSignal
{
public:
ResourceRegisterLoadSignal( Delegate<bool(const Torque::Path &, void **)> func )
{
Resource<T>::getLoadSignal().notify( func );
}
};
template< class T >
class ResourceRegisterPostLoadSignal
{
public:
ResourceRegisterPostLoadSignal( Delegate< void( Resource< T >& ) > func )
{
Resource< T >::getPostLoadSignal().notify( func );
}
};
template< class T >
class ResourceRegisterUnloadSignal
{
public:
ResourceRegisterUnloadSignal( Delegate< void( const Torque::Path&, T* ) > func )
{
Resource< T >::getUnloadSignal().notify( func );
}
};
#endif // __RESOURCE_H__

View file

@ -0,0 +1,246 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/resourceManager.h"
#include "core/volume.h"
#include "console/console.h"
#include "core/util/autoPtr.h"
#include "console/engineAPI.h"
static AutoPtr< ResourceManager > smInstance;
ResourceManager::ResourceManager()
: mIterSigFilter( U32_MAX )
{
}
ResourceManager::~ResourceManager()
{
// TODO: Dump resources that have not been released?
}
ResourceManager &ResourceManager::get()
{
if ( smInstance.isNull() )
smInstance = new ResourceManager;
return *smInstance;
}
ResourceBase ResourceManager::load(const Torque::Path &path)
{
#ifdef TORQUE_DEBUG_RES_MANAGER
Con::printf( "ResourceManager::load : [%s]", path.getFullPath().c_str() );
#endif
ResourceHeaderMap::Iterator iter = mResourceHeaderMap.findOrInsert( path.getFullPath() );
ResourceHeaderMap::Pair &pair = *iter;
if ( pair.value == NULL )
{
pair.value = new ResourceBase::Header;
// TODO: This can fail if the file doesn't exist
// at all which is possible.
//
// The problem is the templated design in ResourceManager
// keeps me from checking to see if the resource load failed
// before adding a notification.
//
// IMO the resource manager is overly templateized and
// we should refactor it so that its not so.
//
FS::AddChangeNotification( path, this, &ResourceManager::notifiedFileChanged );
}
ResourceBase::Header *header = pair.value;
if (header->getSignature() == 0)
header->mPath = path;
return ResourceBase( header );
}
ResourceBase ResourceManager::find(const Torque::Path &path)
{
#ifdef TORQUE_DEBUG_RES_MANAGER
Con::printf( "ResourceManager::find : [%s]", path.getFullPath().c_str() );
#endif
ResourceHeaderMap::Iterator iter = mResourceHeaderMap.find( path.getFullPath() );
if ( iter == mResourceHeaderMap.end() )
return ResourceBase();
ResourceHeaderMap::Pair &pair = *iter;
ResourceBase::Header *header = pair.value;
return ResourceBase(header);
}
#ifdef TORQUE_DEBUG
void ResourceManager::dumpToConsole()
{
const U32 numResources = mResourceHeaderMap.size();
if ( numResources == 0 )
{
Con::printf( "ResourceManager is not managing any resources" );
return;
}
Con::printf( "ResourceManager is managing %d resources:", numResources );
Con::printf( " [ref count/signature/path]" );
ResourceHeaderMap::Iterator iter;
for( iter = mResourceHeaderMap.begin(); iter != mResourceHeaderMap.end(); ++iter )
{
ResourceBase::Header *header = (*iter).value;
char fourCC[ 5 ];
*( ( U32* ) fourCC ) = header->getSignature();
fourCC[ 4 ] = 0;
Con::printf( " %3d %s [%s] ", header->getRefCount(), fourCC, (*iter).key.c_str() );
}
}
#endif
bool ResourceManager::remove( ResourceBase::Header* header )
{
const Path &path = header->getPath();
#ifdef TORQUE_DEBUG_RES_MANAGER
Con::printf( "ResourceManager::remove : [%s]", path.getFullPath().c_str() );
#endif
ResourceHeaderMap::Iterator iter = mResourceHeaderMap.find( path.getFullPath() );
if ( iter != mResourceHeaderMap.end() && iter->value == header )
{
AssertISV( header && (header->getRefCount() == 0), "ResourceManager error: trying to remove resource which is still in use." );
mResourceHeaderMap.erase( iter );
}
else
{
iter = mPrevResourceHeaderMap.find( path.getFullPath() );
if ( iter == mPrevResourceHeaderMap.end() || iter->value != header )
{
Con::errorf( "ResourceManager::remove : Trying to remove non-existent resource [%s]", path.getFullPath().c_str() );
return false;
}
AssertISV( header && (header->getRefCount() == 0), "ResourceManager error: trying to remove resource which is still in use." );
mPrevResourceHeaderMap.erase( iter );
}
FS::RemoveChangeNotification( path, this, &ResourceManager::notifiedFileChanged );
return true;
}
void ResourceManager::notifiedFileChanged( const Torque::Path &path )
{
reloadResource( path, true );
}
void ResourceManager::reloadResource( const Torque::Path &path, bool showMessage )
{
if ( showMessage )
Con::warnf( "[ResourceManager::notifiedFileChanged] : File changed [%s]", path.getFullPath().c_str() );
ResourceHeaderMap::Iterator iter = mResourceHeaderMap.find( path.getFullPath() );
if ( iter != mResourceHeaderMap.end() )
{
ResourceBase::Header *header = (*iter).value;
mResourceHeaderMap.erase( iter );
// Move the resource into the previous resource map.
iter = mPrevResourceHeaderMap.findOrInsert( path );
iter->value = header;
}
// Now notify users of the resource change so they
// can release and reload.
mChangeSignal.trigger( path );
}
ResourceBase ResourceManager::startResourceList( ResourceBase::Signature inSignature )
{
mIter = mResourceHeaderMap.begin();
mIterSigFilter = inSignature;
return nextResource();
}
ResourceBase ResourceManager::nextResource()
{
ResourceBase::Header *header = NULL;
while( mIter != mResourceHeaderMap.end() )
{
header = (*mIter).value;
++mIter;
if ( mIterSigFilter == U32_MAX )
return ResourceBase(header);
if ( header->getSignature() == mIterSigFilter )
return ResourceBase(header);
}
return ResourceBase();
}
ConsoleFunctionGroupBegin(ResourceManagerFunctions, "Resource management functions.");
#ifdef TORQUE_DEBUG
ConsoleFunction(resourceDump, void, 1, 1, "()"
"@brief List the currently managed resources\n\n"
"Currently used by editors only, internal\n"
"@ingroup Editors\n"
"@internal")
{
ResourceManager::get().dumpToConsole();
}
#endif
DefineEngineFunction( reloadResource, void, ( const char* path ),,
"Force the resource at specified input path to be reloaded\n"
"@param path Path to the resource to be reloaded\n\n"
"@tsexample\n"
"reloadResource( \"art/shapes/box.dts\" );\n"
"@endtsexample\n\n"
"@note Currently used by editors only\n"
"@ingroup Editors\n"
"@internal")
{
ResourceManager::get().reloadResource( path );
}
ConsoleFunctionGroupEnd( ResourceManagerFunctions );

View file

@ -0,0 +1,91 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _RESOURCEMANAGER_H_
#define _RESOURCEMANAGER_H_
#ifndef __RESOURCE_H__
#include "core/resource.h"
#endif
#ifndef _TDICTIONARY_H_
#include "core/util/tDictionary.h"
#endif
using namespace Torque;
class ResourceManager
{
public:
static ResourceManager &get();
ResourceBase load(const Torque::Path &path);
ResourceBase find(const Torque::Path &path);
ResourceBase startResourceList( ResourceBase::Signature inSignature = U32_MAX );
ResourceBase nextResource();
void reloadResource( const Torque::Path &path, bool showMessage = false );
typedef Signal<void(const Torque::Path &path)> ChangedSignal;
/// Registering with this signal will give an opportunity to handle a change to the
/// resource on disk. For example, if a PNG file is edited by the artist and saved
/// the ResourceManager will signal that the file has changed so the TextureManager
/// may act appropriately - which probably means to re-init the materials using that PNG.
/// The signal passes the Resource's signature so the callee may filter these.
ChangedSignal &getChangedSignal() { return mChangeSignal; }
#ifdef TORQUE_DEBUG
void dumpToConsole();
#endif
~ResourceManager();
protected:
friend class ResourceBase::Header;
ResourceManager();
bool remove( ResourceBase::Header* header );
void notifiedFileChanged( const Torque::Path &path );
typedef HashTable<String,ResourceBase::Header*> ResourceHeaderMap;
/// The map of resources.
ResourceHeaderMap mResourceHeaderMap;
/// The map of old resources which have been replaced by
/// new resources from a file change notification.
ResourceHeaderMap mPrevResourceHeaderMap;
ResourceHeaderMap::Iterator mIter;
U32 mIterSigFilter;
ChangedSignal mChangeSignal;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,385 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _BITSTREAM_H_
#define _BITSTREAM_H_
#ifndef _STREAM_H_
#include "core/stream/stream.h"
#endif
#ifndef _MPOINT3_H_
#include "math/mPoint3.h"
#endif
#ifndef _CRC_H_
#include "core/crc.h"
#endif
//-------------------------------------- Some caveats when using this class:
// - Get/setPosition semantics are changed
// to indicate bit position rather than
// byte position.
//
class Point3F;
class MatrixF;
class HuffmanProcessor;
class BitVector;
class QuatF;
class BitStream : public Stream
{
protected:
U8 *dataPtr;
S32 bitNum;
S32 bufSize;
bool error;
S32 maxReadBitNum;
S32 maxWriteBitNum;
char *stringBuffer;
Point3F mCompressPoint;
friend class HuffmanProcessor;
public:
static BitStream *getPacketStream(U32 writeSize = 0);
static void sendPacketStream(const NetAddress *addr);
void setBuffer(void *bufPtr, S32 bufSize, S32 maxSize = 0);
U8* getBuffer() { return dataPtr; }
U8* getBytePtr();
U32 getReadByteSize();
U32 getWriteByteSize();
S32 getCurPos() const;
void setCurPos(const U32);
// HACK: We reverted BitStream to this previous version
// because it was crashing the build.
//
// These are just here so that we don't have to revert
// the changes from the rest of the code.
//
// 9/11/2008 - Tom Spilman
//
S32 getBitPosition() const { return getCurPos(); }
void clearStringBuffer();
BitStream(void *bufPtr, S32 bufSize, S32 maxWriteSize = -1) { setBuffer(bufPtr, bufSize,maxWriteSize); stringBuffer = NULL; }
void clear();
void setStringBuffer(char buffer[256]);
void writeInt(S32 value, S32 bitCount);
S32 readInt(S32 bitCount);
/// Use this method to write out values in a concise but ass backwards way...
/// Good for values you expect to be frequently zero, often small. Worst case
/// this will bloat values by nearly 20% (5 extra bits!) Best case you'll get
/// one bit (if it's zero).
///
/// This is not so much for efficiency's sake, as to make life painful for
/// people that want to reverse engineer our network or file formats.
void writeCussedU32(U32 val)
{
// Is it zero?
if(writeFlag(val == 0))
return;
if(writeFlag(val <= 0xF)) // 4 bit
writeRangedU32(val, 0, 0xF);
else if(writeFlag(val <= 0xFF)) // 8 bit
writeRangedU32(val, 0, 0xFF);
else if(writeFlag(val <= 0xFFFF)) // 16 bit
writeRangedU32(val, 0, 0xFFFF);
else if(writeFlag(val <= 0xFFFFFF)) // 24 bit
writeRangedU32(val, 0, 0xFFFFFF);
else
writeRangedU32(val, 0, 0xFFFFFFFF);
}
U32 readCussedU32()
{
if(readFlag())
return 0;
if(readFlag())
return readRangedU32(0, 0xF);
else if(readFlag())
return readRangedU32(0, 0xFF);
else if(readFlag())
return readRangedU32(0, 0xFFFF);
else if(readFlag())
return readRangedU32(0, 0xFFFFFF);
else
return readRangedU32(0, 0xFFFFFFFF);
}
void writeSignedInt(S32 value, S32 bitCount);
S32 readSignedInt(S32 bitCount);
void writeRangedU32(U32 value, U32 rangeStart, U32 rangeEnd);
U32 readRangedU32(U32 rangeStart, U32 rangeEnd);
/// Writes a clamped signed integer to the stream using
/// an optimal amount of bits for the range.
void writeRangedS32( S32 value, S32 min, S32 max );
/// Reads a ranged signed integer written with writeRangedS32.
S32 readRangedS32( S32 min, S32 max );
// read and write floats... floats are 0 to 1 inclusive, signed floats are -1 to 1 inclusive
F32 readFloat(S32 bitCount);
F32 readSignedFloat(S32 bitCount);
void writeFloat(F32 f, S32 bitCount);
void writeSignedFloat(F32 f, S32 bitCount);
/// Writes a clamped floating point value to the
/// stream with the desired bits of precision.
void writeRangedF32( F32 value, F32 min, F32 max, U32 numBits );
/// Reads a ranged floating point value written with writeRangedF32.
F32 readRangedF32( F32 min, F32 max, U32 numBits );
void writeClassId(U32 classId, U32 classType, U32 classGroup);
S32 readClassId(U32 classType, U32 classGroup); // returns -1 if the class type is out of range
// writes a normalized vector
void writeNormalVector(const Point3F& vec, S32 bitCount);
void readNormalVector(Point3F *vec, S32 bitCount);
void clearCompressionPoint();
void setCompressionPoint(const Point3F& p);
// Matching calls to these compression methods must, of course,
// have matching scale values.
void writeCompressedPoint(const Point3F& p,F32 scale = 0.001f);
void readCompressedPoint(Point3F* p,F32 scale = 0.001f);
// Uses the above method to reduce the precision of a normal vector so the server can
// determine exactly what is on the client. (Pre-dumbing the vector before sending
// to the client can result in precision errors...)
static Point3F dumbDownNormal(const Point3F& vec, S32 bitCount);
/// Writes a compressed vector as separate magnitude and
/// normal components. The final space used depends on the
/// content of the vector.
///
/// - 1 bit is used to skip over zero length vectors.
/// - 1 bit is used to mark if the magnitude exceeds max.
/// - The magnitude as:
/// a. magBits if less than maxMag.
/// b. a full 32bit value if greater than maxMag.
/// - The normal as a phi and theta sized normalBits+1 and normalBits.
///
void writeVector( Point3F vec, F32 maxMag, S32 magBits, S32 normalBits );
/// Reads a compressed vector.
/// @see writeVector
void readVector( Point3F *outVec, F32 maxMag, S32 magBits, S32 normalBits );
// writes an affine transform (full precision version)
void writeAffineTransform(const MatrixF&);
void readAffineTransform(MatrixF*);
/// Writes a quaternion in a lossy compressed format that
/// is ( bitCount * 3 ) + 1 bits in size.
///
/// @param quat The normalized quaternion to write.
/// @param bitCount The the storage space for the xyz component of
/// the quaternion.
///
void writeQuat( const QuatF& quat, U32 bitCount = 9 );
/// Reads a quaternion written with writeQuat.
///
/// @param quat The normalized quaternion to write.
/// @param bitCount The the storage space for the xyz component of
/// the quaternion. Must match the bitCount at write.
/// @see writeQuat
///
void readQuat( QuatF *outQuat, U32 bitCount = 9 );
virtual void writeBits(S32 bitCount, const void *bitPtr);
virtual void readBits(S32 bitCount, void *bitPtr);
virtual bool writeFlag(bool val);
inline bool writeFlag(U32 val)
{
return writeFlag(val != 0);
}
inline bool writeFlag(void *val)
{
return writeFlag(val != 0);
}
virtual bool readFlag();
void writeBits(const BitVector &bitvec);
void readBits(BitVector *bitvec);
void setBit(S32 bitCount, bool set);
bool testBit(S32 bitCount);
bool isFull() { return bitNum > (bufSize << 3); }
bool isValid() { return !error; }
bool _read (const U32 size,void* d);
bool _write(const U32 size,const void* d);
void readString(char stringBuf[256]);
void writeString(const char *stringBuf, S32 maxLen=255);
bool hasCapability(const Capability) const { return true; }
U32 getPosition() const;
bool setPosition(const U32 in_newPosition);
U32 getStreamSize();
};
class ResizeBitStream : public BitStream
{
protected:
U32 mMinSpace;
public:
ResizeBitStream(U32 minSpace = 1500, U32 initialSize = 0);
void validate();
~ResizeBitStream();
};
/// This class acts to provide an "infinitely extending" stream.
///
/// Basically, it does what ResizeBitStream does, but it validates
/// on every write op, so that you never have to worry about overwriting
/// the buffer.
class InfiniteBitStream : public ResizeBitStream
{
public:
InfiniteBitStream();
~InfiniteBitStream();
/// Ensure we have space for at least upcomingBytes more bytes in the stream.
void validate(U32 upcomingBytes);
/// Reset the stream to zero length (but don't clean memory).
void reset();
/// Shrink the buffer down to match the actual size of the data.
void compact();
/// Write us out to a stream... Results in last byte getting padded!
void writeToStream(Stream &s);
virtual void writeBits(S32 bitCount, const void *bitPtr)
{
validate((bitCount >> 3) + 1); // Add a little safety.
BitStream::writeBits(bitCount, bitPtr);
}
virtual bool writeFlag(bool val)
{
validate(1); // One bit will at most grow our buffer by a byte.
return BitStream::writeFlag(val);
}
const U32 getCRC()
{
// This could be kinda inefficient - BJG
return CRC::calculateCRC(getBuffer(), getStreamSize());
}
};
//------------------------------------------------------------------------------
//-------------------------------------- INLINES
//
inline S32 BitStream::getCurPos() const
{
return bitNum;
}
inline void BitStream::setCurPos(const U32 in_position)
{
AssertFatal(in_position < (U32)(bufSize << 3), "Out of range bitposition");
bitNum = S32(in_position);
}
inline bool BitStream::readFlag()
{
if(bitNum > maxReadBitNum)
{
error = true;
AssertFatal(false, "Out of range read");
return false;
}
S32 mask = 1 << (bitNum & 0x7);
bool ret = (*(dataPtr + (bitNum >> 3)) & mask) != 0;
bitNum++;
return ret;
}
inline void BitStream::writeRangedU32(U32 value, U32 rangeStart, U32 rangeEnd)
{
AssertFatal(value >= rangeStart && value <= rangeEnd, "Out of bounds value!");
AssertFatal(rangeEnd >= rangeStart, "error, end of range less than start");
U32 rangeSize = rangeEnd - rangeStart + 1;
U32 rangeBits = getBinLog2(getNextPow2(rangeSize));
writeInt(S32(value - rangeStart), S32(rangeBits));
}
inline U32 BitStream::readRangedU32(U32 rangeStart, U32 rangeEnd)
{
AssertFatal(rangeEnd >= rangeStart, "error, end of range less than start");
U32 rangeSize = rangeEnd - rangeStart + 1;
U32 rangeBits = getBinLog2(getNextPow2(rangeSize));
U32 val = U32(readInt(S32(rangeBits)));
return val + rangeStart;
}
inline void BitStream::writeRangedS32( S32 value, S32 min, S32 max )
{
value = mClamp( value, min, max );
writeRangedU32( ( value - min ), 0, ( max - min ) );
}
inline S32 BitStream::readRangedS32( S32 min, S32 max )
{
return readRangedU32( 0, ( max - min ) ) + min;
}
inline void BitStream::writeRangedF32( F32 value, F32 min, F32 max, U32 numBits )
{
value = ( mClampF( value, min, max ) - min ) / ( max - min );
writeInt( (S32)mFloor(value * F32( (1 << numBits) - 1 )), numBits );
}
inline F32 BitStream::readRangedF32( F32 min, F32 max, U32 numBits )
{
F32 value = (F32)readInt( numBits );
value /= F32( ( 1 << numBits ) - 1 );
return min + value * ( max - min );
}
#endif //_BITSTREAM_H_

View file

@ -0,0 +1,579 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/stream/fileStream.h"
//-----------------------------------------------------------------------------
// FileStream methods...
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
FileStream::FileStream()
{
// initialize the file stream
init();
}
FileStream *FileStream::createAndOpen(const String &inFileName, Torque::FS::File::AccessMode inMode)
{
FileStream *newStream = new FileStream;
if ( newStream )
{
bool success = newStream->open( inFileName, inMode );
if ( !success )
{
delete newStream;
newStream = NULL;
}
}
return newStream;
}
//-----------------------------------------------------------------------------
FileStream::~FileStream()
{
// make sure the file stream is closed
close();
}
//-----------------------------------------------------------------------------
bool FileStream::hasCapability(const Capability i_cap) const
{
return(0 != (U32(i_cap) & mStreamCaps));
}
//-----------------------------------------------------------------------------
U32 FileStream::getPosition() const
{
AssertFatal(0 != mStreamCaps, "FileStream::getPosition: the stream isn't open");
//AssertFatal(true == hasCapability(StreamPosition), "FileStream::getPosition(): lacks positioning capability");
// return the position inside the buffer if its valid, otherwise return the underlying file position
return((BUFFER_INVALID != mBuffHead) ? mBuffPos : mFile->getPosition());
}
//-----------------------------------------------------------------------------
bool FileStream::setPosition(const U32 i_newPosition)
{
AssertFatal(0 != mStreamCaps, "FileStream::setPosition: the stream isn't open");
AssertFatal(hasCapability(StreamPosition), "FileStream::setPosition: lacks positioning capability");
// if the buffer is valid, test the new position against the bounds of the buffer
if ((BUFFER_INVALID != mBuffHead) && (i_newPosition >= mBuffHead) && (i_newPosition <= mBuffTail))
{
// set the position and return
mBuffPos = i_newPosition;
// FIXME [tom, 9/5/2006] This needs to be checked. Basically, when seeking within
// the buffer, if the stream has an EOS status before the seek then if you try to
// read immediately after seeking, you'll incorrectly get an EOS.
//
// I am not 100% sure if this fix is correct, but it seems to be working for the undo system.
if(mBuffPos < mBuffTail)
Stream::setStatus(Ok);
return(true);
}
// otherwise the new position lies in some block not in memory
else
{
if (mDirty)
flush();
clearBuffer();
mFile->setPosition(i_newPosition, Torque::FS::File::Begin);
setStatus();
if (mFile->getStatus() == Torque::FS::FileNode::EndOfFile)
mEOF = true;
return(Ok == getStatus() || EOS == getStatus());
}
}
//-----------------------------------------------------------------------------
U32 FileStream::getStreamSize()
{
AssertWarn(0 != mStreamCaps, "FileStream::getStreamSize: the stream isn't open");
AssertFatal((BUFFER_INVALID != mBuffHead && true == mDirty) || false == mDirty, "FileStream::getStreamSize: buffer must be valid if its dirty");
// the stream size may not match the size on-disk if its been written to...
if (mDirty)
return(getMax((U32)(mFile->getSize()), mBuffTail + 1)); ///<@todo U64 vs U32 issue
// otherwise just get the size on disk...
else
return(mFile->getSize());
}
//-----------------------------------------------------------------------------
bool FileStream::open(const String &inFileName, Torque::FS::File::AccessMode inMode)
{
AssertWarn(0 == mStreamCaps, "FileStream::setPosition: the stream is already open");
AssertFatal(inFileName.isNotEmpty(), "FileStream::open: empty filename");
// make sure the file stream's state is clean
clearBuffer();
Torque::Path filePath(inFileName);
// IF we are writing, make sure the path exists
if( inMode == Torque::FS::File::Write || inMode == Torque::FS::File::WriteAppend || inMode == Torque::FS::File::ReadWrite )
Torque::FS::CreatePath(filePath);
mFile = Torque::FS::OpenFile(filePath, inMode);
if (mFile != NULL)
{
setStatus();
switch (inMode)
{
case Torque::FS::File::Read:
mStreamCaps = U32(StreamRead) |
U32(StreamPosition);
break;
case Torque::FS::File::Write:
case Torque::FS::File::WriteAppend:
mStreamCaps = U32(StreamWrite) |
U32(StreamPosition);
break;
case Torque::FS::File::ReadWrite:
mStreamCaps = U32(StreamRead) |
U32(StreamWrite) |
U32(StreamPosition);
break;
default:
AssertFatal(false, String::ToString( "FileStream::open: bad access mode on %s", inFileName.c_str() ));
}
}
else
{
Stream::setStatus(IOError);
return(false);
}
return getStatus() == Ok;
}
//-----------------------------------------------------------------------------
void FileStream::close()
{
if (getStatus() == Closed)
return;
if (mFile != NULL )
{
// make sure nothing in the buffer differs from what is on disk
if (mDirty)
flush();
// and close the file
mFile->close();
AssertFatal(mFile->getStatus() == Torque::FS::FileNode::Closed, "FileStream::close: close failed");
mFile = NULL;
}
// clear the file stream's state
init();
}
//-----------------------------------------------------------------------------
bool FileStream::flush()
{
AssertWarn(0 != mStreamCaps, "FileStream::flush: the stream isn't open");
AssertFatal(false == mDirty || BUFFER_INVALID != mBuffHead, "FileStream::flush: buffer must be valid if its dirty");
// if the buffer is dirty
if (mDirty)
{
AssertFatal(hasCapability(StreamWrite), "FileStream::flush: a buffer without write-capability should never be dirty");
// align the file pointer to the buffer head
if (mBuffHead != mFile->getPosition())
{
mFile->setPosition(mBuffHead, Torque::FS::File::Begin);
if (mFile->getStatus() != Torque::FS::FileNode::Open && mFile->getStatus() != Torque::FS::FileNode::EndOfFile)
return(false);
}
// write contents of the buffer to disk
U32 blockHead;
calcBlockHead(mBuffHead, &blockHead);
mFile->write((char *)mBuffer + (mBuffHead - blockHead), mBuffTail - mBuffHead + 1);
// and update the file stream's state
setStatus();
if (EOS == getStatus())
mEOF = true;
if (Ok == getStatus() || EOS == getStatus())
// and update the status of the buffer
mDirty = false;
else
return(false);
}
return(true);
}
//-----------------------------------------------------------------------------
bool FileStream::_read(const U32 i_numBytes, void *o_pBuffer)
{
AssertFatal(0 != mStreamCaps, "FileStream::_read: the stream isn't open");
AssertFatal(NULL != o_pBuffer || i_numBytes == 0, "FileStream::_read: NULL destination pointer with non-zero read request");
if (!hasCapability(Stream::StreamRead))
{
AssertFatal(false, "FileStream::_read: file stream lacks capability");
Stream::setStatus(IllegalCall);
return(false);
}
// exit on pre-existing errors
if (Ok != getStatus())
return(false);
// if a request of non-zero length was made
if (0 != i_numBytes)
{
U8 *pDst = (U8 *)o_pBuffer;
U32 readSize;
U32 remaining = i_numBytes;
U32 bytesRead;
U32 blockHead;
U32 blockTail;
// check if the buffer has some data in it
if (BUFFER_INVALID != mBuffHead)
{
// copy as much as possible from the buffer into the destination
readSize = ((mBuffTail + 1) >= mBuffPos) ? (mBuffTail + 1 - mBuffPos) : 0;
readSize = getMin(readSize, remaining);
calcBlockHead(mBuffPos, &blockHead);
dMemcpy(pDst, mBuffer + (mBuffPos - blockHead), readSize);
// reduce the remaining amount to read
remaining -= readSize;
// advance the buffer pointers
mBuffPos += readSize;
pDst += readSize;
if (mBuffPos > mBuffTail && remaining != 0)
{
flush();
mBuffHead = BUFFER_INVALID;
if (mEOF == true)
Stream::setStatus(EOS);
}
}
// if the request wasn't satisfied by the buffer and the file has more data
if (false == mEOF && 0 < remaining)
{
// flush the buffer if its dirty, since we now need to go to disk
if (true == mDirty)
flush();
// make sure we know the current read location in the underlying file
mBuffPos = mFile->getPosition();
calcBlockBounds(mBuffPos, &blockHead, &blockTail);
// check if the data to be read falls within a single block
if ((mBuffPos + remaining) <= blockTail)
{
// fill the buffer from disk
if (true == fillBuffer(mBuffPos))
{
// copy as much as possible from the buffer to the destination
remaining = getMin(remaining, mBuffTail - mBuffPos + 1);
dMemcpy(pDst, mBuffer + (mBuffPos - blockHead), remaining);
// advance the buffer pointer
mBuffPos += remaining;
}
else
return(false);
}
// otherwise the remaining spans multiple blocks
else
{
clearBuffer();
// read from disk directly into the destination
bytesRead = mFile->read((char *)pDst, remaining);
setStatus();
// check to make sure we read as much as expected
if (Ok == getStatus() || EOS == getStatus())
{
// if not, update the end-of-file status
if (0 != bytesRead && EOS == getStatus())
{
Stream::setStatus(Ok);
mEOF = true;
}
}
else
return(false);
}
}
}
return(true);
}
//-----------------------------------------------------------------------------
bool FileStream::_write(const U32 i_numBytes, const void *i_pBuffer)
{
AssertFatal(0 != mStreamCaps, "FileStream::_write: the stream isn't open");
AssertFatal(NULL != i_pBuffer || i_numBytes == 0, "FileStream::_write: NULL source buffer pointer on non-zero write request");
if (!hasCapability(Stream::StreamWrite))
{
AssertFatal(false, "FileStream::_write: file stream lacks capability");
Stream::setStatus(IllegalCall);
return(false);
}
// exit on pre-existing errors
if (Ok != getStatus() && EOS != getStatus())
return(false);
// if a request of non-zero length was made
if (0 != i_numBytes)
{
U8 *pSrc = (U8 *)i_pBuffer;
U32 writeSize;
U32 remaining = i_numBytes;
U32 bytesWrit;
U32 blockHead;
U32 blockTail;
// check if the buffer is valid
if (BUFFER_INVALID != mBuffHead)
{
// copy as much as possible from the source to the buffer
calcBlockBounds(mBuffHead, &blockHead, &blockTail);
writeSize = (mBuffPos > blockTail) ? 0 : blockTail - mBuffPos + 1;
writeSize = getMin(writeSize, remaining);
AssertFatal(0 == writeSize || (mBuffPos - blockHead) < BUFFER_SIZE, "FileStream::_write: out of bounds buffer position");
dMemcpy(mBuffer + (mBuffPos - blockHead), pSrc, writeSize);
// reduce the remaining amount to be written
remaining -= writeSize;
// advance the buffer pointers
mBuffPos += writeSize;
mBuffTail = getMax(mBuffTail, mBuffPos - 1);
pSrc += writeSize;
// mark the buffer dirty
if (0 < writeSize)
mDirty = true;
}
// if the request wasn't satisfied by the buffer
if (0 < remaining)
{
// flush the buffer if its dirty, since we now need to go to disk
if (mDirty)
flush();
// make sure we know the current write location in the underlying file
mBuffPos = mFile->getPosition();
calcBlockBounds(mBuffPos, &blockHead, &blockTail);
// check if the data to be written falls within a single block
if ((mBuffPos + remaining) <= blockTail)
{
// write the data to the buffer
dMemcpy(mBuffer + (mBuffPos - blockHead), pSrc, remaining);
// update the buffer pointers
mBuffHead = mBuffPos;
mBuffPos += remaining;
mBuffTail = mBuffPos - 1;
// mark the buffer dirty
mDirty = true;
}
// otherwise the remaining spans multiple blocks
else
{
clearBuffer();
// write to disk directly from the source
bytesWrit = mFile->write((char *)pSrc, remaining);
setStatus();
return(Ok == getStatus() || EOS == getStatus());
}
}
}
return(true);
}
//-----------------------------------------------------------------------------
void FileStream::init()
{
mStreamCaps = 0;
Stream::setStatus(Closed);
clearBuffer();
}
//-----------------------------------------------------------------------------
bool FileStream::fillBuffer(const U32 i_startPosition)
{
AssertFatal(0 != mStreamCaps, "FileStream::fillBuffer: the stream isn't open");
AssertFatal(false == mDirty, "FileStream::fillBuffer: buffer must be clean to fill");
// make sure start position and file pointer jive
if (i_startPosition != mFile->getPosition())
{
mFile->setPosition(i_startPosition, Torque::FS::File::Begin);
if (mFile->getStatus() != Torque::FS::FileNode::Open && mFile->getStatus() != Torque::FS::FileNode::EndOfFile)
{
setStatus();
return(false);
}
else
// update buffer pointer
mBuffPos = i_startPosition;
}
// check if file pointer is at end-of-file
if (EOS == getStatus())
{
// invalidate the buffer
mBuffHead = BUFFER_INVALID;
// set the status to end-of-stream
mEOF = true;
}
// otherwise
else
{
U32 blockHead;
// locate bounds of buffer containing current position
calcBlockHead(mBuffPos, &blockHead);
// read as much as possible from input file
U32 bytesRead = mFile->read((char *)mBuffer + (i_startPosition - blockHead), BUFFER_SIZE - (i_startPosition - blockHead));
setStatus();
if (Ok == getStatus() || EOS == getStatus())
{
// update buffer pointers
mBuffHead = i_startPosition;
mBuffPos = i_startPosition;
mBuffTail = i_startPosition + bytesRead - 1;
// update end-of-file status
if (0 != bytesRead && EOS == getStatus())
{
Stream::setStatus(Ok);
mEOF = true;
}
}
else
{
mBuffHead = BUFFER_INVALID;
return(false);
}
}
return(true);
}
//-----------------------------------------------------------------------------
void FileStream::clearBuffer()
{
mBuffHead = BUFFER_INVALID;
mBuffPos = 0;
mBuffTail = 0;
mDirty = false;
mEOF = false;
}
//-----------------------------------------------------------------------------
void FileStream::calcBlockHead(const U32 i_position, U32 *o_blockHead)
{
AssertFatal(NULL != o_blockHead, "FileStream::calcBlockHead: NULL pointer passed for block head");
*o_blockHead = i_position/BUFFER_SIZE * BUFFER_SIZE;
}
//-----------------------------------------------------------------------------
void FileStream::calcBlockBounds(const U32 i_position, U32 *o_blockHead, U32 *o_blockTail)
{
AssertFatal(NULL != o_blockHead, "FileStream::calcBlockBounds: NULL pointer passed for block head");
AssertFatal(NULL != o_blockTail, "FileStream::calcBlockBounds: NULL pointer passed for block tail");
*o_blockHead = i_position/BUFFER_SIZE * BUFFER_SIZE;
*o_blockTail = *o_blockHead + BUFFER_SIZE - 1;
}
//-----------------------------------------------------------------------------
void FileStream::setStatus()
{
switch (mFile->getStatus())
{
case Torque::FS::FileNode::Open:
Stream::setStatus(Ok);
break;
case Torque::FS::FileNode::Closed:
Stream::setStatus(Closed);
break;
case Torque::FS::FileNode::EndOfFile:
Stream::setStatus(EOS);
break;
case Torque::FS::FileNode::FileSystemFull:
case Torque::FS::FileNode::NoSuchFile:
case Torque::FS::FileNode::AccessDenied:
case Torque::FS::FileNode::NoDisk:
case Torque::FS::FileNode::SharingViolation:
Stream::setStatus(IOError);
break;
case Torque::FS::FileNode::IllegalCall:
Stream::setStatus(IllegalCall);
break;
case Torque::FS::FileNode::UnknownError:
Stream::setStatus(UnknownError);
break;
default:
AssertFatal(false, "FileStream::setStatus: invalid error mode");
}
}
FileStream* FileStream::clone() const
{
Torque::FS::File::AccessMode mode;
if( hasCapability( StreamWrite ) && hasCapability( StreamRead ) )
mode = Torque::FS::File::ReadWrite;
else if( hasCapability( StreamWrite ) )
mode = Torque::FS::File::Write;
else
mode = Torque::FS::File::Read;
FileStream* copy = createAndOpen( mFile->getName(), mode );
if( copy && copy->setPosition( getPosition() ) )
return copy;
delete copy;
return NULL;
}

View file

@ -0,0 +1,93 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _FILESTREAM_H_
#define _FILESTREAM_H_
#ifndef _VOLUME_H_
#include "core/volume.h"
#endif
#ifndef _STREAM_H_
#include "core/stream/stream.h"
#endif
class FileStream : public Stream
{
public:
enum
{
BUFFER_SIZE = 8 * 1024, // this can be changed to anything appropriate [in k]
BUFFER_INVALID = 0xffffffff // file offsets must all be less than this
};
public:
FileStream(); // default constructor
virtual ~FileStream(); // destructor
// This function will allocate a new FileStream and open it.
// If it fails to allocate or fails to open, it will return NULL.
// The caller is responsible for deleting the instance.
static FileStream *createAndOpen(const String &inFileName, Torque::FS::File::AccessMode inMode);
// mandatory methods from Stream base class...
virtual bool hasCapability(const Capability i_cap) const;
virtual U32 getPosition() const;
virtual bool setPosition(const U32 i_newPosition);
virtual U32 getStreamSize();
// additional methods needed for a file stream...
virtual bool open(const String &inFileName, Torque::FS::File::AccessMode inMode);
virtual void close();
bool flush();
FileStream* clone() const;
protected:
// more mandatory methods from Stream base class...
virtual bool _read(const U32 i_numBytes, void *o_pBuffer);
virtual bool _write(const U32 i_numBytes, const void* i_pBuffer);
void init();
bool fillBuffer(const U32 i_startPosition);
void clearBuffer();
static void calcBlockHead(const U32 i_position, U32 *o_blockHead);
static void calcBlockBounds(const U32 i_position, U32 *o_blockHead, U32 *o_blockTail);
void setStatus();
U32 mStreamCaps; // dependent on access mode
private:
Torque::FS::FileRef mFile; // file being streamed
U8 mBuffer[BUFFER_SIZE];
U32 mBuffHead; // first valid position of buffer (from start-of-file)
U32 mBuffPos; // next read or write will occur here
U32 mBuffTail; // last valid position in buffer (inclusive)
bool mDirty; // whether buffer has been written to
bool mEOF; // whether disk reads have reached the end-of-file
FileStream(const FileStream &i_fileStrm); // disable copy constructor
FileStream& operator=(const FileStream &i_fileStrm); // disable assignment operator
};
#endif // _FILE_STREAM_H

View file

@ -0,0 +1,175 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/stream/fileStreamObject.h"
#include "console/engineAPI.h"
//-----------------------------------------------------------------------------
// Local Globals
//-----------------------------------------------------------------------------
static const struct
{
const char *strMode;
Torque::FS::File::AccessMode mode;
} gModeMap[]=
{
{ "read", Torque::FS::File::Read },
{ "write", Torque::FS::File::Write },
{ "readwrite", Torque::FS::File::ReadWrite },
{ "writeappend", Torque::FS::File::WriteAppend },
{ NULL, (Torque::FS::File::AccessMode)0 }
};
//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
FileStreamObject::FileStreamObject()
{
}
FileStreamObject::~FileStreamObject()
{
close();
}
IMPLEMENT_CONOBJECT(FileStreamObject);
ConsoleDocClass( FileStreamObject,
"@brief A wrapper around StreamObject for parsing text and data from files.\n\n"
"FileStreamObject inherits from StreamObject and provides some unique methods for working "
"with strings. If you're looking for general file handling, you may want to use FileObject.\n\n"
"@tsexample\n"
"// Create a file stream object for reading\n"
"%fsObject = new FileStreamObject();\n\n"
"// Open a file for reading\n"
"%fsObject.open(\"./test.txt\", \"read\");\n\n"
"// Get the status and print it\n"
"%status = %fsObject.getStatus();\n"
"echo(%status);\n\n"
"// Always remember to close a file stream when finished\n"
"%fsObject.close();\n"
"@endtsexample\n\n"
"@see StreamObject for the list of inherited functions variables\n"
"@see FileObject for general file handling.\n"
"@ingroup FileSystem\n"
);
//-----------------------------------------------------------------------------
bool FileStreamObject::onAdd()
{
// [tom, 2/12/2007] Skip over StreamObject's onAdd() so that we can
// be instantiated from script.
return SimObject::onAdd();
}
//-----------------------------------------------------------------------------
// Public Methods
//-----------------------------------------------------------------------------
bool FileStreamObject::open(const char *filename, Torque::FS::File::AccessMode mode)
{
close();
if(! mFileStream.open(filename, mode))
return false;
mStream = &mFileStream;
return true;
}
void FileStreamObject::close()
{
mFileStream.close();
mStream = NULL;
}
//-----------------------------------------------------------------------------
// Console Methods
//-----------------------------------------------------------------------------
DefineEngineMethod( FileStreamObject, open, bool, (const char* filename, const char* openMode),,
"@brief Open a file for reading, writing, reading and writing, or appending\n\n"
"Using \"Read\" for the open mode allows you to parse the contents of file, but not making modifications. \"Write\" will create a new "
"file if it does not exist, or erase the contents of an existing file when opened. Write also allows you to modify the contents of the file.\n\n"
"\"ReadWrite\" will provide the ability to parse data (read it in) and manipulate data (write it out) interchangeably. Keep in mind the stream can "
"move during each operation. Finally, \"WriteAppend\" will open a file if it exists, but will not clear the contents. You can write new data starting "
" at the end of the files existing contents.\n\n"
"@param filename Name of file to open\n"
"@param openMode One of \"Read\", \"Write\", \"ReadWrite\" or \"WriteAppend\"\n\n"
"@tsexample\n"
"// Create a file stream object for reading\n"
"%fsObject = new FileStreamObject();\n\n"
"// Open a file for reading\n"
"%fsObject.open(\"./test.txt\", \"read\");\n\n"
"// Get the status and print it\n"
"%status = %fsObject.getStatus();\n"
"echo(%status);\n\n"
"// Always remember to close a file stream when finished\n"
"%fsObject.close();\n"
"@endtsexample\n\n"
"@return True if the file was successfully opened, false if something went wrong"
"@see close()")
{
for(S32 i = 0;gModeMap[i].strMode;++i)
{
if(dStricmp(gModeMap[i].strMode, openMode) == 0)
{
Torque::FS::File::AccessMode mode = gModeMap[i].mode;
char buffer[1024];
Con::expandScriptFilename(buffer, sizeof(buffer), filename);
return object->open(buffer, mode);
}
}
Con::errorf("FileStreamObject::open - Mode must be one of Read, Write, ReadWrite or WriteAppend.");
return false;
}
DefineEngineMethod( FileStreamObject, close, void, (),,
"@brief Close the file. You can no longer read or write to it unless you open it again.\n\n"
"@tsexample\n"
"// Create a file stream object for reading\n"
"%fsObject = new FileStreamObject();\n\n"
"// Open a file for reading\n"
"%fsObject.open(\"./test.txt\", \"read\");\n\n"
"// Always remember to close a file stream when finished\n"
"%fsObject.close();\n"
"@endtsexample\n\n"
"@see open()")
{
object->close();
}

View file

@ -0,0 +1,67 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _FILESTREAMOBJECT_H_
#define _FILESTREAMOBJECT_H_
#ifndef _STREAMOBJECT_H_
#include "core/stream/streamObject.h"
#endif
#ifndef _FILESTREAM_H_
#include "core/stream/fileStream.h"
#endif
class FileStreamObject : public StreamObject
{
typedef StreamObject Parent;
protected:
FileStream mFileStream;
public:
FileStreamObject();
virtual ~FileStreamObject();
DECLARE_CONOBJECT(FileStreamObject);
virtual bool onAdd();
//-----------------------------------------------------------------------------
/// @brief Open a file
///
/// @param filename Name of file to open
/// @param mode One of #Torque::FS::File::Read, #Torque::FS::File::Write, #Torque::FS::File::ReadWrite or #Torque::FS::File::WriteAppend
/// @return true for success, false for failure
/// @see close()
//-----------------------------------------------------------------------------
bool open(const char *filename, Torque::FS::File::AccessMode mode);
//-----------------------------------------------------------------------------
/// @brief Close the file
///
/// @see open()
//-----------------------------------------------------------------------------
void close();
};
#endif // _FILESTREAMOBJECT_H_

View file

@ -0,0 +1,115 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _UTIL_IOHELPER_H_
#define _UTIL_IOHELPER_H_
#ifndef _CORE_STREAM_H_
#include "core/stream/stream.h"
#endif
/// Helper templates to aggregate IO operations - generally used in
/// template expansion.
namespace IOHelper
{
template<class A,class B,class C,class D,class E,class F,class G,class H,class I,class J>
inline bool reads(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f,G& g,H& h,I& i,J& j)
{ s->read(&a); s->read(&b); s->read(&c); s->read(&d); s->read(&e); s->read(&f); s->read(&g); s->read(&h); s->read(&i); return s->read(&j); }
template<class A,class B,class C,class D,class E,class F,class G,class H,class I>
inline bool reads(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f,G& g,H& h,I& i)
{ s->read(&a); s->read(&b); s->read(&c); s->read(&d); s->read(&e); s->read(&f); s->read(&g); s->read(&h); return s->read(&i); }
template<class A,class B,class C,class D,class E,class F,class G,class H>
inline bool reads(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f,G& g,H& h)
{ s->read(&a); s->read(&b); s->read(&c); s->read(&d); s->read(&e); s->read(&f); s->read(&g); return s->read(&h); }
template<class A,class B,class C,class D,class E,class F,class G>
inline bool reads(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f,G& g)
{ s->read(&a); s->read(&b); s->read(&c); s->read(&d); s->read(&e); s->read(&f); return s->read(&g); }
template<class A,class B,class C,class D,class E,class F>
inline bool reads(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f)
{ s->read(&a); s->read(&b); s->read(&c); s->read(&d); s->read(&e); return s->read(&f); }
template<class A,class B,class C,class D,class E>
inline bool reads(Stream *s,A& a,B& b,C& c,D& d,E& e)
{ s->read(&a); s->read(&b); s->read(&c); s->read(&d); return s->read(&e); }
template<class A,class B,class C,class D>
inline bool reads(Stream *s,A& a,B& b,C& c,D& d)
{ s->read(&a); s->read(&b); s->read(&c); return s->read(&d); }
template<class A,class B,class C>
inline bool reads(Stream *s,A& a,B& b,C& c)
{ s->read(&a); s->read(&b); return s->read(&c); }
template<class A,class B>
inline bool reads(Stream *s,A& a,B& b)
{ s->read(&a); return s->read(&b); }
template<class A>
inline bool reads(Stream *s,A& a)
{ return s->read(&a); }
template<class A,class B,class C,class D,class E,class F,class G,class H,class I,class J>
inline bool writes(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f,G& g,H& h,I& i,J& j)
{ s->write(a); s->write(b); s->write(c); s->write(d); s->write(e); s->write(f); s->write(g); s->write(h); s->write(i); return s->write(j); }
template<class A,class B,class C,class D,class E,class F,class G,class H,class I>
inline bool writes(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f,G& g,H& h,I& i)
{ s->write(a); s->write(b); s->write(c); s->write(d); s->write(e); s->write(f); s->write(g); s->write(h); return s->write(i); }
template<class A,class B,class C,class D,class E,class F,class G,class H>
inline bool writes(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f,G& g,H& h)
{ s->write(a); s->write(b); s->write(c); s->write(d); s->write(e); s->write(f); s->write(g); return s->write(h); }
template<class A,class B,class C,class D,class E,class F,class G>
inline bool writes(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f,G& g)
{ s->write(a); s->write(b); s->write(c); s->write(d); s->write(e); s->write(f); return s->write(g); }
template<class A,class B,class C,class D,class E,class F>
inline bool writes(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f)
{ s->write(a); s->write(b); s->write(c); s->write(d); s->write(e); return s->write(f); }
template<class A,class B,class C,class D,class E>
inline bool writes(Stream *s,A& a,B& b,C& c,D& d,E& e)
{ s->write(a); s->write(b); s->write(c); s->write(d); return s->write(e); }
template<class A,class B,class C,class D>
inline bool writes(Stream *s,A& a,B& b,C& c,D& d)
{ s->write(a); s->write(b); s->write(c); return s->write(d); }
template<class A,class B,class C>
inline bool writes(Stream *s,A& a,B& b,C& c)
{ s->write(a); s->write(b); return s->write(c); }
template<class A,class B>
inline bool writes(Stream *s,A& a,B& b)
{ s->write(a); return s->write(b); }
template<class A>
inline bool writes(Stream *s,A& a)
{ return s->write(a); }
}
#endif

View file

@ -0,0 +1,233 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/stream/memStream.h"
MemStream::MemStream( U32 growSize,
bool allowRead,
bool allowWrite )
: mGrowSize( growSize ),
mBufferSize( 0 ),
mStreamSize( 0 ),
mBufferBase( NULL ),
mInstCaps( 0 ),
mCurrentPosition( 0 )
{
AssertFatal( allowRead || allowWrite, "Either write or read must be allowed" );
if ( allowRead )
mInstCaps |= Stream::StreamRead;
if ( allowWrite )
mInstCaps |= Stream::StreamWrite;
mOwnsMemory = true;
setStatus( mGrowSize > 0 ? Ok : EOS );
}
MemStream::MemStream( U32 bufferSize,
void *buffer,
bool allowRead,
bool allowWrite)
: mGrowSize( 0 ),
mBufferSize( bufferSize ),
mStreamSize( bufferSize ),
mBufferBase( buffer ),
mInstCaps( 0 ),
mCurrentPosition( 0 ),
mOwnsMemory( false )
{
AssertFatal( bufferSize > 0, "Invalid buffer size");
AssertFatal( allowRead || allowWrite, "Either write or read must be allowed");
if ( allowRead )
mInstCaps |= Stream::StreamRead;
if ( allowWrite )
mInstCaps |= Stream::StreamWrite;
if ( !buffer )
{
mOwnsMemory = true;
mBufferBase = dMalloc( mBufferSize );
}
setStatus( Ok );
}
MemStream::~MemStream()
{
if ( mOwnsMemory )
dFree( mBufferBase );
mBufferBase = NULL;
mCurrentPosition = 0;
setStatus( Closed );
}
U32 MemStream::getStreamSize()
{
AssertFatal( getStatus() != Closed, "Stream not open, size undefined" );
return mStreamSize;
}
bool MemStream::hasCapability(const Capability in_cap) const
{
// Closed streams can't do anything
//
if (getStatus() == Closed)
return false;
U32 totalCaps = U32(Stream::StreamPosition) | mInstCaps;
return (U32(in_cap) & totalCaps) != 0;
}
U32 MemStream::getPosition() const
{
AssertFatal(getStatus() != Closed, "Position of a closed stream is undefined");
return mCurrentPosition;
}
bool MemStream::setPosition(const U32 in_newPosition)
{
AssertFatal(getStatus() != Closed, "SetPosition of a closed stream is not allowed");
AssertFatal(in_newPosition <= mStreamSize, "Invalid position");
mCurrentPosition = in_newPosition;
if (mCurrentPosition > mStreamSize) {
// Never gets here in debug version, this is for the release builds...
//
setStatus(UnknownError);
return false;
} else if (mCurrentPosition == mStreamSize) {
setStatus(EOS);
} else {
setStatus(Ok);
}
return true;
}
bool MemStream::_read(const U32 in_numBytes, void *out_pBuffer)
{
AssertFatal(getStatus() != Closed, "Attempted read from a closed stream");
if (in_numBytes == 0)
return true;
AssertFatal(out_pBuffer != NULL, "Invalid output buffer");
if (hasCapability(StreamRead) == false) {
AssertWarn(false, "Reading is disallowed on this stream");
setStatus(IllegalCall);
return false;
}
bool success = true;
U32 actualBytes = in_numBytes;
if ((mCurrentPosition + in_numBytes) > mStreamSize) {
success = false;
actualBytes = mStreamSize - mCurrentPosition;
}
// Obtain a current pointer, and do the copy
const void* pCurrent = (const void*)((const U8*)mBufferBase + mCurrentPosition);
dMemcpy(out_pBuffer, pCurrent, actualBytes);
// Advance the stream position
mCurrentPosition += actualBytes;
if (!success)
setStatus(EOS);
else
setStatus(Ok);
return success;
}
bool MemStream::_write(const U32 in_numBytes, const void *in_pBuffer)
{
AssertFatal(getStatus() != Closed, "Attempted write to a closed stream");
if (in_numBytes == 0)
return true;
if (hasCapability(StreamWrite) == false)
{
AssertWarn(0, "Writing is disallowed on this stream");
setStatus(IllegalCall);
return false;
}
bool success = true;
U32 actualBytes = in_numBytes;
if ((mCurrentPosition + in_numBytes) > mBufferSize)
{
if ( mGrowSize > 0 )
{
U32 growSize = (mCurrentPosition + in_numBytes) - mBufferSize;
mBufferSize += growSize + ( mGrowSize - ( growSize % mGrowSize ) );
mBufferBase = dRealloc( mBufferBase, mBufferSize );
}
else
{
success = false;
actualBytes = mBufferSize - mCurrentPosition;
}
}
AssertFatal(in_pBuffer != NULL, "Invalid input buffer");
// Obtain a current pointer, and do the copy
void* pCurrent = (void*)((U8*)mBufferBase + mCurrentPosition);
dMemcpy(pCurrent, in_pBuffer, actualBytes);
// Advance the stream position
mCurrentPosition += actualBytes;
if (mCurrentPosition > mStreamSize)
mStreamSize = mCurrentPosition;
if (mCurrentPosition == mStreamSize)
setStatus(EOS);
else
setStatus(Ok);
return success;
}
void *MemStream::takeBuffer()
{
void *buffer = mBufferBase;
mBufferBase = NULL;
mBufferSize = 0;
mStreamSize = 0;
mCurrentPosition = 0;
setStatus(EOS);
return buffer;
}

View file

@ -0,0 +1,104 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _MEMSTREAM_H_
#define _MEMSTREAM_H_
#ifndef _STREAM_H_
#include "core/stream/stream.h"
#endif
/// The MemStream class is used to read and write to a memory buffer.
class MemStream : public Stream
{
typedef Stream Parent;
protected:
/// The amount to grow the buffer when we run out of
/// space writing to the stream.
U32 mGrowSize;
/// The actual size of the memory buffer. It is always
/// greater than or equal to the mStreamSize.
U32 mBufferSize;
/// The size of the data in the stream. It is always
/// less than or equal to the mBufferSize.
U32 mStreamSize;
/// The memory buffer.
void *mBufferBase;
/// If true the memory is owned by the steam and it
/// will be deleted in the destructor.
bool mOwnsMemory;
///
U32 mInstCaps;
/// Our current read/write position within the buffer.
U32 mCurrentPosition;
public:
/// This constructs an empty memory stream that will grow
/// in increments as needed.
MemStream( U32 growSize,
bool allowRead = true,
bool allowWrite = true );
/// This constructs the stream with a fixed size memory buffer. If
/// buffer is null then it will be allocated for you.
MemStream( U32 bufferSize,
void *buffer,
bool allowRead = true,
bool allowWrite = true );
/// The destructor.
virtual ~MemStream();
protected:
// Stream
bool _read( const U32 in_numBytes, void *out_pBuffer );
bool _write( const U32 in_numBytes, const void *in_pBuffer );
public:
// Stream
bool hasCapability( const Capability caps ) const;
U32 getPosition() const;
bool setPosition( const U32 in_newPosition );
U32 getStreamSize();
/// Returns the memory buffer.
void *getBuffer() { return mBufferBase; }
const void *getBuffer() const { return mBufferBase; }
/// Takes the memory buffer reseting the stream.
void *takeBuffer();
};
#endif //_MEMSTREAM_H_

View file

@ -0,0 +1,378 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/color.h"
#include "core/util/rawData.h"
#include "core/frameAllocator.h"
#include "platform/platformNet.h"
#include "core/stream/stream.h"
#include "core/stringTable.h"
#include "core/strings/stringFunctions.h"
#include "core/util/byteBuffer.h"
#include "core/util/endian.h"
#include "core/util/str.h"
#define IMPLEMENT_OVERLOADED_READ(type) \
bool Stream::read(type* out_read) \
{ \
return read(sizeof(type), out_read); \
}
#define IMPLEMENT_OVERLOADED_WRITE(type) \
bool Stream::write(type in_write) \
{ \
return write(sizeof(type), &in_write); \
}
#define IMPLEMENT_ENDIAN_OVERLOADED_READ(type) \
bool Stream::read(type* out_read) \
{ \
type temp; \
bool success = read(sizeof(type), &temp); \
*out_read = convertLEndianToHost(temp); \
return success; \
}
#define IMPLEMENT_ENDIAN_OVERLOADED_WRITE(type) \
bool Stream::write(type in_write) \
{ \
type temp = convertHostToLEndian(in_write); \
return write(sizeof(type), &temp); \
}
IMPLEMENT_OVERLOADED_WRITE(S8)
IMPLEMENT_OVERLOADED_WRITE(U8)
IMPLEMENT_ENDIAN_OVERLOADED_WRITE(S16)
IMPLEMENT_ENDIAN_OVERLOADED_WRITE(S32)
IMPLEMENT_ENDIAN_OVERLOADED_WRITE(U16)
IMPLEMENT_ENDIAN_OVERLOADED_WRITE(U32)
IMPLEMENT_ENDIAN_OVERLOADED_WRITE(U64)
IMPLEMENT_ENDIAN_OVERLOADED_WRITE(F32)
IMPLEMENT_ENDIAN_OVERLOADED_WRITE(F64)
IMPLEMENT_OVERLOADED_READ(S8)
IMPLEMENT_OVERLOADED_READ(U8)
IMPLEMENT_ENDIAN_OVERLOADED_READ(S16)
IMPLEMENT_ENDIAN_OVERLOADED_READ(S32)
IMPLEMENT_ENDIAN_OVERLOADED_READ(U16)
IMPLEMENT_ENDIAN_OVERLOADED_READ(U32)
IMPLEMENT_ENDIAN_OVERLOADED_READ(U64)
IMPLEMENT_ENDIAN_OVERLOADED_READ(F32)
IMPLEMENT_ENDIAN_OVERLOADED_READ(F64)
Stream::Stream()
: m_streamStatus(Closed)
{
}
const char* Stream::getStatusString(const Status in_status)
{
switch (in_status) {
case Ok:
return "StreamOk";
case IOError:
return "StreamIOError";
case EOS:
return "StreamEOS";
case IllegalCall:
return "StreamIllegalCall";
case Closed:
return "StreamClosed";
case UnknownError:
return "StreamUnknownError";
default:
return "Invalid Stream::Status";
}
}
void Stream::writeString(const char *string, S32 maxLen)
{
S32 len = string ? dStrlen(string) : 0;
if(len > maxLen)
len = maxLen;
write(U8(len));
if(len)
write(len, string);
}
void Stream::readString(char buf[256])
{
U8 len;
read(&len);
read(S32(len), buf);
buf[len] = 0;
}
const char *Stream::readSTString(bool casesens)
{
char buf[256];
readString(buf);
return StringTable->insert(buf, casesens);
}
void Stream::readLongString(U32 maxStringLen, char *stringBuf)
{
U32 len;
read(&len);
if(len > maxStringLen)
{
m_streamStatus = IOError;
return;
}
read(len, stringBuf);
stringBuf[len] = 0;
}
void Stream::writeLongString(U32 maxStringLen, const char *string)
{
U32 len = dStrlen(string);
if(len > maxStringLen)
len = maxStringLen;
write(len);
write(len, string);
}
void Stream::readLine(U8 *buffer, U32 bufferSize)
{
bufferSize--; // account for NULL terminator
U8 *buff = buffer;
U8 *buffEnd = buff + bufferSize;
*buff = '\r';
// strip off preceding white space
while ( *buff == '\r' )
{
if ( !read(buff) || *buff == '\n' )
{
*buff = 0;
return;
}
}
// read line
while ( buff != buffEnd && read(++buff) && *buff != '\n' )
{
if ( *buff == '\r' )
{
#if defined(TORQUE_OS_MAC)
U32 pushPos = getPosition(); // in case we need to back up.
if (read(buff)) // feeling free to overwrite the \r as the NULL below will overwrite again...
if (*buff != '\n') // then push our position back.
setPosition(pushPos);
break; // we're always done after seeing the CR...
#else
buff--; // 'erases' the CR of a CRLF
#endif
}
}
*buff = 0;
}
void Stream::writeText(const char *text)
{
if (text && text[0])
write(dStrlen(text), text);
}
void Stream::writeLine(const U8 *buffer)
{
write(dStrlen((const char *)buffer), buffer);
write(2, "\r\n");
}
void Stream::_write(const String & str)
{
U32 len = str.length();
if (len<255)
write(U8(len));
else
{
// longer string, write full length
write(U8(255));
// fail if longer than 16 bits (will truncate string modulo 2^16)
AssertFatal(len < (1<<16),"String too long");
len &= (1<<16)-1;
write(U16(len));
}
write(len,str.c_str());
}
void Stream::_read(String * str)
{
U16 len;
U8 len8;
read(&len8);
if (len8==255)
read(&len);
else
len = len8;
char * buffer = (char*)FrameAllocator::alloc(len);
read(len, buffer);
*str = String(buffer,len);
}
bool Stream::write(const ColorI& rColor)
{
bool success = write(rColor.red);
success |= write(rColor.green);
success |= write(rColor.blue);
success |= write(rColor.alpha);
return success;
}
bool Stream::write(const ColorF& rColor)
{
ColorI temp = rColor;
return write(temp);
}
bool Stream::read(ColorI* pColor)
{
bool success = read(&pColor->red);
success |= read(&pColor->green);
success |= read(&pColor->blue);
success |= read(&pColor->alpha);
return success;
}
bool Stream::read(ColorF* pColor)
{
ColorI temp;
bool success = read(&temp);
*pColor = temp;
return success;
}
bool Stream::write(const NetAddress &na)
{
bool success = write(na.type);
success &= write(na.port);
success &= write(na.netNum[0]);
success &= write(na.netNum[1]);
success &= write(na.netNum[2]);
success &= write(na.netNum[3]);
success &= write(na.nodeNum[0]);
success &= write(na.nodeNum[1]);
success &= write(na.nodeNum[2]);
success &= write(na.nodeNum[3]);
success &= write(na.nodeNum[4]);
success &= write(na.nodeNum[5]);
return success;
}
bool Stream::read(NetAddress *na)
{
bool success = read(&na->type);
success &= read(&na->port);
success &= read(&na->netNum[0]);
success &= read(&na->netNum[1]);
success &= read(&na->netNum[2]);
success &= read(&na->netNum[3]);
success &= read(&na->nodeNum[0]);
success &= read(&na->nodeNum[1]);
success &= read(&na->nodeNum[2]);
success &= read(&na->nodeNum[3]);
success &= read(&na->nodeNum[4]);
success &= read(&na->nodeNum[5]);
return success;
}
bool Stream::write(const RawData &rd)
{
bool s = write(rd.size);
s &= write(rd.size, rd.data);
return s;
}
bool Stream::read(RawData *rd)
{
U32 size = 0;
bool s = read(&size);
rd->alloc(size);
s &= read(rd->size, rd->data);
return s;
}
bool Stream::write(const Torque::ByteBuffer &rd)
{
bool s = write(rd.getBufferSize());
s &= write(rd.getBufferSize(), rd.getBuffer());
return s;
}
bool Stream::read(Torque::ByteBuffer *rd)
{
U32 size = 0;
bool s = read(&size);
rd->resize(size);
s &= read(rd->getBufferSize(), rd->getBuffer());
return s;
}
bool Stream::copyFrom(Stream *other)
{
U8 buffer[1024];
U32 numBytes = other->getStreamSize() - other->getPosition();
while((other->getStatus() != Stream::EOS) && numBytes > 0)
{
U32 numRead = numBytes > sizeof(buffer) ? sizeof(buffer) : numBytes;
if(! other->read(numRead, buffer))
return false;
if(! write(numRead, buffer))
return false;
numBytes -= numRead;
}
return true;
}
Stream* Stream::clone() const
{
return NULL;
}

View file

@ -0,0 +1,242 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _STREAM_H_
#define _STREAM_H_
#ifndef _TORQUE_TYPES_H_
#include "platform/types.h"
#endif
#ifndef _ENDIAN_H_
#include "core/util/endian.h"
#endif
/// @defgroup stream_overload Primitive Type Stream Operation Overloads
/// These macros declare the read and write functions for all primitive types.
/// @{
#define DECLARE_OVERLOADED_READ(type) bool read(type* out_read);
#define DECLARE_OVERLOADED_WRITE(type) bool write(type in_write);
/// @}
class ColorI;
class ColorF;
struct NetAddress;
class RawData;
class String;
namespace Torque {
class ByteBuffer;
}
//------------------------------------------------------------------------------
//-------------------------------------- Base Stream class
//
/// Base stream class for streaming data across a specific media
class Stream
{
// Public structs and enumerations...
public:
/// Status constants for the stream
enum Status {
Ok = 0, ///< Ok!
IOError, ///< Read or Write error
EOS, ///< End of Stream reached (mostly for reads)
IllegalCall, ///< An unsupported operation used. Always w/ accompanied by AssertWarn
Closed, ///< Tried to operate on a closed stream (or detached filter)
UnknownError ///< Catchall
};
enum Capability {
StreamWrite = BIT(0), ///< Can this stream write?
StreamRead = BIT(1), ///< Can this stream read?
StreamPosition = BIT(2) ///< Can this stream position?
};
// Accessible only through inline accessors
private:
Status m_streamStatus;
// Derived accessible data modifiers...
protected:
void setStatus(const Status in_newStatus) { m_streamStatus = in_newStatus; }
public:
Stream();
virtual ~Stream() {}
/// Gets the status of the stream
Stream::Status getStatus() const { return m_streamStatus; }
/// Gets a printable string form of the status
static const char* getStatusString(const Status in_status);
// Derived classes must override these...
protected:
virtual bool _read(const U32 in_numBytes, void* out_pBuffer) = 0;
virtual bool _write(const U32 in_numBytes, const void* in_pBuffer) = 0;
virtual void _write(const String & str);
virtual void _read(String * str);
public:
/// Checks to see if this stream has the capability of a given function
virtual bool hasCapability(const Capability caps) const = 0;
/// Gets the position in the stream
virtual U32 getPosition() const = 0;
/// Sets the position of the stream. Returns if the new position is valid or not
virtual bool setPosition(const U32 in_newPosition) = 0;
/// Gets the size of the stream
virtual U32 getStreamSize() = 0;
/// Reads a line from the stream.
/// @param buffer buffer to be read into
/// @param bufferSize max size of the buffer. Will not read more than the "bufferSize"
void readLine(U8 *buffer, U32 bufferSize);
/// writes a line to the stream
void writeLine(const U8 *buffer);
/// Reads a string and inserts it into the StringTable
/// @see StringTable
const char *readSTString(bool casesens = false);
/// Reads a string of maximum 255 characters long
virtual void readString(char stringBuf[256]);
/// Reads a string that could potentially be more than 255 characters long.
/// @param maxStringLen Maximum length to read. If the string is longer than maxStringLen, only maxStringLen bytes will be read.
/// @param stringBuf buffer where data is read into
void readLongString(U32 maxStringLen, char *stringBuf);
/// Writes a string to the stream. This function is slightly unstable.
/// Only use this if you have a valid string that is not empty.
/// writeString is safer.
void writeLongString(U32 maxStringLen, const char *string);
/// Write raw text to the stream
void writeText(const char *text);
/// Writes a string to the stream.
virtual void writeString(const char *stringBuf, S32 maxLen=255);
// read/write real strings
void write(const String & str) { _write(str); }
void read(String * str) { _read(str); }
/// Write an integral color to the stream.
bool write(const ColorI&);
/// Write a floating point color to the stream.
bool write(const ColorF&);
/// Read an integral color from the stream.
bool read(ColorI*);
/// Read a floating point color from the stream.
bool read(ColorF*);
/// Write a network address to the stream.
bool write(const NetAddress &);
/// Read a network address from the stream.
bool read(NetAddress*);
/// Write some raw data onto the stream.
bool write(const RawData &);
/// Read some raw data from the stream.
bool read(RawData *);
/// Write some raw data onto the stream.
bool write(const Torque::ByteBuffer &);
/// Read some raw data from the stream.
bool read(Torque::ByteBuffer *);
// Overloaded write and read ops..
public:
bool read(const U32 in_numBytes, void* out_pBuffer) {
return _read(in_numBytes, out_pBuffer);
}
bool write(const U32 in_numBytes, const void* in_pBuffer) {
return _write(in_numBytes, in_pBuffer);
}
DECLARE_OVERLOADED_WRITE(S8)
DECLARE_OVERLOADED_WRITE(U8)
DECLARE_OVERLOADED_WRITE(S16)
DECLARE_OVERLOADED_WRITE(S32)
DECLARE_OVERLOADED_WRITE(U16)
DECLARE_OVERLOADED_WRITE(U32)
DECLARE_OVERLOADED_WRITE(U64)
DECLARE_OVERLOADED_WRITE(F32)
DECLARE_OVERLOADED_WRITE(F64)
DECLARE_OVERLOADED_READ(S8)
DECLARE_OVERLOADED_READ(U8)
DECLARE_OVERLOADED_READ(S16)
DECLARE_OVERLOADED_READ(S32)
DECLARE_OVERLOADED_READ(U16)
DECLARE_OVERLOADED_READ(U32)
DECLARE_OVERLOADED_READ(U64)
DECLARE_OVERLOADED_READ(F32)
DECLARE_OVERLOADED_READ(F64)
// We have to do the bool's by hand, since they are different sizes
// on different compilers...
//
bool read(bool* out_pRead) {
U8 translate;
bool success = read(&translate);
if (success == false)
return false;
*out_pRead = translate != 0;
return true;
}
bool write(const bool& in_rWrite) {
U8 translate = in_rWrite ? U8(1) : U8(0);
return write(translate);
}
/// Copy the contents of another stream into this one
bool copyFrom(Stream *other);
/// Write a number of tabs to this stream
void writeTabs(U32 count)
{
char tab[] = " ";
while(count--)
write(3, (void*)tab);
}
/// Create an exact replica of this stream.
/// Return NULL if the cloning fails.
virtual Stream* clone() const;
};
// This interface is used to provide the amount of bytes actually written/read when using the stream as a
// file. The Stream interface does not provide this information. This is a lame workaround.
class IStreamByteCount
{
public:
virtual ~IStreamByteCount() {}
virtual U32 getLastBytesRead() = 0;
virtual U32 getLastBytesWritten() = 0;
};
#endif //_STREAM_H_

View file

@ -0,0 +1,476 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/stream/streamObject.h"
#include "console/engineAPI.h"
//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
// This entire class has some major issues. Lots of bad characters when reading and
// writing. Stream doesn't seem to jump correctly. All in all this either needs
// to be deprecated, fixed, or someone explain to me what I'm doing wrong.
// Hard to document when it's not working. -Mich 7/16/2010
StreamObject::StreamObject()
{
mStream = NULL;
}
StreamObject::StreamObject(Stream *stream)
{
mStream = stream;
}
StreamObject::~StreamObject()
{
}
IMPLEMENT_CONOBJECT(StreamObject);
ConsoleDocClass( StreamObject,
"@brief Base class for working with streams.\n\n"
"You do not instantiate a StreamObject directly. Instead, it is used as part of a FileStreamObject and ZipObject to "
"support working with uncompressed and compressed files respectively."
"@tsexample\n"
"// You cannot actually declare a StreamObject\n"
"// Instead, use the derived class \"FileStreamObject\"\n"
"%fsObject = FileStreamObject();\n"
"@endtsexample\n\n"
"@see FileStreamObject for the derived class usable in script.\n"
"@see ZipObject where StreamObject is used to read and write to files within a zip archive.\n"
"@ingroup FileSystem\n\n"
);
//-----------------------------------------------------------------------------
bool StreamObject::onAdd()
{
if(mStream == NULL)
{
Con::errorf("StreamObject::onAdd - StreamObject can not be instantiated from script.");
return false;
}
return Parent::onAdd();
}
//-----------------------------------------------------------------------------
// Public Methods
//-----------------------------------------------------------------------------
const char * StreamObject::getStatus()
{
if(mStream == NULL)
return "";
switch(mStream->getStatus())
{
case Stream::Ok:
return "Ok";
case Stream::IOError:
return "IOError";
case Stream::EOS:
return "EOS";
case Stream::IllegalCall:
return "IllegalCall";
case Stream::Closed:
return "Closed";
case Stream::UnknownError:
return "UnknownError";
default:
return "Invalid";
}
}
//-----------------------------------------------------------------------------
const char * StreamObject::readLine()
{
if(mStream == NULL)
return NULL;
char *buffer = Con::getReturnBuffer(256);
mStream->readLine((U8 *)buffer, 256);
return buffer;
}
const char * StreamObject::readString()
{
if(mStream == NULL)
return NULL;
char *buffer = Con::getReturnBuffer(256);
mStream->readString(buffer);
return buffer;
}
const char *StreamObject::readLongString(U32 maxStringLen)
{
if(mStream == NULL)
return NULL;
char *buffer = Con::getReturnBuffer(maxStringLen + 1);
mStream->readLongString(maxStringLen, buffer);
return buffer;
}
//-----------------------------------------------------------------------------
// Console Methods
//-----------------------------------------------------------------------------
DefineEngineMethod( StreamObject, getStatus, const char*, (),,
"@brief Gets a printable string form of the stream's status\n\n"
"@tsexample\n"
"// Create a file stream object for reading\n"
"%fsObject = new FileStreamObject();\n\n"
"// Open a file for reading\n"
"%fsObject.open(\"./test.txt\", \"read\");\n\n"
"// Get the status and print it\n"
"%status = %fsObject.getStatus();\n"
"echo(%status);\n\n"
"// Always remember to close a file stream when finished\n"
"%fsObject.close();\n"
"@endtsexample\n\n"
"@return String containing status constant, one of the following:\n\n"
" OK - Stream is active and no file errors\n\n"
" IOError - Something went wrong during read or writing the stream\n\n"
" EOS - End of Stream reached (mostly for reads)\n\n"
" IllegalCall - An unsupported operation used. Always w/ accompanied by AssertWarn\n\n"
" Closed - Tried to operate on a closed stream (or detached filter)\n\n"
" UnknownError - Catch all for an error of some kind\n\n"
" Invalid - Entire stream is invalid\n\n")
{
return object->getStatus();
}
DefineEngineMethod( StreamObject, isEOS, bool, (),,
"@brief Tests if the stream has reached the end of the file\n\n"
"This is an alternative name for isEOF. Both functions are interchangeable. This simply exists "
"for those familiar with some C++ file I/O standards.\n\n"
"@tsexample\n"
"// Create a file stream object for reading\n"
"%fsObject = new FileStreamObject();\n\n"
"// Open a file for reading\n"
"%fsObject.open(\"./test.txt\", \"read\");\n\n"
"// Keep reading until we reach the end of the file\n"
"while( !%fsObject.isEOS() )\n"
"{\n"
" %line = %fsObject.readLine();\n"
" echo(%line);\n"
"}\n"
"// Made it to the end\n"
"echo(\"Finished reading file\");\n\n"
"// Always remember to close a file stream when finished\n"
"%fsObject.close();\n"
"@endtsexample\n\n"
"@return True if the parser has reached the end of the file, false otherwise\n"
"@see isEOF()")
{
return object->isEOS();
}
DefineEngineMethod( StreamObject, isEOF, bool, (),,
"@brief Tests if the stream has reached the end of the file\n\n"
"This is an alternative name for isEOS. Both functions are interchangeable. This simply exists "
"for those familiar with some C++ file I/O standards.\n\n"
"@tsexample\n"
"// Create a file stream object for reading\n"
"%fsObject = new FileStreamObject();\n\n"
"// Open a file for reading\n"
"%fsObject.open(\"./test.txt\", \"read\");\n\n"
"// Keep reading until we reach the end of the file\n"
"while( !%fsObject.isEOF() )\n"
"{\n"
" %line = %fsObject.readLine();\n"
" echo(%line);\n"
"}\n"
"// Made it to the end\n"
"echo(\"Finished reading file\");\n\n"
"// Always remember to close a file stream when finished\n"
"%fsObject.close();\n"
"@endtsexample\n\n"
"@return True if the parser has reached the end of the file, false otherwise\n"
"@see isEOS()")
{
return object->isEOS();
}
//-----------------------------------------------------------------------------
DefineEngineMethod( StreamObject, getPosition, S32, (),,
"@brief Gets the position in the stream\n\n"
"The easiest way to visualize this is to think of a cursor in a text file. If you have moved the cursor by "
"five characters, the current position is 5. If you move ahead 10 more characters, the position is now 15. "
"For StreamObject, when you read in the line the position is increased by the number of characters parsed, "
"the null terminator, and a newline.\n\n"
"@tsexample\n"
"// Create a file stream object for reading\n"
"%fsObject = new FileStreamObject();\n\n"
"// Open a file for reading\n"
"// This file contains two lines of text repeated:\n"
"// Hello World\n"
"// Hello World\n"
"%fsObject.open(\"./test.txt\", \"read\");\n\n"
"// Read in the first line\n"
"%line = %fsObject.readLine();\n\n"
"// Get the position of the stream\n"
"%position = %fsObject.getPosition();\n\n"
"// Print the current position\n"
"// Should be 13, 10 for the words, 1 for the space, 1 for the null terminator, and 1 for the newline\n"
"echo(%position);\n\n"
"// Always remember to close a file stream when finished\n"
"%fsObject.close();\n"
"@endtsexample\n\n"
"@return Number of bytes which stream has parsed so far, null terminators and newlines are included\n"
"@see setPosition()")
{
return object->getPosition();
}
DefineEngineMethod( StreamObject, setPosition, bool, (S32 newPosition),,
"@brief Gets the position in the stream\n\n"
"The easiest way to visualize this is to think of a cursor in a text file. If you have moved the cursor by "
"five characters, the current position is 5. If you move ahead 10 more characters, the position is now 15. "
"For StreamObject, when you read in the line the position is increased by the number of characters parsed, "
"the null terminator, and a newline. Using setPosition allows you to skip to specific points of the file.\n\n"
"@tsexample\n"
"// Create a file stream object for reading\n"
"%fsObject = new FileStreamObject();\n\n"
"// Open a file for reading\n"
"// This file contains the following two lines:\n"
"// 11111111111\n"
"// Hello World\n"
"%fsObject.open(\"./test.txt\", \"read\");\n\n"
"// Skip ahead by 12, which will bypass the first line entirely\n"
"%fsObject.setPosition(12);\n\n"
"// Read in the next line\n"
"%line = %fsObject.readLine();\n\n"
"// Print the line just read in, should be \"Hello World\"\n"
"echo(%line);\n\n"
"// Always remember to close a file stream when finished\n"
"%fsObject.close();\n"
"@endtsexample\n\n"
"@return Number of bytes which stream has parsed so far, null terminators and newlines are included\n"
"@see getPosition()")
{
return object->setPosition(newPosition);
}
DefineEngineMethod( StreamObject, getStreamSize, S32, (),,
"@brief Gets the size of the stream\n\n"
"The size is dependent on the type of stream being used. If it is a file stream, returned value will "
"be the size of the file. If it is a memory stream, it will be the size of the allocated buffer.\n\n"
"@tsexample\n"
"// Create a file stream object for reading\n"
"%fsObject = new FileStreamObject();\n\n"
"// Open a file for reading\n"
"// This file contains the following two lines:\n"
"// HelloWorld\n"
"// HelloWorld\n"
"%fsObject.open(\"./test.txt\", \"read\");\n\n"
"// Found out how large the file stream is\n"
"// Then print it to the console\n"
"// Should be 22\n"
"%streamSize = %fsObject.getStreamSize();\n"
"echo(%streamSize);\n\n"
"// Always remember to close a file stream when finished\n"
"%fsObject.close();\n"
"@endtsexample\n\n"
"@return Size of stream, in bytes\n")
{
return object->getStreamSize();
}
//-----------------------------------------------------------------------------
DefineEngineMethod( StreamObject, readLine, const char*, (),,
"@brief Read a line from the stream.\n\n"
"Emphasis on *line*, as in you cannot parse individual characters or chunks of data. "
"There is no limitation as to what kind of data you can read.\n\n"
"@tsexample\n"
"// Create a file stream object for reading\n"
"// This file contains the following two lines:\n"
"// HelloWorld\n"
"// HelloWorld\n"
"%fsObject = new FileStreamObject();\n\n"
"%fsObject.open(\"./test.txt\", \"read\");\n\n"
"// Read in the first line\n"
"%line = %fsObject.readLine();\n\n"
"// Print the line we just read\n"
"echo(%line);\n\n"
"// Always remember to close a file stream when finished\n"
"%fsObject.close();\n"
"@endtsexample\n\n"
"@return String containing the line of data that was just read\n"
"@see writeLine()")
{
const char *str = object->readLine();
return str ? str : "";
}
DefineEngineMethod( StreamObject, writeLine, void, ( const char* line ),,
"@brief Write a line to the stream, if it was opened for writing.\n\n"
"There is no limit as to what kind of data you can write. Any format and data is allowable, not just text. "
"Be careful of what you write, as whitespace, current values, and literals will be preserved.\n\n"
"@param line The data we are writing out to file."
"@tsexample\n"
"// Create a file stream\n"
"%fsObject = new FileStreamObject();\n\n"
"// Open the file for writing\n"
"// If it does not exist, it is created. If it does exist, the file is cleared\n"
"%fsObject.open(\"./test.txt\", \"write\");\n\n"
"// Write a line to the file\n"
"%fsObject.writeLine(\"Hello World\");\n\n"
"// Write another line to the file\n"
"%fsObject.writeLine(\"Documentation Rocks!\");\n\n"
"// Always remember to close a file stream when finished\n"
"%fsObject.close();\n"
"@endtsexample\n\n"
"@see readLine()")
{
object->writeLine((U8 *)line);
}
//-----------------------------------------------------------------------------
/* readSTString, readString, and readLongString need to be fixed or deprecated */
DefineEngineMethod(StreamObject, readSTString, String, ( bool caseSensitive ), ( false ),
"@brief Read in a string and place it on the string table.\n\n"
"@param caseSensitive If false then case will not be taken into account when attempting "
"to match the read in string with what is already in the string table.\n"
"@return The string that was read from the stream.\n"
"@see writeString()"
"@note When working with these particular string reading and writing methods, the stream "
"begins with the length of the string followed by the string itself, and does not include "
"a NULL terminator.")
{
const char *str = object->readSTString(caseSensitive);
return str ? str : "";
}
DefineEngineMethod(StreamObject, readString, String, (),,
"@brief Read a string up to a maximum of 256 characters"
"@return The string that was read from the stream.\n"
"@see writeString()"
"@note When working with these particular string reading and writing methods, the stream "
"begins with the length of the string followed by the string itself, and does not include "
"a NULL terminator.")
{
const char *str = object->readString();
return str ? str : "";
}
DefineEngineMethod(StreamObject, readLongString, String, ( S32 maxLength ),,
"@brief Read in a string up to the given maximum number of characters.\n\n"
"@param maxLength The maximum number of characters to read in.\n"
"@return The string that was read from the stream.\n"
"@see writeLongString()"
"@note When working with these particular string reading and writing methods, the stream "
"begins with the length of the string followed by the string itself, and does not include "
"a NULL terminator.")
{
const char *str = object->readLongString(maxLength);
return str ? str : "";
}
DefineEngineMethod(StreamObject, writeLongString, void, ( S32 maxLength, const char* string ),,
"@brief Write out a string up to the maximum number of characters.\n\n"
"@param maxLength The maximum number of characters that will be written.\n"
"@param string The string to write out to the stream.\n"
"@see readLongString()"
"@note When working with these particular string reading and writing methods, the stream "
"begins with the length of the string followed by the string itself, and does not include "
"a NULL terminator.")
{
object->writeLongString(maxLength, string);
}
DefineEngineMethod(StreamObject, writeString, void, ( const char* string, S32 maxLength ), ( 256 ),
"@brief Write out a string with a default maximum length of 256 characters.\n\n"
"@param string The string to write out to the stream\n"
"@param maxLength The maximum string length to write out with a default of 256 characters. This "
"value should not be larger than 256 as it is written to the stream as a single byte.\n"
"@see readString()"
"@note When working with these particular string reading and writing methods, the stream "
"begins with the length of the string followed by the string itself, and does not include "
"a NULL terminator.")
{
object->writeString(string, maxLength);
}
//-----------------------------------------------------------------------------
DefineEngineMethod(StreamObject, copyFrom, bool, (SimObject* other),,
"@brief Copy from another StreamObject into this StreamObject\n\n"
"@param other The StreamObject to copy from.\n"
"@return True if the copy was successful.\n")
{
StreamObject *so = dynamic_cast<StreamObject *>(other);
if(so == NULL)
return false;
return object->copyFrom(so);
}

View file

@ -0,0 +1,138 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _STREAMOBJECT_H_
#define _STREAMOBJECT_H_
#ifndef _SIMOBJECT_H_
#include "console/simObject.h"
#endif
/// @addtogroup zip_group
// @{
//-----------------------------------------------------------------------------
/// @brief Script wrapper for the Stream class
///
/// It is not possible to instantiate StreamObject in script. Instead,
/// it is instantiated in C++ code and returned to script.
///
/// This was mainly intended to allow the \ref zip_group "zip code" to
/// provide the stream interface to script.
//-----------------------------------------------------------------------------
class StreamObject : public SimObject
{
typedef SimObject Parent;
protected:
Stream *mStream;
public:
StreamObject();
StreamObject(Stream *stream);
virtual ~StreamObject();
DECLARE_CONOBJECT(StreamObject);
virtual bool onAdd();
/// Set the stream to allow reuse of the object
void setStream(Stream *stream) { mStream = stream; }
/// Get the underlying stream. Used with setStream() to support object reuse
Stream *getStream() { return mStream; }
/// Gets a printable string form of the status
const char* getStatus();
bool isEOS() { return mStream ? mStream->getStatus() == Stream::EOS : true; }
/// Gets the position in the stream
U32 getPosition() const
{
return mStream ? mStream->getPosition() : 0;
}
/// Sets the position of the stream. Returns if the new position is valid or not
bool setPosition(const U32 in_newPosition)
{
return mStream ? mStream->setPosition(in_newPosition) : false;
}
/// Gets the size of the stream
U32 getStreamSize()
{
return mStream ? mStream->getStreamSize() : 0;
}
/// Reads a line from the stream.
const char * readLine();
/// Writes a line to the stream
void writeLine(U8 *buffer)
{
if(mStream)
mStream->writeLine(buffer);
}
/// Reads a string and inserts it into the StringTable
/// @see StringTable
const char *readSTString(bool casesens = false)
{
return mStream ? mStream->readSTString(casesens) : NULL;
}
/// Reads a string of maximum 255 characters long
const char *readString();
/// Reads a string that could potentially be more than 255 characters long.
/// @param maxStringLen Maximum length to read. If the string is longer than maxStringLen, only maxStringLen bytes will be read.
/// @param stringBuf buffer where data is read into
const char * readLongString(U32 maxStringLen);
/// Writes a string to the stream. This function is slightly unstable.
/// Only use this if you have a valid string that is not empty.
/// writeString is safer.
void writeLongString(U32 maxStringLen, const char *string)
{
if(mStream)
mStream->writeLongString(maxStringLen, string);
}
/// Writes a string to the stream.
void writeString(const char *stringBuf, S32 maxLen=255)
{
if(mStream)
mStream->writeString(stringBuf, maxLen);
}
/// Copy the contents of another stream into this one
bool copyFrom(StreamObject *other)
{
if(mStream)
return mStream->copyFrom(other->getStream());
return false;
}
};
// @}
#endif // _STREAMOBJECT_H_

View file

@ -0,0 +1,373 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _TSTREAM_H_
#define _TSTREAM_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _TYPETRAITS_H_
#include "platform/typetraits.h"
#endif
//TODO: error handling
/// @file
/// Definitions for lightweight componentized streaming.
///
/// This file is an assembly of lightweight classes/interfaces that
/// describe various aspects of streaming classes. The advantage
/// over using Torque's Stream class is that very little requirements
/// are placed on implementations, that specific abilities can be
/// mixed and matched very selectively, and that complex stream processing
/// chains can be hidden behind very simple stream interfaces.
/// Status of an asynchronous I/O operation.
enum EAsyncIOStatus
{
ASYNC_IO_Pending, ///< I/O is still in queue or being processed.
ASYNC_IO_Complete, ///< I/O has completed successfully.
ASYNC_IO_Error ///< I/O has aborted with an error.
};
//-----------------------------------------------------------------------------
// Several component interfaces.
//-----------------------------------------------------------------------------
/// Interface for streams with an explicit position property.
template< typename P = U32 >
class IPositionable
{
public:
typedef void Parent;
/// The type used to indicate positions.
typedef P PositionType;
/// @return the current position.
virtual PositionType getPosition() const = 0;
/// Set the current position to be "pos".
/// @param pos The new position.
virtual void setPosition( PositionType pos ) = 0;
};
/// Interface for structures that allow their state to be reset.
class IResettable
{
public:
typedef void Parent;
/// Reset to the initial state.
virtual void reset() = 0;
};
/// Interface for structures of finite size.
template< typename T = U32 >
class ISizeable
{
public:
typedef void Parent;
/// The type used to indicate the structure's size.
typedef T SizeType;
/// @return the size of the structure in number of elements.
virtual SizeType getSize() const = 0;
};
/// Interface for structures that represent processes.
class IProcess
{
public:
typedef void Parent;
/// Start the process.
virtual void start() = 0;
/// Stop the process.
virtual void stop() = 0;
/// Pause the process.
virtual void pause() = 0;
};
/// Interface for objects that need continuous explicit updates from
/// an outside source.
class IPolled
{
public:
typedef void Parent;
/// Update the object state.
virtual bool update() = 0;
};
//-----------------------------------------------------------------------------
// IInputStream.
//-----------------------------------------------------------------------------
/// An input stream delivers a sequence of elements of type "T".
///
/// @note This stream has an inherent position property and is thus not
/// safe for concurrent access.
template< typename T >
class IInputStream
{
public:
typedef void Parent;
/// The element type of this input stream.
typedef T ElementType;
/// Read the next "num" elements into "buffer".
///
/// @param buffer The buffer into which the elements are stored.
/// @param num Number of elements to read.
/// @return the number of elements actually read; this may be less than
/// "num" or even zero if no elements are available or reading failed.
virtual U32 read( ElementType* buffer, U32 num ) = 0;
};
/// An input stream over elements of type "T" that reads from
/// user-specified explicit offsets.
template< typename T, typename Offset = U32 >
class IOffsetInputStream
{
public:
typedef void Parent;
typedef Offset OffsetType;
typedef T ElementType;
/// Read the next "num" elements at "offset" into "buffer".
///
/// @param offset The offset in the stream from which to read.
/// @param buffer The buffer into which the elements are stored.
/// @param num Number of elements to read.
/// @return the number of elements actually read; this may be less than
/// "num" or even zero if no elements are available or reading failed.
virtual U32 readAt( OffsetType offset, T* buffer, U32 num ) = 0;
};
/// An input stream over elements of type "T" that works in
/// the background.
template< typename T, typename Offset = U32 >
class IAsyncInputStream
{
public:
typedef void Parent;
typedef Offset OffsetType;
typedef T ElementType;
/// Queue a read of "num" elements at "offset" into "buffer".
///
/// @param offset The offset in the stream from which to read.
/// @param buffer The buffer into which the elements are stored.
/// @param num Number of elements to read.
/// @return a handle for the asynchronous read operation or NULL if the
/// operation could not be queued.
virtual void* issueReadAt( OffsetType offset, T* buffer, U32 num ) = 0;
/// Try moving the given asynchronous read operation to ASYNC_IO_Complete.
///
/// @note This method invalidates the given handle.
///
/// @param handle Handle returned by "issueReadAt".
/// @param outNumRead Reference that receives the number of bytes actually read in the
/// operation.
/// @param wait If true, the method waits until the given operation either fails or
/// completes successfully before returning.
/// @return the final operation status.
virtual EAsyncIOStatus tryCompleteReadAt( void* handle, U32& outNumRead, bool wait = false ) = 0;
/// Cancel the given asynchronous read operation.
///
/// @note This method invalidates the given handle.
///
/// @param handle Handle returned by "issueReadAt".
virtual void cancelReadAt( void* handle ) = 0;
};
//-----------------------------------------------------------------------------
// IOutputStream.
//-----------------------------------------------------------------------------
/// An output stream that writes elements of type "T".
///
/// @note This stream has an inherent position property and is thus not
/// safe for concurrent access.
template< typename T >
class IOutputStream
{
public:
typedef void Parent;
/// The element type of this input stream.
typedef T ElementType;
/// Write "num" elements from "buffer" to the stream at its
/// current position.
///
/// @param buffer The buffer from which to read elements.
/// @param num Number of elements to write.
virtual void write( const ElementType* buffer, U32 num ) = 0;
};
/// An output stream that writes elements of type "T" to a
/// user-specified explicit offset.
template< typename T, typename Offset = U32 >
class IOffsetOutputStream
{
public:
typedef void Parent;
typedef Offset OffsetType;
typedef T ElementType;
/// Write "num" elements from "buffer" to the stream at "offset".
///
/// @param offset The offset in the stream at which to write the elements.
/// @param buffer The buffer from which to read elements.
/// @param num Number of elements to write.
virtual void writeAt( OffsetType offset, const ElementType* buffer, U32 num ) = 0;
};
/// An output stream that writes elements of type "T" in the background.
template< typename T, typename Offset = U32 >
class IAsyncOutputStream
{
public:
typedef void Parent;
typedef Offset OffsetType;
typedef T ElementType;
/// Queue a write operation of "num" elements from "buffer" to stream position
/// "offset".
///
/// @param offset The offset in the stream at which to write the elements.
/// @param buffer The buffer from which to read elements.
/// @param num The number of elements to write.
/// @return a handle to the asynchronous write operatior or NULL if the operation
/// could not be queued.
virtual void* issueWriteAt( OffsetType offset, const ElementType* buffer, U32 num ) = 0;
/// Try moving the given asynchronous write operation to ASYNC_IO_Complete.
///
/// @note This method invalidates the given handle.
///
/// @param handle Handle returned by "issueWriteAt".
/// @param wait If true, the method waits until the given operation either fails or
/// completes successfully before returning.
/// @return the final operation status.
virtual EAsyncIOStatus tryCompleteWriteAt( void* handle, bool wait = false ) = 0;
/// Cancel the given asynchronous write operation.
///
/// @note This method invalidates the given handle.
///
/// @param handle Handle return by "issueWriteAt".
virtual void cancelWriteAt( void* handle ) = 0;
};
//-----------------------------------------------------------------------------
// IInputStreamFilter.
//-----------------------------------------------------------------------------
/// An input stream filter takes an input stream "Stream" and processes it
/// into an input stream over type "To".
template< typename To, typename Stream >
class IInputStreamFilter : public IInputStream< To >
{
public:
typedef IInputStream< To > Parent;
///
typedef typename TypeTraits< Stream >::BaseType SourceStreamType;
/// The element type of the source stream.
typedef typename SourceStreamType::ElementType SourceElementType;
/// Construct a filter reading elements from "stream".
IInputStreamFilter( const Stream& stream )
: mSourceStream( stream ) {}
/// Return the stream from which this filter is reading its
/// source elements.
const Stream& getSourceStream() const { return mSourceStream; }
Stream& getSourceStream() { return mSourceStream; }
private:
Stream mSourceStream;
};
//-----------------------------------------------------------------------------
// IOutputStreamFilter.
//-----------------------------------------------------------------------------
/// An output stream filter takes an output stream "Stream" and processes it
/// into an output stream over type "To".
template< typename To, class Stream >
class IOutputStreamFilter : public IOutputStream< To >
{
public:
typedef IOutputStream< To > Parent;
///
typedef typename TypeTraits< Stream >::BaseType TargetStreamType;
/// The element type of the target stream.
typedef typename TargetStreamType::ElementType TargetElementType;
/// Construct a filter writing elements to "stream".
IOutputStreamFilter( const Stream& stream )
: mTargetStream( stream ) {}
/// Return the stream to which this filter is writing its
/// elements.
const Stream& getTargetStream() const { return mTargetStream; }
Stream& getTargetStream() { return mTargetStream; }
private:
Stream mTargetStream;
};
#endif // _TSTREAM_H_

View file

@ -0,0 +1,471 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/stringBuffer.h"
#include "core/frameAllocator.h"
#include "core/strings/unicode.h"
#include "core/strings/stringFunctions.h"
#if defined(TORQUE_DEBUG)
class StringBufferManager
{
public:
static StringBufferManager& getManager();
Vector<StringBuffer*> strings;
U64 request8;
U64 request16;
StringBufferManager()
{
VECTOR_SET_ASSOCIATION( strings );
}
void add(StringBuffer* s);
void remove(StringBuffer* s);
void updateStats();
void dumpStats();
void dumpAllStrings();
};
ConsoleFunction(sbmDumpStats, void, 1, 1, "")
{
StringBufferManager::getManager().dumpStats();
}
ConsoleFunction(sbmDumpStrings, void, 1, 1, "")
{
StringBufferManager::getManager().dumpAllStrings();
}
#endif // TORQUE_DEBUG
#if defined(TORQUE_DEBUG)
#define SBMAddThisStringBuffer() \
StringBufferManager::getManager().add(this); \
rc = new RequestCounts; \
clearRequestCounts()
#define incRequestCount8() rc->requestCount8++
#define incRequestCount16() rc->requestCount16++
#define decRequestCount16() rc->requestCount16--
#else
#define SBMAddThisStringBuffer()
#define incRequestCount8()
#define incRequestCount16()
#define decRequestCount16()
#endif
StringBuffer::StringBuffer() : mBuffer(), mBuffer8(), mDirty8(true)
{
VECTOR_SET_ASSOCIATION( mBuffer );
SBMAddThisStringBuffer();
mBuffer.push_back(0);
};
/// Copy constructor. Very important.
StringBuffer::StringBuffer(const StringBuffer &copy) : mBuffer(), mBuffer8()
{
VECTOR_SET_ASSOCIATION( mBuffer );
SBMAddThisStringBuffer();
set(&copy);
};
StringBuffer::StringBuffer(const StringBuffer *in)
: mBuffer(), mBuffer8()
{
VECTOR_SET_ASSOCIATION( mBuffer );
SBMAddThisStringBuffer();
set(in);
}
StringBuffer::StringBuffer(const UTF8 *in)
: mBuffer(), mBuffer8()
{
VECTOR_SET_ASSOCIATION( mBuffer );
SBMAddThisStringBuffer();
set(in);
}
StringBuffer::StringBuffer(const UTF16 *in)
: mBuffer(), mBuffer8()
{
VECTOR_SET_ASSOCIATION( mBuffer );
SBMAddThisStringBuffer();
set(in);
}
//-------------------------------------------------------------------------
StringBuffer::~StringBuffer()
{
// Everything will get cleared up nicely cuz it's a vector. Sweet.
#if defined(TORQUE_DEBUG)
StringBufferManager::getManager().remove(this);
delete rc;
#endif
}
//-------------------------------------------------------------------------
void StringBuffer::set(const StringBuffer *in)
{
// Copy the vector.
mBuffer.setSize(in->mBuffer.size());
dMemcpy(mBuffer.address(), in->mBuffer.address(), sizeof(UTF16) * mBuffer.size());
mDirty8 = true;
}
void StringBuffer::set(const UTF8 *in)
{
incRequestCount8();
// Convert and store. Note that a UTF16 version of the string cannot be longer.
FrameTemp<UTF16> tmpBuff(dStrlen(in)+1);
if(!in || in[0] == 0 || !convertUTF8toUTF16(in, tmpBuff, dStrlen(in)+1))
{
// Easy out, it's a blank string, or a bad string.
mBuffer.clear();
mBuffer.push_back(0);
AssertFatal(mBuffer.last() == 0, "StringBuffer::set UTF8 - not a null terminated string!");
mDirty8 = true;
return;
}
// Otherwise, we've a copy to do. (This might not be strictly necessary.)
mBuffer.setSize(dStrlen(tmpBuff)+1);
dMemcpy(mBuffer.address(), tmpBuff, sizeof(UTF16) * mBuffer.size());
mBuffer.compact();
AssertFatal(mBuffer.last() == 0, "StringBuffer::set UTF8 - not a null terminated string!");
mDirty8 = true;
}
void StringBuffer::set(const UTF16 *in)
{
incRequestCount16();
// Just copy, it's already UTF16.
U32 newsize = dStrlen(in);
mBuffer.setSize(newsize + 1);
dMemcpy(mBuffer.address(), in, sizeof(UTF16) * newsize);
mBuffer[newsize] = '\0';
mBuffer.compact();
AssertFatal(mBuffer.last() == 0, "StringBuffer::set UTF16 - not a null terminated string!");
mDirty8 = true;
}
//-------------------------------------------------------------------------
void StringBuffer::append(const StringBuffer &in)
{
append(in.mBuffer.address(), in.length());
}
void StringBuffer::append(const UTF8* in)
{
incRequestCount8();
decRequestCount16(); // because we're about to inc it when we go through append(utf16)
// convert to UTF16, because that's our internal format.
// if the conversion fails, exit.
UTF16* tmp = convertUTF8toUTF16(in);
AssertFatal(tmp, "StringBuffer::append(UTF8) - could not convert UTF8 string!");
if(!tmp)
return;
append(tmp);
delete[] tmp;
}
void StringBuffer::append(const UTF16* in)
{
AssertFatal(in, "StringBuffer::append(UTF16) - null UTF16 string!");
append(in, dStrlen(in));
}
void StringBuffer::append(const UTF16* in, const U32 len)
{
incRequestCount16();
// Stick in onto the end of us - first make space.
U32 oldSize = length();
mBuffer.increment(len);
// Copy it in, ignoring both our terminator and theirs.
dMemcpy(&mBuffer[oldSize], in, sizeof(UTF16) * len);
// Terminate the string.
mBuffer.last() = 0;
// mark utf8 buffer dirty
mDirty8 = true;
}
void StringBuffer::insert(const U32 charOffset, const StringBuffer &in)
{
insert(charOffset, in.mBuffer.address(), in.length());
}
void StringBuffer::insert(const U32 charOffset, const UTF8* in)
{
incRequestCount8();
decRequestCount16();
// convert to UTF16, because that's our internal format.
// if the conversion fails, exit.
UTF16* tmp = convertUTF8toUTF16(in);
AssertFatal(tmp, "StringBuffer::insert(UTF8) - could not convert UTF8 string!");
if(!tmp)
return;
insert(charOffset, tmp);
delete[] tmp;
}
void StringBuffer::insert(const U32 charOffset, const UTF16* in)
{
AssertFatal(in, "StringBuffer::insert(UTF16) - null UTF16 string!");
insert(charOffset, in, dStrlen(in));
}
void StringBuffer::insert(const U32 charOffset, const UTF16* in, const U32 len)
{
incRequestCount16();
// Deal with append case.
if(charOffset >= length())
{
append(in, len);
return;
}
// Append was easy, now we have to do some work.
// Copy everything we have that comes after charOffset past where the new
// string data will be.
// Figure the number of UTF16's to copy, taking into account the possibility
// that we may be inserting a long string into a short string.
// How many chars come after the insert point? Add 1 to deal with terminator.
const U32 copyCharLength = (S32)(length() - charOffset) + 1;
// Make some space in our buffer. We only need in.length() more as we
// will be dropping one of the two terminators present in this operation.
mBuffer.increment(len);
for(S32 i=copyCharLength-1; i>=0; i--)
mBuffer[charOffset+i+len] = mBuffer[charOffset+i];
// Can't copy directly: memcpy behavior is undefined if src and dst overlap.
//dMemcpy(&mBuffer[charOffset+len], &mBuffer[charOffset], sizeof(UTF16) * copyCharLength);
// And finally copy the new data in, not including its terminator.
dMemcpy(&mBuffer[charOffset], in, sizeof(UTF16) * len);
// All done!
AssertFatal(mBuffer.last() == 0, "StringBuffer::insert - not a null terminated string!");
// mark utf8 buffer dirty
mDirty8 = true;
}
StringBuffer StringBuffer::substring(const U32 start, const U32 len) const
{
// Deal with bonehead user input.
if(start >= length() || len == 0)
{
// Either they asked beyond the end of the string or we're null. Either
// way, let's give them a null string.
return StringBuffer("");
}
AssertFatal(start < length(), "StringBuffer::substring - invalid start!");
AssertFatal(start+len <= length(), "StringBuffer::substring - invalid len!");
AssertFatal(len > 0, "StringBuffer::substring - len must be >= 1.");
StringBuffer tmp;
tmp.mBuffer.clear();
for(S32 i=0; i<len; i++)
tmp.mBuffer.push_back(mBuffer[start+i]);
if(tmp.mBuffer.last() != 0) tmp.mBuffer.push_back(0);
// Make sure this shit is terminated; we might get a totally empty string.
if(!tmp.mBuffer.size())
tmp.mBuffer.push_back(0);
return tmp;
}
UTF8* StringBuffer::createSubstring8(const U32 start, const U32 len) const
{
incRequestCount8();
StringBuffer sub = this->substring(start, len);
return sub.createCopy8();
}
void StringBuffer::cut(const U32 start, const U32 len)
{
AssertFatal(start < length(), "StringBuffer::cut - invalid start!");
AssertFatal(start+len <= length(), "StringBuffer::cut - invalid len!");
AssertFatal(len > 0, "StringBuffer::cut - len >= 1.");
AssertFatal(mBuffer.last() == 0, "StringBuffer::cut - not a null terminated string! (pre)");
// Now snip things.
for(S32 i=start; i<mBuffer.size()-len; i++)
mBuffer[i] = mBuffer[i+len];
mBuffer.decrement(len);
mBuffer.compact();
AssertFatal(mBuffer.last() == 0, "StringBuffer::cut - not a null terminated string! (post)");
// mark utf8 buffer dirty
mDirty8 = true;
}
const UTF16 StringBuffer::getChar(const U32 offset) const
{
incRequestCount16();
// Allow them to grab the null terminator if they want.
AssertFatal(offset<mBuffer.size(), "StringBuffer::getChar - outside of range.");
return mBuffer[offset];
}
//-------------------------------------------------------------------------
void StringBuffer::getCopy8(UTF8 *buff, const U32 buffSize) const
{
incRequestCount8();
AssertFatal(mBuffer.last() == 0, "StringBuffer::get UTF8 - not a null terminated string!");
convertUTF16toUTF8(mBuffer.address(), buff, buffSize);
}
void StringBuffer::getCopy(UTF16 *buff, const U32 buffSize) const
{
incRequestCount16();
// Just copy it out.
AssertFatal(mBuffer.last() == 0, "StringBuffer::get UTF8 - not a null terminated string!");
dMemcpy(buff, mBuffer.address(), sizeof(UTF16) * getMin(buffSize, (U32)mBuffer.size()));
// ensure null termination.
buff[buffSize-1] = NULL;
}
UTF8* StringBuffer::createCopy8() const
{
incRequestCount8();
// convert will create a buffer of the appropriate size for a null terminated
// input string.
UTF8* out = convertUTF16toUTF8(mBuffer.address());
return out;
}
const UTF16* StringBuffer::getPtr() const
{
incRequestCount16();
// get a pointer to the StringBuffer's data store.
// this pointer is volatile, and not thread safe.
// use this in situations where you can be sure that the StringBuffer will
// not be modified out from under you.
// the key here is, you avoid yet another data copy. data copy is slow on
// most modern hardware.
return mBuffer.address();
}
const UTF8* StringBuffer::getPtr8() const
{
incRequestCount8();
// get a pointer to the utf8 version of the StringBuffer's data store.
// if the utf8 version is dirty, update it first.
if(mDirty8)
// force const cheating.
const_cast<StringBuffer*>(this)->updateBuffer8();
return mBuffer8.address();
}
void StringBuffer::updateBuffer8()
{
U32 slackLen = getUTF8BufferSizeEstimate();
mBuffer8.setSize(slackLen);
U32 len = convertUTF16toUTF8(mBuffer.address(), mBuffer8.address(), slackLen);
mBuffer8.setSize(len+1);
mBuffer8.compact();
mDirty8 = false;
}
#if defined(TORQUE_DEBUG)
StringBufferManager& StringBufferManager::getManager()
{
static StringBufferManager _sbm; return _sbm;
}
void StringBufferManager::add(StringBuffer* s)
{
strings.push_back(s);
}
void StringBufferManager::remove(StringBuffer* s)
{
U32 nstrings = strings.size() - 1;
for(S32 i = nstrings; i >= 0; i--)
if(strings[i] == s)
strings.erase_fast(i);
}
void StringBufferManager::updateStats()
{
request8 = 0;
request16 = 0;
U32 nstrings = strings.size();
for(int i=0; i < nstrings; i++)
{
request8 += strings[i]->rc->requestCount8;
request16 += strings[i]->rc->requestCount16;
}
}
void StringBufferManager::dumpStats()
{
updateStats();
Con::printf("===== String Manager Stats =====");
Con::printf(" strings: %i", strings.size());
Con::printf(" utf8 requests: %Lu", request8);
Con::printf(" utf16 requests: %Lu", request16);
}
void StringBufferManager::dumpAllStrings()
{
U32 nstrings = strings.size();
Con::printf("===== String Manager: All Strings =====");
Con::printf(" utf8 | utf16 | string");
for(int i=0; i < nstrings; i++)
{
UTF8* tmp = strings[i]->createCopy8();
strings[i]->rc->requestCount8--;
Con::printf("%5llu %5llu \"%s\"", strings[i]->rc->requestCount8, strings[i]->rc->requestCount16, tmp);
delete[] tmp;
}
}
#endif

View file

@ -0,0 +1,134 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _STRINGBUFFER_H_
#define _STRINGBUFFER_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
#include "console/console.h"
/// Utility class to wrap string manipulation in a representation
/// independent way.
///
/// Length does NOT include the null terminator.
class StringBuffer
{
Vector<UTF16> mBuffer;
mutable Vector<UTF8> mBuffer8;
mutable bool mDirty8;
public:
#if defined(TORQUE_DEBUG)
struct RequestCounts
{
U64 requestCount8;
U64 requestCount16;
};
RequestCounts *rc;
#endif
StringBuffer();
StringBuffer(const StringBuffer &copy);
StringBuffer(const StringBuffer *in);
StringBuffer(const UTF8 *in);
StringBuffer(const UTF16 *in);
~StringBuffer();
void append(const StringBuffer &in);
void append(const UTF8* in);
void append(const UTF16* in);
void append(const UTF16* in, U32 len);
void insert(const U32 charOffset, const StringBuffer &in);
void insert(const U32 charOffset, const UTF8* in);
void insert(const U32 charOffset, const UTF16* in);
void insert(const U32 charOffset, const UTF16* in, const U32 len);
/// Get a StringBuffer substring of length 'len' starting from 'start'.
/// Returns a new StringBuffer by value;
StringBuffer substring(const U32 start, const U32 len) const;
/// Get a pointer to a substring of length 'len' starting from 'start'.
/// Returns a raw pointer to a unicode string.
/// You must delete[] the returned string when you are done with it.
/// This follows the "create rule".
UTF8* createSubstring8(const U32 start, const U32 len) const;
UTF16* createSubstring16(const U32 start, const U32 len) const;
void cut(const U32 start, const U32 len);
// UTF8* cut8(const U32 start, const U32 len);
// UTF16* cut16(const U32 start, const U32 len);
const UTF16 getChar(const U32 offset) const;
void setChar(const U32 offset, UTF16 c);
void set(const StringBuffer *in);
void set(const UTF8 *in);
void set(const UTF16 *in);
inline const U32 length() const
{
return mBuffer.size() - 1; // Don't count the NULL of course.
}
/// Get an upper bound size estimate for a UTF8 buffer to hold this
/// string.
const U32 getUTF8BufferSizeEstimate() const
{
return length() * 3 + 1;
}
void getCopy8(UTF8 *buff, const U32 buffSize) const;
void getCopy(UTF16 *buff, const U32 buffSize) const;
/// Get a copy of the contents of the string buffer.
/// You must delete[] the returned copy when you are done with it.
/// This follows the "create rule".
UTF8* createCopy8() const;
UTF16* createCopy() const;
/// Get a pointer to the StringBuffer's data store.
/// Use this in situations where you can be sure that the StringBuffer will
/// not be modified out from under you.
/// The win here is, you avoid yet another data copy. Data copy is slow on
/// most modern hardware.
const UTF16* getPtr() const;
const UTF8* getPtr8() const;
private:
void updateBuffer8();
#if defined(TORQUE_DEBUG)
void clearRequestCounts() { rc->requestCount16 = 0; rc->requestCount8 = 0; }
#endif
};
#endif

View file

@ -0,0 +1,233 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/strings/stringFunctions.h"
#include "core/stringTable.h"
_StringTable *_gStringTable = NULL;
const U32 _StringTable::csm_stInitSize = 29;
//---------------------------------------------------------------
//
// StringTable functions
//
//---------------------------------------------------------------
namespace {
bool sgInitTable = true;
U8 sgHashTable[256];
void initTolowerTable()
{
for (U32 i = 0; i < 256; i++) {
U8 c = dTolower(i);
sgHashTable[i] = c * c;
}
sgInitTable = false;
}
} // namespace {}
U32 _StringTable::hashString(const char* str)
{
if (sgInitTable)
initTolowerTable();
if(!str) return -1;
U32 ret = 0;
char c;
while((c = *str++) != 0) {
ret <<= 1;
ret ^= sgHashTable[static_cast<U32>(c)];
}
return ret;
}
U32 _StringTable::hashStringn(const char* str, S32 len)
{
if (sgInitTable)
initTolowerTable();
U32 ret = 0;
char c;
while((c = *str++) != 0 && len--) {
ret <<= 1;
ret ^= sgHashTable[static_cast<U32>(c)];
}
return ret;
}
//--------------------------------------
_StringTable::_StringTable()
{
buckets = (Node **) dMalloc(csm_stInitSize * sizeof(Node *));
for(U32 i = 0; i < csm_stInitSize; i++) {
buckets[i] = 0;
}
numBuckets = csm_stInitSize;
itemCount = 0;
}
//--------------------------------------
_StringTable::~_StringTable()
{
dFree(buckets);
}
//--------------------------------------
void _StringTable::create()
{
//AssertFatal(_gStringTable == NULL, "StringTable::create: StringTable already exists.");
if(!_gStringTable)
{
_gStringTable = new _StringTable;
_gStringTable->_EmptyString = _gStringTable->insert("");
}
}
//--------------------------------------
void _StringTable::destroy()
{
AssertFatal(StringTable != NULL, "StringTable::destroy: StringTable does not exist.");
delete _gStringTable;
_gStringTable = NULL;
}
//--------------------------------------
StringTableEntry _StringTable::insert(const char* _val, const bool caseSens)
{
// Added 3/29/2007 -- If this is undesirable behavior, let me know -patw
const char *val = _val;
if( val == NULL )
val = "";
//-
Node **walk, *temp;
U32 key = hashString(val);
walk = &buckets[key % numBuckets];
while((temp = *walk) != NULL) {
if(caseSens && !dStrcmp(temp->val, val))
return temp->val;
else if(!caseSens && !dStricmp(temp->val, val))
return temp->val;
walk = &(temp->next);
}
char *ret = 0;
if(!*walk) {
*walk = (Node *) mempool.alloc(sizeof(Node));
(*walk)->next = 0;
(*walk)->val = (char *) mempool.alloc(dStrlen(val) + 1);
dStrcpy((*walk)->val, val);
ret = (*walk)->val;
itemCount ++;
}
if(itemCount > 2 * numBuckets) {
resize(4 * numBuckets - 1);
}
return ret;
}
//--------------------------------------
StringTableEntry _StringTable::insertn(const char* src, S32 len, const bool caseSens)
{
char val[256];
AssertFatal(len < 255, "Invalid string to insertn");
dStrncpy(val, src, len);
val[len] = 0;
return insert(val, caseSens);
}
//--------------------------------------
StringTableEntry _StringTable::lookup(const char* val, const bool caseSens)
{
Node **walk, *temp;
U32 key = hashString(val);
walk = &buckets[key % numBuckets];
while((temp = *walk) != NULL) {
if(caseSens && !dStrcmp(temp->val, val))
return temp->val;
else if(!caseSens && !dStricmp(temp->val, val))
return temp->val;
walk = &(temp->next);
}
return NULL;
}
//--------------------------------------
StringTableEntry _StringTable::lookupn(const char* val, S32 len, const bool caseSens)
{
Node **walk, *temp;
U32 key = hashStringn(val, len);
walk = &buckets[key % numBuckets];
while((temp = *walk) != NULL) {
if(caseSens && !dStrncmp(temp->val, val, len) && temp->val[len] == 0)
return temp->val;
else if(!caseSens && !dStrnicmp(temp->val, val, len) && temp->val[len] == 0)
return temp->val;
walk = &(temp->next);
}
return NULL;
}
//--------------------------------------
void _StringTable::resize(const U32 newSize)
{
Node *head = NULL, *walk, *temp;
U32 i;
// reverse individual bucket lists
// we do this because new strings are added at the end of bucket
// lists so that case sens strings are always after their
// corresponding case insens strings
for(i = 0; i < numBuckets; i++) {
walk = buckets[i];
while(walk)
{
temp = walk->next;
walk->next = head;
head = walk;
walk = temp;
}
}
buckets = (Node **) dRealloc(buckets, newSize * sizeof(Node));
for(i = 0; i < newSize; i++) {
buckets[i] = 0;
}
numBuckets = newSize;
walk = head;
while(walk) {
U32 key;
Node *temp = walk;
walk = walk->next;
key = hashString(temp->val);
temp->next = buckets[key % newSize];
buckets[key % newSize] = temp;
}
}

View file

@ -0,0 +1,176 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _STRINGTABLE_H_
#define _STRINGTABLE_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _DATACHUNKER_H_
#include "core/dataChunker.h"
#endif
//--------------------------------------
/// A global table for the hashing and tracking of strings.
///
/// Only one _StringTable is ever instantiated in Torque. It is accessible via the
/// global variable StringTable.
///
/// StringTable is used to manage strings in Torque. It performs the following tasks:
/// - Ensures that only one pointer is ever used for a given string (through
/// insert()).
/// - Allows the lookup of a string in the table.
///
/// @code
/// // Adding a string to the StringTable.
/// StringTableEntry mRoot;
/// mRoot = StringTable->insert(root);
///
/// // Looking up a string in the StringTable.
/// StringTableEntry stName = StringTable->lookupn(name, len);
///
/// // Comparing two strings in the StringTable (see below).
/// if(mRoot == stName) Con::printf("These strings are equal!");
/// @endcode
///
/// <b>But why is this useful, you ask?</b> Because every string that's run through the
/// StringTable is stored once and only once, every string has one and only one
/// pointer mapped to it. As a pointer is an integer value (usually an unsigned int),
/// so we can do several neat things:
/// - StringTableEntrys can be compared directly for equality, instead of using
/// the time-consuming dStrcmp() or dStricmp() function.
/// - For things like object names, we can avoid storing multiple copies of the
/// string containing the name. The StringTable ensures that we only ever store
/// one copy.
/// - When we're doing lookups by name (for instances, of resources), we can determine
/// if the object is even registered in the system by looking up its name in the
/// StringTable. Then, we can use the pointer as a hash key.
///
/// The scripting engine and the resource manager are the primary users of the
/// StringTable.
///
/// @note Be aware that the StringTable NEVER DEALLOCATES memory, so be careful when you
/// add strings to it. If you carelessly add many strings, you will end up wasting
/// space.
class _StringTable
{
private:
/// @name Implementation details
/// @{
/// This is internal to the _StringTable class.
struct Node
{
char *val;
Node *next;
};
Node** buckets;
U32 numBuckets;
U32 itemCount;
DataChunker mempool;
StringTableEntry _EmptyString;
protected:
static const U32 csm_stInitSize;
_StringTable();
~_StringTable();
/// @}
public:
/// Initialize StringTable.
///
/// This is called at program start to initialize the StringTable global.
static void create();
/// Destroy the StringTable
///
/// This is called at program end to destroy the StringTable global.
static void destroy();
/// Get a pointer from the string table, adding the string to the table
/// if it was not already present.
///
/// @param string String to check in the table (and add).
/// @param caseSens Determines whether case matters.
StringTableEntry insert(const char *string, bool caseSens = false);
/// Get a pointer from the string table, adding the string to the table
/// if it was not already present.
///
/// @param string String to check in the table (and add).
/// @param len Length of the string in bytes.
/// @param caseSens Determines whether case matters.
StringTableEntry insertn(const char *string, S32 len, bool caseSens = false);
/// Get a pointer from the string table, NOT adding the string to the table
/// if it was not already present.
///
/// @param string String to check in the table (but not add).
/// @param caseSens Determines whether case matters.
StringTableEntry lookup(const char *string, bool caseSens = false);
/// Get a pointer from the string table, NOT adding the string to the table
/// if it was not already present.
///
/// @param string String to check in the table (but not add).
/// @param len Length of string in bytes.
/// @param caseSens Determines whether case matters.
StringTableEntry lookupn(const char *string, S32 len, bool caseSens = false);
/// Resize the StringTable to be able to hold newSize items. This
/// is called automatically by the StringTable when the table is
/// full past a certain threshhold.
///
/// @param newSize Number of new items to allocate space for.
void resize(const U32 newSize);
/// Hash a string into a U32.
static U32 hashString(const char* in_pString);
/// Hash a string of given length into a U32.
static U32 hashStringn(const char* in_pString, S32 len);
/// Represents a zero length string.
StringTableEntry EmptyString() const { return _EmptyString; }
};
extern _StringTable *_gStringTable;
inline _StringTable* _getStringTable()
{
if(_gStringTable == NULL)
_StringTable::create();
return _gStringTable;
}
#define StringTable _getStringTable()
#endif //_STRINGTABLE_H_

View file

@ -0,0 +1,163 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/strings/findMatch.h"
#include "core/strings/stringFunctions.h"
//--------------------------------------------------------------------------------
// NAME
// FindMatch::FindMatch( const char *_expression, S32 maxNumMatches )
//
// DESCRIPTION
// Class to match regular expressions (file names)
// only works with '*','?', and 'chars'
//
// ARGUMENTS
// _expression - The regular expression you intend to match (*.??abc.bmp)
// _maxMatches - The maximum number of strings you wish to match.
//
// RETURNS
//
// NOTES
//
//--------------------------------------------------------------------------------
FindMatch::FindMatch( U32 _maxMatches )
{
VECTOR_SET_ASSOCIATION(matchList);
expression = NULL;
maxMatches = _maxMatches;
matchList.reserve( maxMatches );
}
FindMatch::FindMatch( char *_expression, U32 _maxMatches )
{
VECTOR_SET_ASSOCIATION(matchList);
expression = NULL;
setExpression( _expression );
maxMatches = _maxMatches;
matchList.reserve( maxMatches );
}
FindMatch::~FindMatch()
{
delete [] expression;
matchList.clear();
}
void FindMatch::setExpression( const char *_expression )
{
delete [] expression;
expression = new char[dStrlen(_expression) + 1];
dStrcpy(expression, _expression);
dStrupr(expression);
}
bool FindMatch::findMatch( const char *str, bool caseSensitive )
{
if ( isFull() )
return false;
char nstr[512];
dStrcpy( nstr,str );
dStrupr(nstr);
if ( isMatch( expression, nstr, caseSensitive ) )
{
matchList.push_back( (char*)str );
return true;
}
return false;
}
inline bool IsCharMatch( char e, char s, bool caseSensitive )
{
return ( ( e == '?' ) || ( caseSensitive && e == s ) || ( dToupper(e) == dToupper(s) ) );
}
bool FindMatch::isMatch( const char *exp, const char *str, bool caseSensitive )
{
while ( *str && ( *exp != '*' ) )
{
if ( !IsCharMatch( *exp++, *str++, caseSensitive ) )
return false;
}
const char* cp = NULL;
const char* mp = NULL;
while ( *str )
{
if ( *exp == '*' )
{
if ( !*++exp )
return true;
mp = exp;
cp = str+1;
}
else if ( IsCharMatch( *exp, *str, caseSensitive ) )
{
exp++;
str++;
}
else
{
exp = mp;
str = cp++;
}
}
while ( *exp == '*' )
exp++;
return !*exp;
}
bool FindMatch::isMatchMultipleExprs( const char *exps, const char *str, bool caseSensitive )
{
char *tok = 0;
int len = dStrlen(exps);
char *e = new char[len+1];
dStrcpy(e,exps);
// [tom, 12/18/2006] This no longer supports space separated expressions as
// they don't work when the paths have spaces in.
// search for each expression. return true soon as we see one.
for( tok = dStrtok(e,"\t"); tok != NULL; tok = dStrtok(NULL,"\t"))
{
if( isMatch( tok, str, caseSensitive) )
{
delete []e;
return true;
}
}
delete []e;
return false;
}

View file

@ -0,0 +1,63 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _FINDMATCH_H_
#define _FINDMATCH_H_
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
class FindMatch
{
char* expression;
U32 maxMatches;
public:
static bool isMatch( const char *exp, const char *string, bool caseSensitive = false );
static bool isMatchMultipleExprs( const char *exps, const char *str, bool caseSensitive );
Vector<char *> matchList;
FindMatch( U32 _maxMatches = 256 );
FindMatch( char *_expression, U32 _maxMatches = 256 );
~FindMatch();
bool findMatch(const char *string, bool caseSensitive = false);
void setExpression( const char *_expression );
S32 numMatches() const
{
return(matchList.size());
}
bool isFull() const
{
return (matchList.size() >= S32(maxMatches));
}
void clear()
{
matchList.clear();
}
};
#endif // _FINDMATCH_H_

View file

@ -0,0 +1,534 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <stdarg.h>
#include <stdio.h>
#include "core/strings/stringFunctions.h"
#include "platform/platform.h"
#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON)
// This standard function is not defined when compiling with VC7...
#define vsnprintf _vsnprintf
#endif
//-----------------------------------------------------------------------------
// Original code from: http://sourcefrog.net/projects/natsort/
// Somewhat altered here.
//TODO: proper UTF8 support; currently only working for single-byte characters
/* -*- mode: c; c-file-style: "k&r" -*-
strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/* partial change history:
*
* 2004-10-10 mbp: Lift out character type dependencies into macros.
*
* Eric Sosman pointed out that ctype functions take a parameter whose
* value must be that of an unsigned int, even on platforms that have
* negative chars in their default char type.
*/
typedef char nat_char;
/* These are defined as macros to make it easier to adapt this code to
* different characters types or comparison functions. */
static inline int
nat_isdigit( nat_char a )
{
return dIsdigit( a );
}
static inline int
nat_isspace( nat_char a )
{
return dIsspace( a );
}
static inline nat_char
nat_toupper( nat_char a )
{
return dToupper( a );
}
static int
compare_right(const nat_char* a, const nat_char* b)
{
int bias = 0;
/* The longest run of digits wins. That aside, the greatest
value wins, but we can't know that it will until we've scanned
both numbers to know that they have the same magnitude, so we
remember it in BIAS. */
for (;; a++, b++) {
if (!nat_isdigit(*a) && !nat_isdigit(*b))
return bias;
else if (!nat_isdigit(*a))
return -1;
else if (!nat_isdigit(*b))
return +1;
else if (*a < *b) {
if (!bias)
bias = -1;
} else if (*a > *b) {
if (!bias)
bias = +1;
} else if (!*a && !*b)
return bias;
}
return 0;
}
static int
compare_left(const nat_char* a, const nat_char* b)
{
/* Compare two left-aligned numbers: the first to have a
different value wins. */
for (;; a++, b++) {
if (!nat_isdigit(*a) && !nat_isdigit(*b))
return 0;
else if (!nat_isdigit(*a))
return -1;
else if (!nat_isdigit(*b))
return +1;
else if (*a < *b)
return -1;
else if (*a > *b)
return +1;
}
return 0;
}
static int strnatcmp0(const nat_char* a, const nat_char* b, int fold_case)
{
int ai, bi;
nat_char ca, cb;
int fractional, result;
ai = bi = 0;
while (1) {
ca = a[ai]; cb = b[bi];
/* skip over leading spaces or zeros */
while (nat_isspace(ca))
ca = a[++ai];
while (nat_isspace(cb))
cb = b[++bi];
/* process run of digits */
if (nat_isdigit(ca) && nat_isdigit(cb)) {
fractional = (ca == '0' || cb == '0');
if (fractional) {
if ((result = compare_left(a+ai, b+bi)) != 0)
return result;
} else {
if ((result = compare_right(a+ai, b+bi)) != 0)
return result;
}
}
if (!ca && !cb) {
/* The strings compare the same. Perhaps the caller
will want to call strcmp to break the tie. */
return 0;
}
if (fold_case) {
ca = nat_toupper(ca);
cb = nat_toupper(cb);
}
if (ca < cb)
return -1;
else if (ca > cb)
return +1;
++ai; ++bi;
}
}
int dStrnatcmp(const nat_char* a, const nat_char* b) {
return strnatcmp0(a, b, 0);
}
/* Compare, recognizing numeric string and ignoring case. */
int dStrnatcasecmp(const nat_char* a, const nat_char* b) {
return strnatcmp0(a, b, 1);
}
//------------------------------------------------------------------------------
// non-standard string functions
char *dStrdup_r(const char *src, const char *fileName, dsize_t lineNumber)
{
char *buffer = (char *) dMalloc_r(dStrlen(src) + 1, fileName, lineNumber);
dStrcpy(buffer, src);
return buffer;
}
char* dStrichr( char* str, char ch )
{
AssertFatal( str != NULL, "dStrichr - NULL string" );
if( !ch )
return dStrchr( str, ch );
char c = dToupper( ch );
while( *str )
{
if( dToupper( *str ) == c )
return str;
++ str;
}
return NULL;
}
const char* dStrichr( const char* str, char ch )
{
AssertFatal( str != NULL, "dStrichr - NULL string" );
if( !ch )
return dStrchr( str, ch );
char c = dToupper( ch );
while( *str )
{
if( dToupper( *str ) == c )
return str;
++ str;
}
return NULL;
}
// concatenates a list of src's onto the end of dst
// the list of src's MUST be terminated by a NULL parameter
// dStrcatl(dst, sizeof(dst), src1, src2, NULL);
char* dStrcatl(char *dst, dsize_t dstSize, ...)
{
const char* src = NULL;
char *p = dst;
AssertFatal(dstSize > 0, "dStrcatl: destination size is set zero");
dstSize--; // leave room for string termination
// find end of dst
while (dstSize && *p++)
dstSize--;
va_list args;
va_start(args, dstSize);
// concatenate each src to end of dst
while ( (src = va_arg(args, const char*)) != NULL )
{
while( dstSize && *src )
{
*p++ = *src++;
dstSize--;
}
}
va_end(args);
// make sure the string is terminated
*p = 0;
return dst;
}
// copy a list of src's into dst
// the list of src's MUST be terminated by a NULL parameter
// dStrccpyl(dst, sizeof(dst), src1, src2, NULL);
char* dStrcpyl(char *dst, dsize_t dstSize, ...)
{
const char* src = NULL;
char *p = dst;
AssertFatal(dstSize > 0, "dStrcpyl: destination size is set zero");
dstSize--; // leave room for string termination
va_list args;
va_start(args, dstSize);
// concatenate each src to end of dst
while ( (src = va_arg(args, const char*)) != NULL )
{
while( dstSize && *src )
{
*p++ = *src++;
dstSize--;
}
}
va_end(args);
// make sure the string is terminated
*p = 0;
return dst;
}
int dStrcmp( const UTF16 *str1, const UTF16 *str2)
{
#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON)
return wcscmp( reinterpret_cast<const wchar_t *>( str1 ), reinterpret_cast<const wchar_t *>( str2 ) );
#else
int ret;
const UTF16 *a, *b;
a = str1;
b = str2;
while( ((ret = *a - *b) == 0) && *a && *b )
a++, b++;
return ret;
#endif
}
char* dStrupr(char *str)
{
#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON)
return _strupr(str);
#else
if (str == NULL)
return(NULL);
char* saveStr = str;
while (*str)
{
*str = toupper(*str);
str++;
}
return saveStr;
#endif
}
char* dStrlwr(char *str)
{
#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON)
return _strlwr(str);
#else
if (str == NULL)
return(NULL);
char* saveStr = str;
while (*str)
{
*str = tolower(*str);
str++;
}
return saveStr;
#endif
}
//------------------------------------------------------------------------------
// standard I/O functions
void dPrintf(const char *format, ...)
{
va_list args;
va_start(args, format);
vprintf(format, args);
}
S32 dVprintf(const char *format, void *arglist)
{
return vprintf(format, (char*)arglist);
}
S32 dSprintf(char *buffer, U32 bufferSize, const char *format, ...)
{
va_list args;
va_start(args, format);
S32 len = vsnprintf(buffer, bufferSize, format, args);
AssertWarn( len < bufferSize, "Buffer too small in call to dSprintf!" );
return (len);
}
S32 dVsprintf(char *buffer, U32 bufferSize, const char *format, void *arglist)
{
S32 len = vsnprintf(buffer, bufferSize, format, (char*)arglist);
AssertWarn( len < bufferSize, "Buffer too small in call to dVsprintf!" );
return (len);
}
S32 dSscanf(const char *buffer, const char *format, ...)
{
#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON)
va_list args;
va_start(args, format);
// Boy is this lame. We have to scan through the format string, and find out how many
// arguments there are. We'll store them off as void*, and pass them to the sscanf
// function through specialized calls. We're going to have to put a cap on the number of args that
// can be passed, 8 for the moment. Sigh.
static void* sVarArgs[20];
U32 numArgs = 0;
for (const char* search = format; *search != '\0'; search++) {
if (search[0] == '%' && search[1] != '%')
numArgs++;
}
AssertFatal(numArgs <= 20, "Error, too many arguments to lame implementation of dSscanf. Fix implmentation");
// Ok, we have the number of arguments...
for (U32 i = 0; i < numArgs; i++)
sVarArgs[i] = va_arg(args, void*);
va_end(args);
switch (numArgs) {
case 0: return 0;
case 1: return sscanf(buffer, format, sVarArgs[0]);
case 2: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1]);
case 3: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2]);
case 4: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3]);
case 5: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4]);
case 6: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5]);
case 7: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6]);
case 8: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7]);
case 9: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8]);
case 10: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9]);
case 11: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10]);
case 12: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11]);
case 13: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11], sVarArgs[12]);
case 14: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11], sVarArgs[12], sVarArgs[13]);
case 15: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11], sVarArgs[12], sVarArgs[13], sVarArgs[14]);
case 16: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11], sVarArgs[12], sVarArgs[13], sVarArgs[14], sVarArgs[15]);
case 17: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11], sVarArgs[12], sVarArgs[13], sVarArgs[14], sVarArgs[15], sVarArgs[16]);
case 18: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11], sVarArgs[12], sVarArgs[13], sVarArgs[14], sVarArgs[15], sVarArgs[16], sVarArgs[17]);
case 19: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11], sVarArgs[12], sVarArgs[13], sVarArgs[14], sVarArgs[15], sVarArgs[16], sVarArgs[17], sVarArgs[18]);
case 20: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11], sVarArgs[12], sVarArgs[13], sVarArgs[14], sVarArgs[15], sVarArgs[16], sVarArgs[17], sVarArgs[18], sVarArgs[19]);
}
return 0;
#else
va_list args;
va_start(args, format);
return vsscanf(buffer, format, args);
#endif
}
/// Safe form of dStrcmp: checks both strings for NULL before comparing
bool dStrEqual(const char* str1, const char* str2)
{
if (!str1 || !str2)
return false;
else
return (dStrcmp(str1, str2) == 0);
}
/// Check if one string starts with another
bool dStrStartsWith(const char* str1, const char* str2)
{
return !dStrnicmp(str1, str2, dStrlen(str2));
}
/// Check if one string ends with another
bool dStrEndsWith(const char* str1, const char* str2)
{
const char *p = str1 + dStrlen(str1) - dStrlen(str2);
return ((p >= str1) && !dStricmp(p, str2));
}
/// Strip the path from the input filename
char* dStripPath(const char* filename)
{
const char* itr = filename + dStrlen(filename);
while(--itr != filename) {
if (*itr == '/' || *itr == '\\') {
itr++;
break;
}
}
return dStrdup(itr);
}
char* dStristr( char* str1, const char* str2 )
{
if( !str1 || !str2 )
return NULL;
// Slow but at least we have it.
U32 str2len = strlen( str2 );
while( *str1 )
{
if( strncasecmp( str1, str2, str2len ) == 0 )
return str1;
++ str1;
}
return NULL;
}
const char* dStristr( const char* str1, const char* str2 )
{
return dStristr( const_cast< char* >( str1 ), str2 );
}

View file

@ -0,0 +1,227 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _STRINGFUNCTIONS_H_
#define _STRINGFUNCTIONS_H_
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifndef _TORQUE_TYPES_H_
#include "platform/types.h"
#endif
#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON)
// These standard functions are not defined on Win32 and other Microsoft platforms...
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#define strtof (float)strtod
#endif
//------------------------------------------------------------------------------
// standard string functions [defined in platformString.cpp]
inline char *dStrcat(char *dst, const char *src)
{
return strcat(dst,src);
}
inline char *dStrncat(char *dst, const char *src, dsize_t len)
{
return strncat(dst,src,len);
}
inline int dStrcmp(const char *str1, const char *str2)
{
return strcmp(str1, str2);
}
inline int dStrncmp(const char *str1, const char *str2, dsize_t len)
{
return strncmp(str1, str2, len);
}
inline int dStricmp(const char *str1, const char *str2)
{
return strcasecmp( str1, str2 );
}
inline int dStrnicmp(const char *str1, const char *str2, dsize_t len)
{
return strncasecmp( str1, str2, len );
}
inline char *dStrcpy(char *dst, const char *src)
{
return strcpy(dst,src);
}
inline char *dStrncpy(char *dst, const char *src, dsize_t len)
{
return strncpy(dst,src,len);
}
inline dsize_t dStrlen(const char *str)
{
return strlen(str);
}
inline char *dStrchr(char *str, int c)
{
return strchr(str,c);
}
inline const char *dStrchr(const char *str, int c)
{
return strchr(str,c);
}
inline char *dStrrchr(char *str, int c)
{
return strrchr(str,c);
}
inline const char *dStrrchr(const char *str, int c)
{
return strrchr(str,c);
}
inline dsize_t dStrspn(const char *str, const char *set)
{
return strspn(str, set);
}
inline dsize_t dStrcspn(const char *str, const char *set)
{
return strcspn(str, set);
}
inline char *dStrstr(const char *str1, const char *str2)
{
return strstr((char *)str1,str2);
}
const char* dStristr( const char* str1, const char* str2 );
char* dStristr( char* str1, const char* str2 );
inline char *dStrtok(char *str, const char *sep)
{
return strtok(str, sep);
}
inline S32 dAtoi(const char *str)
{
return strtol(str, NULL, 10);
}
inline U32 dAtoui(const char *str, U32 base = 10)
{
return strtoul(str, NULL, base);
}
inline F32 dAtof(const char *str)
{
return strtof(str, NULL);
}
inline char dToupper(const char c)
{
return toupper( c );
}
inline char dTolower(const char c)
{
return tolower( c );
}
inline bool dIsalnum(const char c)
{
return isalnum(c);
}
inline bool dIsalpha(const char c)
{
return isalpha(c);
}
inline bool dIsspace(const char c)
{
return isspace(c);
}
inline bool dIsdigit(const char c)
{
return isdigit(c);
}
inline bool dIsquote(const char c)
{
return ( c == '\"' );
}
//------------------------------------------------------------------------------
// non-standard string functions [defined in stringFunctions.cpp]
#define dStrdup(x) dStrdup_r(x, __FILE__, __LINE__)
extern char *dStrdup_r(const char *src, const char*, dsize_t);
extern char *dStrcpyl(char *dst, dsize_t dstSize, ...);
extern char *dStrcatl(char *dst, dsize_t dstSize, ...);
extern char *dStrupr(char *str);
extern char *dStrlwr(char *str);
extern char* dStrichr( char* str, char ch );
extern const char* dStrichr( const char* str, char ch );
extern int dStrcmp(const UTF16 *str1, const UTF16 *str2);
extern int dStrnatcmp( const char* str1, const char* str2 );
extern int dStrnatcasecmp( const char* str1, const char* str2 );
inline bool dAtob(const char *str)
{
return !dStricmp(str, "true") || dAtof(str);
}
bool dStrEqual(const char* str1, const char* str2);
bool dStrStartsWith(const char* str1, const char* str2);
bool dStrEndsWith(const char* str1, const char* str2);
char* dStripPath(const char* filename);
//------------------------------------------------------------------------------
// standard I/O functions [defined in platformString.cpp]
extern void dPrintf(const char *format, ...);
extern int dVprintf(const char *format, void *arglist);
extern int dSprintf(char *buffer, U32 bufferSize, const char *format, ...);
extern int dVsprintf(char *buffer, U32 bufferSize, const char *format, void *arglist);
extern int dSscanf(const char *buffer, const char *format, ...);
#endif

View file

@ -0,0 +1,217 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/strings/stringFunctions.h"
#include "core/strings/stringUnit.h"
#include "console/console.h"
namespace StringUnit
{
static char _returnBuffer[ 2048 ];
const char *getUnit(const char *string, U32 index, const char *set, char* buffer, U32 bufferSize)
{
if( !buffer )
{
buffer = _returnBuffer;
bufferSize = sizeof( _returnBuffer );
}
AssertFatal( bufferSize, "StringUnit::getUnit - bufferSize cannot be zero!" );
if( !bufferSize )
return "";
buffer[0] = 0;
U32 sz;
while(index--)
{
if(!*string)
return buffer;
sz = dStrcspn(string, set);
if (string[sz] == 0)
return buffer;
string += (sz + 1);
}
sz = dStrcspn(string, set);
if (sz == 0)
return buffer;
AssertWarn( sz + 1 < bufferSize, "Size of returned string too large for return buffer" );
sz = getMin( sz, bufferSize - 1 );
dStrncpy(buffer, string, sz);
buffer[sz] = '\0';
return buffer;
}
const char *getUnits(const char *string, S32 startIndex, S32 endIndex, const char *set)
{
if( startIndex > endIndex )
return "";
S32 sz;
S32 index = startIndex;
while(index--)
{
if(!*string)
return "";
sz = dStrcspn(string, set);
if (string[sz] == 0)
return "";
string += (sz + 1);
}
const char *startString = string;
for( U32 i = startIndex; i <= endIndex && *string; i ++ )
{
sz = dStrcspn(string, set);
string += sz;
if( i < endIndex )
string ++;
}
S32 totalSize = ( S32 )( string - startString );
AssertWarn( totalSize + 1 < sizeof( _returnBuffer ), "Size of returned string too large for return buffer" );
totalSize = getMin( totalSize, S32( sizeof( _returnBuffer ) - 1 ) );
if( totalSize > 0 )
{
char *ret = &_returnBuffer[0];
dStrncpy( ret, startString, totalSize );
ret[ totalSize ] = '\0';
return ret;
}
return "";
}
U32 getUnitCount(const char *string, const char *set)
{
U32 count = 0;
U8 last = 0;
while(*string)
{
last = *string++;
for(U32 i =0; set[i]; i++)
{
if(last == set[i])
{
count++;
last = 0;
break;
}
}
}
if(last)
count++;
return count;
}
const char* setUnit(const char *string, U32 index, const char *replace, const char *set)
{
U32 sz;
const char *start = string;
AssertFatal( dStrlen(string) + dStrlen(replace) + 1 < sizeof( _returnBuffer ), "Size of returned string too large for return buffer" );
char *ret = &_returnBuffer[0];
ret[0] = '\0';
U32 padCount = 0;
while(index--)
{
sz = dStrcspn(string, set);
if(string[sz] == 0)
{
string += sz;
padCount = index + 1;
break;
}
else
string += (sz + 1);
}
// copy first chunk
sz = string-start;
dStrncpy(ret, start, sz);
for(U32 i = 0; i < padCount; i++)
ret[sz++] = set[0];
// replace this unit
ret[sz] = '\0';
dStrcat(ret, replace);
// copy remaining chunks
sz = dStrcspn(string, set); // skip chunk we're replacing
if(!sz && !string[sz])
return ret;
string += sz;
dStrcat(ret, string);
return ret;
}
const char* removeUnit(const char *string, U32 index, const char *set)
{
U32 sz;
const char *start = string;
AssertFatal( dStrlen(string) + 1 < sizeof( _returnBuffer ), "Size of returned string too large for return buffer" );
char *ret = &_returnBuffer[0];
ret[0] = '\0';
while(index--)
{
sz = dStrcspn(string, set);
// if there was no unit out there... return the original string
if(string[sz] == 0)
return start;
else
string += (sz + 1);
}
// copy first chunk
sz = string-start;
dStrncpy(ret, start, sz);
ret[sz] = 0;
// copy remaining chunks
sz = dStrcspn(string, set); // skip chunk we're removing
if(string[sz] == 0) { // if that was the last...
if(string != start) {
ret[string - start - 1] = 0; // then kill any trailing delimiter
}
return ret; // and bail
}
string += sz + 1; // skip the extra field delimiter
dStrcat(ret, string);
return ret;
}
}

View file

@ -0,0 +1,39 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _STRINGUNIT_H_
#define _STRINGUNIT_H_
#include "platform/types.h"
/// These functions are used for chunking up strings by delimiter.
/// Especially useful for handling TorqueScript space-delimited fields
namespace StringUnit
{
const char *getUnit(const char *string, U32 index, const char *set, char* buffer = 0, U32 bufferSize = 0);
const char *getUnits(const char *string, S32 startIndex, S32 endIndex, const char *set);
U32 getUnitCount(const char *string, const char *set);
const char* setUnit(const char *string, U32 index, const char *replace, const char *set);
const char* removeUnit(const char *string, U32 index, const char *set);
};
#endif

View file

@ -0,0 +1,663 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <stdio.h>
#include "core/frameAllocator.h"
#include "core/strings/unicode.h"
#include "core/strings/stringFunctions.h"
#include "platform/profiler.h"
#include "console/console.h"
#define TORQUE_ENABLE_UTF16_CACHE
#ifdef TORQUE_ENABLE_UTF16_CACHE
#include "core/util/tDictionary.h"
#include "core/util/hashFunction.h"
#endif
//-----------------------------------------------------------------------------
/// replacement character. Standard correct value is 0xFFFD.
#define kReplacementChar 0xFFFD
/// Look up table. Shift a byte >> 1, then look up how many bytes to expect after it.
/// Contains -1's for illegal values.
static const U8 sgFirstByteLUT[128] =
{
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x0F // single byte ascii
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x1F // single byte ascii
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x2F // single byte ascii
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x3F // single byte ascii
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4F // trailing utf8
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x5F // trailing utf8
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x6F // first of 2
3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 6, 0, // 0x7F // first of 3,4,5,illegal in utf-8
};
/// Look up table. Shift a 16-bit word >> 10, then look up whether it is a surrogate,
/// and which part. 0 means non-surrogate, 1 means 1st in pair, 2 means 2nd in pair.
static const U8 sgSurrogateLUT[64] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x1F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x2F
0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, // 0x3F
};
/// Look up table. Feed value from firstByteLUT in, gives you
/// the mask for the data bits of that UTF-8 code unit.
static const U8 sgByteMask8LUT[] = { 0x3f, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; // last 0=6, 1=7, 2=5, 4, 3, 2, 1 bits
/// Mask for the data bits of a UTF-16 surrogate.
static const U16 sgByteMaskLow10 = 0x03ff;
//-----------------------------------------------------------------------------
#ifdef TORQUE_ENABLE_UTF16_CACHE
/// Cache data for UTF16 strings. This is wrapped in a class so that data is
/// automatically freed when the hash table is deleted.
struct UTF16Cache
{
UTF16 *mString;
U32 mLength;
UTF16Cache()
{
mString = NULL;
mLength = 0;
}
UTF16Cache(UTF16 *str, U32 len)
{
mLength = len;
mString = new UTF16[mLength];
dMemcpy(mString, str, mLength * sizeof(UTF16));
}
UTF16Cache(const UTF16Cache &other)
{
mLength = other.mLength;
mString = new UTF16[mLength];
dMemcpy(mString, other.mString, mLength * sizeof(UTF16));
}
void operator =(const UTF16Cache &other)
{
delete [] mString;
mLength = other.mLength;
mString = new UTF16[mLength];
dMemcpy(mString, other.mString, mLength * sizeof(UTF16));
}
~UTF16Cache()
{
delete [] mString;
}
void copyToBuffer(UTF16 *outBuffer, U32 lenToCopy, bool nullTerminate = true) const
{
U32 copy = getMin(mLength, lenToCopy);
if(mString && copy > 0)
dMemcpy(outBuffer, mString, copy * sizeof(UTF16));
if(nullTerminate)
outBuffer[copy] = 0;
}
};
/// Cache for UTF16 strings
typedef HashTable<U32, UTF16Cache> UTF16CacheTable;
static UTF16CacheTable sgUTF16Cache;
#endif // TORQUE_ENABLE_UTF16_CACHE
//-----------------------------------------------------------------------------
inline bool isSurrogateRange(U32 codepoint)
{
return ( 0xd800 < codepoint && codepoint < 0xdfff );
}
inline bool isAboveBMP(U32 codepoint)
{
return ( codepoint > 0xFFFF );
}
//-----------------------------------------------------------------------------
U32 convertUTF8toUTF16(const UTF8 *unistring, UTF16 *outbuffer, U32 len)
{
AssertFatal(len >= 1, "Buffer for unicode conversion must be large enough to hold at least the null terminator.");
PROFILE_SCOPE(convertUTF8toUTF16);
#ifdef TORQUE_ENABLE_UTF16_CACHE
// If we have cached this conversion already, don't do it again
U32 hashKey = Torque::hash((const U8 *)unistring, dStrlen(unistring), 0);
UTF16CacheTable::Iterator cacheItr = sgUTF16Cache.find(hashKey);
if(cacheItr != sgUTF16Cache.end())
{
const UTF16Cache &cache = (*cacheItr).value;
cache.copyToBuffer(outbuffer, len);
outbuffer[len-1] = '\0';
return getMin(cache.mLength,len - 1);
}
#endif
U32 walked, nCodepoints;
UTF32 middleman;
nCodepoints=0;
while(*unistring != '\0' && nCodepoints < len)
{
walked = 1;
middleman = oneUTF8toUTF32(unistring,&walked);
outbuffer[nCodepoints] = oneUTF32toUTF16(middleman);
unistring+=walked;
nCodepoints++;
}
nCodepoints = getMin(nCodepoints,len - 1);
outbuffer[nCodepoints] = '\0';
#ifdef TORQUE_ENABLE_UTF16_CACHE
// Cache the results.
// FIXME As written, this will result in some unnecessary memory copying due to copy constructor calls.
UTF16Cache cache(outbuffer, nCodepoints);
sgUTF16Cache.insertUnique(hashKey, cache);
#endif
return nCodepoints;
}
//-----------------------------------------------------------------------------
U32 convertUTF16toUTF8( const UTF16 *unistring, UTF8 *outbuffer, U32 len)
{
AssertFatal(len >= 1, "Buffer for unicode conversion must be large enough to hold at least the null terminator.");
PROFILE_START(convertUTF16toUTF8);
U32 walked, nCodeunits, codeunitLen;
UTF32 middleman;
nCodeunits=0;
while( *unistring != '\0' && nCodeunits + 3 < len )
{
walked = 1;
middleman = oneUTF16toUTF32(unistring,&walked);
codeunitLen = oneUTF32toUTF8(middleman, &outbuffer[nCodeunits]);
unistring += walked;
nCodeunits += codeunitLen;
}
nCodeunits = getMin(nCodeunits,len - 1);
outbuffer[nCodeunits] = '\0';
PROFILE_END();
return nCodeunits;
}
U32 convertUTF16toUTF8DoubleNULL( const UTF16 *unistring, UTF8 *outbuffer, U32 len)
{
AssertFatal(len >= 1, "Buffer for unicode conversion must be large enough to hold at least the null terminator.");
PROFILE_START(convertUTF16toUTF8DoubleNULL);
U32 walked, nCodeunits, codeunitLen;
UTF32 middleman;
nCodeunits=0;
while( ! (*unistring == '\0' && *(unistring + 1) == '\0') && nCodeunits + 3 < len )
{
walked = 1;
middleman = oneUTF16toUTF32(unistring,&walked);
codeunitLen = oneUTF32toUTF8(middleman, &outbuffer[nCodeunits]);
unistring += walked;
nCodeunits += codeunitLen;
}
nCodeunits = getMin(nCodeunits,len - 1);
outbuffer[nCodeunits] = NULL;
outbuffer[nCodeunits+1] = NULL;
PROFILE_END();
return nCodeunits;
}
//-----------------------------------------------------------------------------
// Functions that convert buffers of unicode code points
//-----------------------------------------------------------------------------
UTF16* convertUTF8toUTF16( const UTF8* unistring)
{
PROFILE_SCOPE(convertUTF8toUTF16_create);
// allocate plenty of memory.
U32 nCodepoints, len = dStrlen(unistring) + 1;
FrameTemp<UTF16> buf(len);
// perform conversion
nCodepoints = convertUTF8toUTF16( unistring, buf, len);
// add 1 for the NULL terminator the converter promises it included.
nCodepoints++;
// allocate the return buffer, copy over, and return it.
UTF16 *ret = new UTF16[nCodepoints];
dMemcpy(ret, buf, nCodepoints * sizeof(UTF16));
return ret;
}
//-----------------------------------------------------------------------------
UTF8* convertUTF16toUTF8( const UTF16* unistring)
{
PROFILE_SCOPE(convertUTF16toUTF8_create);
// allocate plenty of memory.
U32 nCodeunits, len = dStrlen(unistring) * 3 + 1;
FrameTemp<UTF8> buf(len);
// perform conversion
nCodeunits = convertUTF16toUTF8( unistring, buf, len);
// add 1 for the NULL terminator the converter promises it included.
nCodeunits++;
// allocate the return buffer, copy over, and return it.
UTF8 *ret = new UTF8[nCodeunits];
dMemcpy(ret, buf, nCodeunits * sizeof(UTF8));
return ret;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Functions that converts one unicode codepoint at a time
//-----------------------------------------------------------------------------
UTF32 oneUTF8toUTF32( const UTF8* codepoint, U32 *unitsWalked)
{
PROFILE_SCOPE(oneUTF8toUTF32);
// codepoints 6 codeunits long are read, but do not convert correctly,
// and are filtered out anyway.
// early out for ascii
if(!(*codepoint & 0x0080))
{
if (unitsWalked != NULL)
*unitsWalked = 1;
return (UTF32)*codepoint;
}
U32 expectedByteCount;
UTF32 ret = 0;
U8 codeunit;
// check the first byte ( a.k.a. codeunit ) .
unsigned char c = codepoint[0];
c = c >> 1;
expectedByteCount = sgFirstByteLUT[c];
if(expectedByteCount > 0) // 0 or negative is illegal to start with
{
// process 1st codeunit
ret |= sgByteMask8LUT[expectedByteCount] & codepoint[0]; // bug?
// process trailing codeunits
for(U32 i=1;i<expectedByteCount; i++)
{
codeunit = codepoint[i];
if( sgFirstByteLUT[codeunit>>1] == 0 )
{
ret <<= 6; // shift up 6
ret |= (codeunit & 0x3f); // mask in the low 6 bits of this codeunit byte.
}
else
{
// found a bad codepoint - did not get a medial where we wanted one.
// Dump the replacement, and claim to have parsed only 1 char,
// so that we'll dump a slew of replacements, instead of eating the next char.
ret = kReplacementChar;
expectedByteCount = 1;
break;
}
}
}
else
{
// found a bad codepoint - got a medial or an illegal codeunit.
// Dump the replacement, and claim to have parsed only 1 char,
// so that we'll dump a slew of replacements, instead of eating the next char.
ret = kReplacementChar;
expectedByteCount = 1;
}
if(unitsWalked != NULL)
*unitsWalked = expectedByteCount;
// codepoints in the surrogate range are illegal, and should be replaced.
if(isSurrogateRange(ret))
ret = kReplacementChar;
// codepoints outside the Basic Multilingual Plane add complexity to our UTF16 string classes,
// we've read them correctly so they won't foul the byte stream,
// but we kill them here to make sure they wont foul anything else
if(isAboveBMP(ret))
ret = kReplacementChar;
return ret;
}
//-----------------------------------------------------------------------------
UTF32 oneUTF16toUTF32(const UTF16* codepoint, U32 *unitsWalked)
{
PROFILE_START(oneUTF16toUTF32);
U8 expectedType;
U32 unitCount;
UTF32 ret = 0;
UTF16 codeunit1,codeunit2;
codeunit1 = codepoint[0];
expectedType = sgSurrogateLUT[codeunit1 >> 10];
switch(expectedType)
{
case 0: // simple
ret = codeunit1;
unitCount = 1;
break;
case 1: // 2 surrogates
codeunit2 = codepoint[1];
if( sgSurrogateLUT[codeunit2 >> 10] == 2)
{
ret = ((codeunit1 & sgByteMaskLow10 ) << 10) | (codeunit2 & sgByteMaskLow10);
unitCount = 2;
break;
}
// else, did not find a trailing surrogate where we expected one,
// so fall through to the error
case 2: // error
// found a trailing surrogate where we expected a codepoint or leading surrogate.
// Dump the replacement.
ret = kReplacementChar;
unitCount = 1;
break;
default:
// unexpected return
AssertFatal(false, "oneUTF16toUTF323: unexpected type");
ret = kReplacementChar;
unitCount = 1;
break;
}
if(unitsWalked != NULL)
*unitsWalked = unitCount;
// codepoints in the surrogate range are illegal, and should be replaced.
if(isSurrogateRange(ret))
ret = kReplacementChar;
// codepoints outside the Basic Multilingual Plane add complexity to our UTF16 string classes,
// we've read them correctly so they wont foul the byte stream,
// but we kill them here to make sure they wont foul anything else
// NOTE: these are perfectly legal codepoints, we just dont want to deal with them.
if(isAboveBMP(ret))
ret = kReplacementChar;
PROFILE_END();
return ret;
}
//-----------------------------------------------------------------------------
UTF16 oneUTF32toUTF16(const UTF32 codepoint)
{
// found a codepoint outside the encodable UTF-16 range!
// or, found an illegal codepoint!
if(codepoint >= 0x10FFFF || isSurrogateRange(codepoint))
return kReplacementChar;
// these are legal, we just don't want to deal with them.
if(isAboveBMP(codepoint))
return kReplacementChar;
return (UTF16)codepoint;
}
//-----------------------------------------------------------------------------
U32 oneUTF32toUTF8(const UTF32 codepoint, UTF8 *threeByteCodeunitBuf)
{
PROFILE_START(oneUTF32toUTF8);
U32 bytecount = 0;
UTF8 *buf;
U32 working = codepoint;
buf = threeByteCodeunitBuf;
//-----------------
if(isSurrogateRange(working)) // found an illegal codepoint!
working = kReplacementChar;
if(isAboveBMP(working)) // these are legal, we just dont want to deal with them.
working = kReplacementChar;
//-----------------
if( working < (1 << 7)) // codeable in 7 bits
bytecount = 1;
else if( working < (1 << 11)) // codeable in 11 bits
bytecount = 2;
else if( working < (1 << 16)) // codeable in 16 bits
bytecount = 3;
AssertISV( bytecount > 0, "Error converting to UTF-8 in oneUTF32toUTF8(). isAboveBMP() should have caught this!");
//-----------------
U8 mask = sgByteMask8LUT[0]; // 0011 1111
U8 marker = ( ~mask << 1); // 1000 0000
// Process the low order bytes, shifting the codepoint down 6 each pass.
for( int i = bytecount-1; i > 0; i--)
{
threeByteCodeunitBuf[i] = marker | (working & mask);
working >>= 6;
}
// Process the 1st byte. filter based on the # of expected bytes.
mask = sgByteMask8LUT[bytecount];
marker = ( ~mask << 1 );
threeByteCodeunitBuf[0] = marker | working & mask;
PROFILE_END();
return bytecount;
}
//-----------------------------------------------------------------------------
U32 dStrlen(const UTF16 *unistring)
{
if(!unistring)
return 0;
U32 i = 0;
while(unistring[i] != '\0')
i++;
// AssertFatal( wcslen(unistring) == i, "Incorrect length" );
return i;
}
//-----------------------------------------------------------------------------
U32 dStrlen(const UTF32 *unistring)
{
U32 i = 0;
while(unistring[i] != '\0')
i++;
return i;
}
//-----------------------------------------------------------------------------
U32 dStrncmp(const UTF16* unistring1, const UTF16* unistring2, U32 len)
{
UTF16 c1, c2;
for(U32 i = 0; i<len; i++)
{
c1 = *unistring1++;
c2 = *unistring2++;
if(c1 < c2) return -1;
if(c1 > c2) return 1;
if(!c1) return 0;
}
return 0;
}
//-----------------------------------------------------------------------------
const UTF16* dStrrchr(const UTF16* unistring, U32 c)
{
if(!unistring) return NULL;
const UTF16* tmp = unistring + dStrlen(unistring);
while( tmp >= unistring)
{
if(*tmp == c)
return tmp;
tmp--;
}
return NULL;
}
UTF16* dStrrchr(UTF16* unistring, U32 c)
{
const UTF16* str = unistring;
return const_cast<UTF16*>(dStrrchr(str, c));
}
const UTF16* dStrchr(const UTF16* unistring, U32 c)
{
if(!unistring) return NULL;
const UTF16* tmp = unistring;
while ( *tmp && *tmp != c)
tmp++;
return (*tmp == c) ? tmp : NULL;
}
UTF16* dStrchr(UTF16* unistring, U32 c)
{
const UTF16* str = unistring;
return const_cast<UTF16*>(dStrchr(str, c));
}
//-----------------------------------------------------------------------------
const UTF8* getNthCodepoint(const UTF8 *unistring, const U32 n)
{
const UTF8* ret = unistring;
U32 charsseen = 0;
while( *ret && charsseen < n)
{
ret++;
if((*ret & 0xC0) != 0x80)
charsseen++;
}
return ret;
}
/* alternate utf-8 decode impl for speed, no error checking,
left here for your amusement:
U32 codeunit = codepoint + expectedByteCount - 1;
U32 i = 0;
switch(expectedByteCount)
{
case 6: ret |= ( *(codeunit--) & 0x3f ); i++;
case 5: ret |= ( *(codeunit--) & 0x3f ) << (6 * i++);
case 4: ret |= ( *(codeunit--) & 0x3f ) << (6 * i++);
case 3: ret |= ( *(codeunit--) & 0x3f ) << (6 * i++);
case 2: ret |= ( *(codeunit--) & 0x3f ) << (6 * i++);
case 1: ret |= *(codeunit) & byteMask8LUT[expectedByteCount] << (6 * i);
}
*/
//------------------------------------------------------------------------------
// Byte Order Mark functions
bool chompUTF8BOM( const char *inString, char **outStringPtr )
{
*outStringPtr = const_cast<char *>( inString );
U8 bom[4];
dMemcpy( bom, inString, 4 );
bool valid = isValidUTF8BOM( bom );
// This is hackey, but I am not sure the best way to do it at the present.
// The only valid BOM is a UTF8 BOM, which is 3 bytes, even though we read
// 4 bytes because it could possibly be a UTF32 BOM, and we want to provide
// an accurate error message. Perhaps this could be re-worked when more UTF
// formats are supported to have isValidBOM return the size of the BOM, in
// bytes.
if( valid )
(*outStringPtr) += 3; // SEE ABOVE!! -pw
return valid;
}
bool isValidUTF8BOM( U8 bom[4] )
{
// Is it a BOM?
if( bom[0] == 0 )
{
// Could be UTF32BE
if( bom[1] == 0 && bom[2] == 0xFE && bom[3] == 0xFF )
{
Con::warnf( "Encountered a UTF32 BE BOM in this file; Torque does NOT support this file encoding. Use UTF8!" );
return false;
}
return false;
}
else if( bom[0] == 0xFF )
{
// It's little endian, either UTF16 or UTF32
if( bom[1] == 0xFE )
{
if( bom[2] == 0 && bom[3] == 0 )
Con::warnf( "Encountered a UTF32 LE BOM in this file; Torque does NOT support this file encoding. Use UTF8!" );
else
Con::warnf( "Encountered a UTF16 LE BOM in this file; Torque does NOT support this file encoding. Use UTF8!" );
}
return false;
}
else if( bom[0] == 0xFE && bom[1] == 0xFF )
{
Con::warnf( "Encountered a UTF16 BE BOM in this file; Torque does NOT support this file encoding. Use UTF8!" );
return false;
}
else if( bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF )
{
// Can enable this if you want -pw
//Con::printf("Encountered a UTF8 BOM. Torque supports this.");
return true;
}
// Don't print out an error message here, because it will try this with
// every script. -pw
return false;
}

View file

@ -0,0 +1,131 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _UNICODE_H_
#define _UNICODE_H_
#ifndef _TORQUE_TYPES_H_
#include "platform/types.h"
#endif
/// Unicode conversion utility functions
///
/// Some definitions first:
/// - <b>Code Point</b>: a single character of Unicode text. Used to disabmiguate from C char type.
/// - <b>UTF-32</b>: a Unicode encoding format where one code point is always 32 bits wide.
/// This format can in theory contain any Unicode code point that will ever be needed, now or in the future. 4billion+ code points should be enough, right?
/// - <b>UTF-16</b>: a variable length Unicode encoding format where one code point can be
/// either one or two 16-bit code units long.
/// - <b>UTF-8</b>: a variable length Unicode endocing format where one code point can be
/// up to four 8-bit code units long. The first bit of a single byte UTF-8 code point is 0.
/// The first few bits of a multi-byte code point determine the length of the code point.
/// @see http://en.wikipedia.org/wiki/UTF-8
/// - <b>Surrogate Pair</b>: a pair of special UTF-16 code units, that encode a code point
/// that is too large to fit into 16 bits. The surrogate values sit in a special reserved range of Unicode.
/// - <b>Code Unit</b>: a single unit of a variable length Unicode encoded code point.
/// UTF-8 has 8 bit wide code units. UTF-16 has 16 bit wide code units.
/// - <b>BMP</b>: "Basic Multilingual Plane". Unicode values U+0000 - U+FFFF. This range
/// of Unicode contains all the characters for all the languages of the world, that one would
/// usually be interested in. All code points in the BMP are 16 bits wide or less.
/// The current implementation of these conversion functions deals only with the BMP.
/// Any code points above 0xFFFF, the top of the BMP, are replaced with the
/// standard unicode replacement character: 0xFFFD.
/// Any UTF16 surrogates are read correctly, but replaced.
/// UTF-8 code points up to 6 code units wide will be read, but 5+ is illegal,
/// and 4+ is above the BMP, and will be replaced.
/// This means that UTF-8 output is clamped to 3 code units ( bytes ) per code point.
//-----------------------------------------------------------------------------
/// Functions that convert buffers of unicode code points, allocating a buffer.
/// - These functions allocate their own return buffers. You are responsible for
/// calling delete[] on these buffers.
/// - Because they allocate memory, do not use these functions in a tight loop.
/// - These are useful when you need a new long term copy of a string.
UTF16* convertUTF8toUTF16( const UTF8 *unistring);
UTF8* convertUTF16toUTF8( const UTF16 *unistring);
//-----------------------------------------------------------------------------
/// Functions that convert buffers of unicode code points, into a provided buffer.
/// - These functions are useful for working on existing buffers.
/// - These cannot convert a buffer in place. If unistring is the same memory as
/// outbuffer, the behavior is undefined.
/// - The converter clamps output to the BMP (Basic Multilingual Plane) .
/// - Conversion to UTF-8 requires a buffer of 3 bytes (U8's) per character, + 1.
/// - Conversion to UTF-16 requires a buffer of 1 U16 (2 bytes) per character, + 1.
/// - Conversion to UTF-32 requires a buffer of 1 U32 (4 bytes) per character, + 1.
/// - UTF-8 only requires 3 bytes per character in the worst case.
/// - Output is null terminated. Be sure to provide 1 extra byte, U16 or U32 for
/// the null terminator, or you will see truncated output.
/// - If the provided buffer is too small, the output will be truncated.
U32 convertUTF8toUTF16(const UTF8 *unistring, UTF16 *outbuffer, U32 len);
U32 convertUTF16toUTF8( const UTF16 *unistring, UTF8 *outbuffer, U32 len);
//-----------------------------------------------------------------------------
/// Functions that converts one unicode codepoint at a time
/// - Since these functions are designed to be used in tight loops, they do not
/// allocate buffers.
/// - oneUTF8toUTF32() and oneUTF16toUTF32() return the converted Unicode code point
/// in *codepoint, and set *unitsWalked to the \# of code units *codepoint took up.
/// The next Unicode code point should start at *(codepoint + *unitsWalked).
/// - oneUTF32toUTF8() requires a 3 byte buffer, and returns the \# of bytes used.
UTF32 oneUTF8toUTF32( const UTF8 *codepoint, U32 *unitsWalked = NULL);
UTF32 oneUTF16toUTF32(const UTF16 *codepoint, U32 *unitsWalked = NULL);
UTF16 oneUTF32toUTF16(const UTF32 codepoint);
U32 oneUTF32toUTF8( const UTF32 codepoint, UTF8 *threeByteCodeunitBuf);
//-----------------------------------------------------------------------------
/// Functions that calculate the length of unicode strings.
/// - Since calculating the length of a UTF8 string is nearly as expensive as
/// converting it to another format, a dStrlen for UTF8 is not provided here.
/// - If *unistring does not point to a null terminated string of the correct type,
/// the behavior is undefined.
U32 dStrlen(const UTF16 *unistring);
U32 dStrlen(const UTF32 *unistring);
//-----------------------------------------------------------------------------
/// Comparing unicode strings
U32 dStrncmp(const UTF16* unistring1, const UTF16* unistring2, U32 len);
//-----------------------------------------------------------------------------
/// Scanning for characters in unicode strings
UTF16* dStrrchr(UTF16* unistring, U32 c);
const UTF16* dStrrchr(const UTF16* unistring, U32 c);
UTF16* dStrchr(UTF16* unistring, U32 c);
const UTF16* dStrchr(const UTF16* unistring, U32 c);
//-----------------------------------------------------------------------------
/// Functions that scan for characters in a utf8 string.
/// - this is useful for getting a character-wise offset into a UTF8 string,
/// as opposed to a byte-wise offset into a UTF8 string: foo[i]
const UTF8* getNthCodepoint(const UTF8 *unistring, const U32 n);
//------------------------------------------------------------------------------
/// Functions to read and validate UTF BOMs (Byte Order Marker)
/// For reference: http://en.wikipedia.org/wiki/Byte_Order_Mark
bool chompUTF8BOM( const char *inString, char **outStringPtr );
bool isValidUTF8BOM( U8 bom[4] );
#endif // _UNICODE_H_

View file

@ -0,0 +1,62 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _TALGORITHM_H_
#define _TALGORITHM_H_
/// Finds the first matching value within the container
/// returning the the element or last if its not found.
template <class Iterator, class Value>
Iterator find(Iterator first, Iterator last, Value value)
{
while (first != last && *first != value)
++first;
return first;
}
/// Exchanges the values of the two elements.
template <typename T>
inline void swap( T &left, T &right )
{
T temp = right;
right = left;
left = temp;
}
/// Steps thru the elements of an array calling detete for each.
template <class Iterator, class Functor>
void for_each( Iterator first, Iterator last, Functor func )
{
for ( ; first != last; first++ )
func( *first );
}
/// Functor for deleting a pointer.
/// @see for_each
struct delete_pointer
{
template <typename T>
void operator()(T *ptr){ delete ptr;}
};
#endif //_TALGORITHM_H_

View file

@ -0,0 +1,115 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
// [tom, 9/19/2006] Simple hash table. Not intended to replace map<>, but it is
// generally good enough for simple things that you don't need to iterate.
//
// Note: If you move this to another project, you need the updated tSparseArray.h
// as well as hashFunction.cc/h
#include "platform/platform.h"
#include "core/tSparseArray.h"
#include "core/util/hashFunction.h"
#include "core/strings/stringFunctions.h"
#ifndef _TSIMPLEHASHTABLE_H
#define _TSIMPLEHASHTABLE_H
template <class T> class SimpleHashTable : public SparseArray<T>
{
typedef SparseArray<T> Parent;
bool mCaseSensitive;
char mCaseConvBuf[1024];
// [tom, 9/21/2006] This is incredibly lame and adds a pretty big speed penalty
inline const char *caseConv(const char *str)
{
if(mCaseSensitive) return str;
S32 len = dStrlen(str);
if(len >= sizeof(mCaseConvBuf)) len = sizeof(mCaseConvBuf) - 1;
char *dptr = mCaseConvBuf;
const char *sptr = str;
while(*sptr)
{
*dptr = dTolower(*sptr);
++sptr;
++dptr;
}
*dptr = 0;
return mCaseConvBuf;
}
public:
SimpleHashTable(const U32 modulusSize = 64, bool caseSensitive = true) : Parent(modulusSize), mCaseSensitive(caseSensitive)
{
}
void insert(T* pObject, U8 *key, U32 keyLen);
T* remove(U8 *key, U32 keyLen);
T* retreive(U8 *key, U32 keyLen);
void insert(T* pObject, const char *key);
T* remove(const char *key);
T* retreive(const char *key);
};
template <class T> inline void SimpleHashTable<T>::insert(T* pObject, U8 *key, U32 keyLen)
{
Parent::insert(pObject, Torque::hash(key, keyLen, 0));
}
template <class T> inline T* SimpleHashTable<T>::remove(U8 *key, U32 keyLen)
{
return Parent::remove(Torque::hash(key, keyLen, 0));
}
template <class T> inline T* SimpleHashTable<T>::retreive(U8 *key, U32 keyLen)
{
return Parent::retreive(Torque::hash(key, keyLen, 0));
}
template <class T> inline void SimpleHashTable<T>::insert(T* pObject, const char *key)
{
key = caseConv(key);
insert(pObject, (U8 *)key, dStrlen(key));
}
template <class T> T* SimpleHashTable<T>::remove(const char *key)
{
key = caseConv(key);
return remove((U8 *)key, dStrlen(key));
}
template <class T> T* SimpleHashTable<T>::retreive(const char *key)
{
key = caseConv(key);
return retreive((U8 *)key, dStrlen(key));
}
#endif // _TSIMPLEHASHTABLE_H

View file

@ -0,0 +1,152 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _TSPARSEARRAY_H_
#define _TSPARSEARRAY_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _PLATFORMASSERT_H_
#include "platform/platformAssert.h"
#endif
template <class T>
class SparseArray
{
protected:
struct Node {
T* pObject;
U32 key;
Node* next;
};
protected:
U32 mModulus;
Node* mSentryTables;
public:
SparseArray(const U32 modulusSize = 64);
~SparseArray();
void insert(T* pObject, U32 key);
T* remove(U32 key);
T* retreive(U32 key);
void clearTables(); // Note: _deletes_ the objects!
};
template <class T>
inline SparseArray<T>::SparseArray(const U32 modulusSize)
{
AssertFatal(modulusSize > 0, "Error, modulus must be > 0");
mModulus = modulusSize;
mSentryTables = new Node[mModulus];
for (U32 i = 0; i < mModulus; i++)
mSentryTables[i].next = NULL;
}
template <class T>
inline SparseArray<T>::~SparseArray()
{
clearTables();
}
template <class T>
inline void SparseArray<T>::clearTables()
{
for (U32 i = 0; i < mModulus; i++) {
Node* pProbe = mSentryTables[i].next;
while (pProbe != NULL) {
Node* pNext = pProbe->next;
delete pProbe->pObject;
delete pProbe;
pProbe = pNext;
}
}
delete [] mSentryTables;
mSentryTables = NULL;
mModulus = 0;
}
template <class T>
inline void SparseArray<T>::insert(T* pObject, U32 key)
{
U32 insert = key % mModulus;
Node* pNew = new Node;
pNew->pObject = pObject;
pNew->key = key;
pNew->next = mSentryTables[insert].next;
mSentryTables[insert].next = pNew;
#ifdef TORQUE_DEBUG
Node* probe = pNew->next;
while (probe != NULL) {
AssertFatal(probe->key != key, "error, duplicate keys in sparse array!");
probe = probe->next;
}
#endif
}
template <class T>
inline T* SparseArray<T>::remove(U32 key)
{
U32 remove = key % mModulus;
Node* probe = &mSentryTables[remove];
while (probe->next != NULL) {
if (probe->next->key == key) {
Node* remove = probe->next;
T* pReturn = remove->pObject;
probe->next = remove->next;
delete remove;
return pReturn;
}
probe = probe->next;
}
// [tom, 8/19/2006] This assert is also utterly, utterly useless
// AssertFatal(false, "Key didn't exist in the array!");
return NULL;
}
template <class T>
inline T* SparseArray<T>::retreive(U32 key)
{
U32 retrieve = key % mModulus;
Node* probe = &mSentryTables[retrieve];
while (probe->next != NULL) {
if (probe->next->key == key) {
return probe->next->pObject;
}
probe = probe->next;
}
// [tom, 11/16/2005] This assert is utterly, utterly useless
// AssertFatal(false, "Key didn't exist in the array!");
return NULL;
}
#endif //_TSPARSEARRAY_H_

View file

@ -0,0 +1,320 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/strings/stringFunctions.h"
#include "core/tagDictionary.h"
#include "core/stream/stream.h"
namespace {
const char TAG_ASCII_ID[] = "[TAG]";
const char TAG_ASCII_END[] = "[END]";
const char TAG_ASCII_HEADER[] = "// Auto-Generated by TagDictionary class";
const S32 sg_tagDictAsciiUser = 1;
} // namespace
TagDictionary tagDictionary;
TagDictionary::TagDictionary()
{
numBuckets = 29;
defineHashBuckets = (TagEntry **) dMalloc(numBuckets * sizeof(TagEntry *));
idHashBuckets = (TagEntry **) dMalloc(numBuckets * sizeof(TagEntry *));
S32 i;
for(i = 0; i < numBuckets; i++)
{
defineHashBuckets[i] = NULL;
idHashBuckets[i] = NULL;
}
numEntries = 0;
entryChain = NULL;
}
TagDictionary::~TagDictionary()
{
dFree(defineHashBuckets);
dFree(idHashBuckets);
}
//------------------------------------------------------------------------------
static inline S32 hashId(S32 id, S32 tsize)
{
return id % tsize;
}
static inline S32 hashDefine(StringTableEntry define, S32 tsize)
{
return ((S32)((dsize_t)define) >> 2) % tsize;
}
//------------------------------------------------------------------------------
bool TagDictionary::addEntry(S32 value, StringTableEntry define, StringTableEntry string)
{
if(!value)
return false;
//#pragma message "put console prints back"
if(idToDefine(value))
{
AssertWarn(false, avar("Error: id %d already defined to a tag.", value));
//Con::printf("Error: id %d already defined to a tag.", value);
return false;
}
S32 tempTag;
if((tempTag = defineToId(define)) != 0)
{
AssertWarn(false, avar("Error: define %s already defined to tag %d.", define, tempTag));
//Con::printf("Error: define %s already defined to tag %d.", define, tempTag);
return false;
}
TagEntry *newEntry = (TagEntry *) mempool.alloc(sizeof(TagEntry));
newEntry->id = value;
newEntry->define = define;
newEntry->string = string;
numEntries++;
if(numEntries > numBuckets)
{
numBuckets = numBuckets * 2 + 1;
defineHashBuckets = (TagEntry **) dRealloc(defineHashBuckets, numBuckets * sizeof(TagEntry *));
idHashBuckets = (TagEntry **) dRealloc(idHashBuckets, numBuckets * sizeof(TagEntry *));
S32 i;
for(i = 0; i < numBuckets; i++)
{
defineHashBuckets[i] = NULL;
idHashBuckets[i] = NULL;
}
TagEntry *walk = entryChain;
while(walk)
{
S32 index = hashId(walk->id, numBuckets);
walk->idHashLink = idHashBuckets[index];
idHashBuckets[index] = walk;
index = hashDefine(walk->define, numBuckets);
walk->defineHashLink = defineHashBuckets[index];
defineHashBuckets[index] = walk;
walk = walk->chain;
}
}
newEntry->chain = entryChain;
entryChain = newEntry;
S32 index = hashId(newEntry->id, numBuckets);
newEntry->idHashLink = idHashBuckets[index];
idHashBuckets[index] = newEntry;
index = hashDefine(newEntry->define, numBuckets);
newEntry->defineHashLink = defineHashBuckets[index];
defineHashBuckets[index] = newEntry;
return true;
}
//------------------------------------------------------------------------------
bool TagDictionary::writeHeader(Stream& io_sio)
{
char buff[15000];
Vector<S32> v;
TagEntry *walk = entryChain;
while(walk)
{
v.push_back(walk->id);
walk = walk->chain;
}
sortIdVector(v);
io_sio.write( sizeof(TAG_ASCII_HEADER)-1, TAG_ASCII_HEADER);
io_sio.write( 4, "\r\n\r\n");
char exclude[256];
char tempBuf[256];
dSprintf(exclude, sizeof(exclude), "_TD%10.10u_H_", Platform::getVirtualMilliseconds() / 4);
dSprintf(tempBuf, sizeof(tempBuf), "#ifndef %s\r\n", exclude);
io_sio.write(dStrlen(tempBuf), tempBuf);
dSprintf(tempBuf, sizeof(tempBuf), "#define %s\r\n\r\n", exclude);
io_sio.write(dStrlen(tempBuf), tempBuf);
for (U32 i = 0; i < v.size(); i++)
{
dSprintf(buff, sizeof(buff), "#define %s (%d)\r\n", idToDefine(v[i]), v[i]);
io_sio.write(dStrlen(buff), buff);
}
dSprintf(tempBuf, sizeof(tempBuf), "\r\n#endif // %s\r\n", exclude);
io_sio.write(dStrlen(tempBuf), tempBuf);
return (io_sio.getStatus() == Stream::Ok);
}
//------------------------------------------------------------------------------
StringTableEntry TagDictionary::defineToString(StringTableEntry tag)
{
S32 index = hashDefine(tag, numBuckets);
if (index < 0) return NULL;
TagEntry *walk = defineHashBuckets[index];
while(walk)
{
if(walk->define == tag)
return walk->string;
walk = walk->defineHashLink;
}
return NULL;
}
S32 TagDictionary::defineToId(StringTableEntry tag)
{
S32 index = hashDefine(tag, numBuckets);
if (index < 0) return 0;
TagEntry *walk = defineHashBuckets[index];
while(walk)
{
if(walk->define == tag)
return walk->id;
walk = walk->defineHashLink;
}
return 0;
}
StringTableEntry TagDictionary::idToString(S32 id)
{
S32 index = hashId(id, numBuckets);
if (index < 0) return NULL;
TagEntry *walk = idHashBuckets[index];
while(walk)
{
if(walk->id == id)
return walk->string;
walk = walk->idHashLink;
}
return NULL;
}
StringTableEntry TagDictionary::idToDefine(S32 id)
{
S32 index = hashId(id, numBuckets);
if (index < 0) return NULL;
TagEntry *walk = idHashBuckets[index];
while(walk)
{
if(walk->id == id)
return walk->define;
walk = walk->idHashLink;
}
return NULL;
}
//------------------------------------------------------------------------------
void TagDictionary::findIDs(Vector<S32>& out_v,
const S32 in_minID,
const S32 in_maxID )
{
//locate all IDs that lie in between minID and maxID
TagEntry *walk = entryChain;
while(walk)
{
if(walk->id > in_minID && walk->id < in_maxID)
out_v.push_back(walk->id);
walk = walk->chain;
}
sortIdVector(out_v);
}
//------------------------------------------------------------------------------
void TagDictionary::findStrings(Vector<S32>& out_v, const char* in_pPattern)
{
//locate all strings that match the pattern
//
TagEntry *walk = entryChain;
while(walk)
{
if (match(in_pPattern, walk->string))
out_v.push_back(walk->id);
walk = walk->chain;
}
sortIdVector(out_v);
}
//------------------------------------------------------------------------------
void TagDictionary::findDefines(Vector<S32>& out_v, const char* in_pPattern)
{
//locate all define strings that match the pattern and add their ID
//to the given vector
//
TagEntry *walk = entryChain;
while(walk)
{
if (match(in_pPattern, walk->define))
out_v.push_back(walk->id);
walk = walk->chain;
}
sortIdVector(out_v);
}
//------------------------------------------------------------------------------
bool TagDictionary::match(const char* pattern, const char* str)
{
//quick and dirty recursive DOS-style wild-card string matcher
//
switch (*pattern) {
case '\0':
return !*str;
case '*':
return match(pattern+1, str) || *str && match(pattern, str+1);
case '?':
return *str && match(pattern+1, str+1);
default:
return (*pattern == *str) && match(pattern+1, str+1);
}
}
//------------------------------------------------------------------------------
static int QSORT_CALLBACK idCompare(const void *in_p1, const void *in_p2)
{
return *((S32 *) in_p1) - *((S32 *) in_p2);
}
void TagDictionary::sortIdVector(Vector<S32>& out_v)
{
dQsort(out_v.address(), out_v.size(), sizeof(S32), idCompare);
}

View file

@ -0,0 +1,83 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _TAGDICTIONARY_H_
#define _TAGDICTIONARY_H_
#ifndef _STRINGTABLE_H_
#include "core/stringTable.h"
#endif
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
class Stream;
class TagDictionary
{
struct TagEntry
{
S32 id;
StringTableEntry define;
StringTableEntry string;
TagEntry *chain; // for linear traversal
TagEntry *defineHashLink;
TagEntry *idHashLink;
};
TagEntry **defineHashBuckets;
TagEntry **idHashBuckets;
TagEntry *entryChain;
DataChunker mempool;
S32 numBuckets;
S32 numEntries;
bool match(const char* pattern, const char* str);
void sortIdVector(Vector<S32>& out_v);
public:
TagDictionary();
~TagDictionary();
//IO functions
//
bool writeHeader(Stream &);
// String/Define retrieval and search functions...
//
bool addEntry(S32 value, StringTableEntry define, StringTableEntry string);
StringTableEntry defineToString(StringTableEntry tag);
StringTableEntry idToString(S32 tag);
StringTableEntry idToDefine(S32 tag);
S32 defineToId(StringTableEntry tag);
// get IDs such that minID < IDs < maxID
void findIDs( Vector<S32> &v, const S32 minID, const S32 maxID );
void findStrings( Vector<S32> &v, const char *pattern);
void findDefines( Vector<S32> &v, const char *pattern);
};
extern TagDictionary tagDictionary;
#endif //_TAGDICTIONARY_H_

View file

@ -0,0 +1,113 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/threadStatic.h"
//-----------------------------------------------------------------------------
// Statics
U32 _TorqueThreadStatic::mListIndex = 0;
_TorqueThreadStaticReg *_TorqueThreadStaticReg::smFirst = NULL;
//-----------------------------------------------------------------------------
inline Vector<TorqueThreadStaticList> &_TorqueThreadStaticReg::getThreadStaticListVector()
{
// This function assures that the static vector of ThreadStatics will get initialized
// before first use.
static Vector<TorqueThreadStaticList> sTorqueThreadStaticVec( __FILE__, __LINE__ );
return sTorqueThreadStaticVec;
}
//-----------------------------------------------------------------------------
// Destructor, size should == 1 otherwise someone didn't clean up, or someone
// did horrible things to list index 0
_TorqueThreadStaticReg::~_TorqueThreadStaticReg()
{
AssertFatal( getThreadStaticListVector().size() == 1, "Destruction of static list was not performed on program exit" );
}
//-----------------------------------------------------------------------------
void _TorqueThreadStaticReg::destroyInstances()
{
// mThreadStaticInstances[0] does *not* need to be deallocated
// because all members of the list are pointers to static memory
while( getThreadStaticListVector().size() > 1 )
{
// Delete the members of this list
while( getThreadStaticListVector().last().size() )
{
_TorqueThreadStatic *biscuit = getThreadStaticListVector().last().first();
// Erase the vector entry
getThreadStaticListVector().last().pop_front();
// And finally the memory
delete biscuit;
}
// Remove the entry from the list of lists
getThreadStaticListVector().pop_back();
}
}
//-----------------------------------------------------------------------------
void _TorqueThreadStaticReg::destroyInstance( TorqueThreadStaticList *instanceList )
{
AssertFatal( instanceList != &getThreadStaticListVector().first(), "Cannot delete static instance list index 0" );
while( instanceList->size() )
{
_TorqueThreadStatic *biscuit = getThreadStaticListVector().last().first();
// Erase the vector entry
getThreadStaticListVector().last().pop_front();
// And finally the memory
delete biscuit;
}
getThreadStaticListVector().erase( instanceList );
}
//-----------------------------------------------------------------------------
TorqueThreadStaticListHandle _TorqueThreadStaticReg::spawnThreadStaticsInstance()
{
AssertFatal( getThreadStaticListVector().size() > 0, "List is not initialized somehow" );
// Add a new list of static instances
getThreadStaticListVector().increment();
// Copy mThreadStaticInstances[0] (master copy) into new memory, and
// pass it back.
for( int i = 0; i < getThreadStaticListVector()[0].size(); i++ )
{
getThreadStaticListVector().last().push_back( getThreadStaticListVector()[0][i]->_createInstance() );
}
// Return list index of newly allocated static instance list
return &getThreadStaticListVector().last();
}

View file

@ -0,0 +1,216 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _TORQUETHREADSTATIC_H_
#define _TORQUETHREADSTATIC_H_
#include "core/util/tVector.h"
//-----------------------------------------------------------------------------
// TorqueThreadStatic Base Class
class _TorqueThreadStatic
{
friend class _TorqueThreadStaticReg;
private:
#ifdef TORQUE_ENABLE_THREAD_STATIC_METRICS
U32 mHitCount;
#endif
protected:
static U32 mListIndex;
virtual _TorqueThreadStatic *_createInstance() const = 0;
public:
_TorqueThreadStatic()
#ifdef TORQUE_ENABLE_THREAD_STATIC_METRICS
: mHitCount( 0 )
#endif
{ };
static const U32 getListIndex(){ return mListIndex; }
virtual void *getMemInstPtr() = 0;
virtual const dsize_t getMemInstSize() const = 0;
#ifdef TORQUE_ENABLE_THREAD_STATIC_METRICS
_TorqueThreadStatic *_chainHit() { mHitCount++; return this; }
const U32 &trackHit() { return ++mHitCount; }
const U32 &getHitCount() const { return mHitCount; }
#endif
};
// Typedef
typedef VectorPtr<_TorqueThreadStatic *> TorqueThreadStaticList;
typedef TorqueThreadStaticList * TorqueThreadStaticListHandle;
//-----------------------------------------------------------------------------
// Auto-registration class and manager of the instances
class _TorqueThreadStaticReg
{
// This will manage all of the thread static registrations
static _TorqueThreadStaticReg *smFirst;
_TorqueThreadStaticReg *mNext;
// This is a vector of vectors which will store instances of thread static
// variables. mThreadStaticInsances[0] will be the list of the initial values
// of the statics, and then indexing for instanced versions will start at 1
//
// Note that the list of instances in mThreadStaticInstances[0] does not, and
// must not get 'delete' called on it, because all members of the list are
// pointers to statically allocated memory. All other lists will be contain
// pointers to dynamically allocated memory, and will need to be freed upon
// termination.
//
// So this was originally a static data member, however that caused problems because
// I was relying on static initialization order to make sure the vector got initialized
// *before* any static instance of this class was created via macro. By wrapping the
// static in a function, I can be assured that the static memory will get initialized
// before it is modified.
static Vector<TorqueThreadStaticList> &getThreadStaticListVector();
public:
/// Constructor
_TorqueThreadStaticReg( _TorqueThreadStatic *ttsInitial )
{
// Link this entry into the list
mNext = smFirst;
smFirst = this;
// Create list 0 (initial values) if it doesn't exist
if( getThreadStaticListVector().empty() )
getThreadStaticListVector().increment();
// Set the index of the thread static for lookup
ttsInitial->mListIndex = getThreadStaticListVector()[0].size();
// Add the static to the initial value list
getThreadStaticListVector()[0].push_back( ttsInitial );
}
virtual ~_TorqueThreadStaticReg();
// Accessors
static const TorqueThreadStaticList &getStaticList( const U32 idx = 0 )
{
AssertFatal( getThreadStaticListVector().size() > idx, "Out of range static list" );
return getThreadStaticListVector()[idx];
}
static void destroyInstances();
static void destroyInstance( TorqueThreadStaticList *instanceList );
static const _TorqueThreadStaticReg *getFirst() { return smFirst; }
const _TorqueThreadStaticReg *getNext() const { return mNext; }
/// Spawn another copy of the ThreadStatics and pass back the id
static TorqueThreadStaticListHandle spawnThreadStaticsInstance();
};
//-----------------------------------------------------------------------------
// Template class that will get used as a base for the thread statics
template<class T>
class TorqueThreadStatic : public _TorqueThreadStatic
{
// The reg object will want access to mInstance
friend class _TorqueThreadStaticReg;
private:
T mInstance;
public:
TorqueThreadStatic( T instanceVal ) : mInstance( instanceVal ) {}
virtual void *getMemInstPtr() { return &mInstance; }
// I am not sure these are needed, and I don't want to create confusing-to-debug code
#if 0
// Operator overloads
operator T*() { return &mInstance; }
operator T*() const { return &mInstance; }
operator const T*() const { return &mInstance; }
bool operator ==( const T &l ) const { return mInstance == l; }
bool operator !=( const T &l ) const { return mInstance != l; }
T &operator =( const T &l ) { mInstance = l; return mInstance; }
#endif // if0
};
//-----------------------------------------------------------------------------
// If ThreadStatic behavior is not enabled, than the macros will resolve
// to regular, static memory
#ifndef TORQUE_ENABLE_THREAD_STATICS
#define DITTS( type, name, initialvalue ) static type name = initialvalue
#define ATTS( name ) name
#else // TORQUE_ENABLE_THREAD_STATICS is defined
//-----------------------------------------------------------------------------
// Declare TorqueThreadStatic, and initialize it's value
//
// This macro would be used in a .cpp file to declare a ThreadStatic
#define DITTS(type, name, initalvalue) \
class _##name##TorqueThreadStatic : public TorqueThreadStatic<type> \
{ \
protected:\
virtual _TorqueThreadStatic *_createInstance() const { return new _##name##TorqueThreadStatic; } \
public: \
_##name##TorqueThreadStatic() : TorqueThreadStatic<type>( initalvalue ) {} \
virtual const dsize_t getMemInstSize() const { return sizeof( type ); } \
type &_cast() { return *reinterpret_cast<type *>( getMemInstPtr() ); } \
const type &_const_cast() const { return *reinterpret_cast<const type *>( getMemInstPtr() ); } \
}; \
static _##name##TorqueThreadStatic name##TorqueThreadStatic; \
static _TorqueThreadStaticReg _##name##TTSReg( reinterpret_cast<_TorqueThreadStatic *>( & name##TorqueThreadStatic ) )
//-----------------------------------------------------------------------------
// Access TorqueThreadStatic
// NOTE: TEMPDEF is there as a temporary place holder for however we want to get the index of the currently running
// thread or whatever.
#define TEMPDEF 0
#ifdef TORQUE_ENABLE_THREAD_STATIC_METRICS
// Access counting macro
# define ATTS_(name, idx) \
(reinterpret_cast< _##name##TorqueThreadStatic *>( _TorqueThreadStaticReg::getStaticList( idx )[ _##name##TorqueThreadStatic::getListIndex() ]->_chainHit() )->_cast() )
// Const access counting macro
# define CATTS_(name, idx) \
(reinterpret_cast< _##name##TorqueThreadStatic *>( _TorqueThreadStaticReg::getStaticList( idx )[ _##name##TorqueThreadStatic::getListIndex() ]->_chainHit() )->_const_cast() )
#else
// Regular access macro
# define ATTS_(name, idx) \
(reinterpret_cast< _##name##TorqueThreadStatic *>( _TorqueThreadStaticReg::getStaticList( idx )[ _##name##TorqueThreadStatic::getListIndex() ] )->_cast() )
// Const access macro
# define CATTS_(name, idx) \
(reinterpret_cast< _##name##TorqueThreadStatic *>( _TorqueThreadStaticReg::getStaticList( idx )[ _##name##TorqueThreadStatic::getListIndex() ] )->_const_cast() )
#endif // TORQUE_ENABLE_THREAD_STATIC_METRICS
#define ATTS(name) ATTS_(name, TEMPDEF)
#define CATTS(name) CATTS_(name, TEMPDEF)
#endif // TORQUE_ENABLE_THREAD_STATICS
#endif

View file

@ -0,0 +1,613 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/tokenizer.h"
#include "platform/platform.h"
#include "core/stream/fileStream.h"
#include "core/strings/stringFunctions.h"
#include "core/util/safeDelete.h"
Tokenizer::Tokenizer()
{
dMemset(mFileName, 0, sizeof(mFileName));
mpBuffer = NULL;
mBufferSize = 0;
mStartPos = 0;
mCurrPos = 0;
mTokenIsQuoted = false;
dMemset(mCurrTokenBuffer, 0, sizeof(mCurrTokenBuffer));
mTokenIsCurrent = false;
mSingleTokens = NULL;
VECTOR_SET_ASSOCIATION(mLinePositions);
}
Tokenizer::~Tokenizer()
{
clear();
}
bool Tokenizer::openFile(const char* pFileName)
{
AssertFatal(mFileName[0] == '\0', "Reuse of Tokenizers not allowed!");
FileStream* pStream = new FileStream;
if (pStream->open(pFileName, Torque::FS::File::Read) == false)
{
delete pStream;
return false;
}
dStrcpy(mFileName, pFileName);
mBufferSize = pStream->getStreamSize();
mpBuffer = new char[mBufferSize];
pStream->read(mBufferSize, mpBuffer);
pStream->close();
delete pStream;
reset();
buildLinePositions();
return true;
}
bool Tokenizer::openFile(Stream* pStream)
{
mBufferSize = pStream->getStreamSize();
mpBuffer = new char[mBufferSize];
pStream->read(mBufferSize, mpBuffer);
reset();
buildLinePositions();
return true;
}
void Tokenizer::setBuffer(const char* buffer, U32 bufferSize)
{
if (mpBuffer)
{
SAFE_DELETE_ARRAY(mpBuffer);
mBufferSize = 0;
}
mBufferSize = bufferSize;
mpBuffer = new char[mBufferSize + 1];
dStrcpy(mpBuffer, buffer);
reset();
buildLinePositions();
}
void Tokenizer::setSingleTokens(const char* singleTokens)
{
if (mSingleTokens)
SAFE_DELETE(mSingleTokens);
if (singleTokens)
mSingleTokens = dStrdup(singleTokens);
}
bool Tokenizer::reset()
{
mStartPos = 0;
mCurrPos = 0;
mTokenIsQuoted = false;
dMemset(mCurrTokenBuffer, 0, sizeof(mCurrTokenBuffer));
mTokenIsCurrent = false;
return true;
}
bool Tokenizer::clear()
{
// Delete our buffer
if (mpBuffer)
SAFE_DELETE_ARRAY(mpBuffer);
// Reset the buffer size
mBufferSize = 0;
// Reset our active data
reset();
// Clear our line positions
mLinePositions.clear();
// Reset our file name
dMemset(mFileName, 0, 1024);
// Wipe the single tokens
setSingleTokens(NULL);
return true;
}
bool Tokenizer::setCurrentPos(U32 pos)
{
mCurrPos = pos;
mTokenIsCurrent = false;
return advanceToken(true);
}
void Tokenizer::buildLinePositions()
{
if (mBufferSize == 0)
return;
// We can safely assume that the first line is at position 0
mLinePositions.push_back(0);
U32 currPos = 0;
while (currPos + 1 < mBufferSize)
{
// Windows line ending
if (mpBuffer[currPos] == '\r' && mpBuffer[currPos + 1] == '\n')
{
currPos += 2;
mLinePositions.push_back(currPos);
}
// Not sure if this ever happens but just in case
else if (mpBuffer[currPos] == '\n' && mpBuffer[currPos + 1] == '\r')
{
currPos += 2;
mLinePositions.push_back(currPos);
}
// Unix line endings should only have a single line break character
else if (mpBuffer[currPos] == '\n' || mpBuffer[currPos] == '\r')
{
currPos++;
mLinePositions.push_back(currPos);
}
else
currPos++;
}
}
U32 Tokenizer::getLinePosition(const U32 pos, U32 lowIndex, S32 highIndex)
{
// If we have one or less lines then
// the result is easy
if (mLinePositions.size() <= 1)
return 0;
// Now that we know we have at least one position
// we can do a quick test against the last line
if (pos >= mLinePositions.last())
return mLinePositions.size() - 1;
// If this is the beginning of the search
// set a good starting point (the middle)
if (highIndex < 0)
highIndex = mLinePositions.size() - 1;
// Just in case bad values got handed in
if (lowIndex > highIndex)
lowIndex = highIndex;
// Compute our test index (middle)
U32 testIndex = (lowIndex + highIndex) / 2;
// Make sure that our test indices are valid
if (testIndex >= mLinePositions.size() ||
testIndex + 1 >= mLinePositions.size())
return mLinePositions.size() - 1;
// See if we are already at the right line
if (pos >= mLinePositions[testIndex] && pos < mLinePositions[testIndex + 1])
return testIndex;
if (pos < mLinePositions[testIndex])
highIndex = testIndex;
else
lowIndex = testIndex;
return getLinePosition(pos, lowIndex, highIndex);
}
U32 Tokenizer::getCurrentLine()
{
// Binary search for the line number whose
// position is equal to or lower than the
// current position
return getLinePosition(mStartPos);
}
U32 Tokenizer::getTokenLineOffset()
{
U32 lineNumber = getCurrentLine();
if (lineNumber >= mLinePositions.size())
return 0;
U32 linePosition = mLinePositions[lineNumber];
if (linePosition >= mStartPos)
return 0;
return mStartPos - linePosition;
}
bool Tokenizer::advanceToken(const bool crossLine, const bool assertAvail)
{
if (mTokenIsCurrent == true)
{
AssertFatal(mCurrTokenBuffer[0] != '\0', "No token, but marked as current?");
mTokenIsCurrent = false;
return true;
}
U32 currPosition = 0;
mCurrTokenBuffer[0] = '\0';
mTokenIsQuoted = false;
// Store the beginning of the previous advance
// and the beginning of the current advance
mStartPos = mCurrPos;
while (mCurrPos < mBufferSize)
{
char c = mpBuffer[mCurrPos];
bool cont = true;
if (mSingleTokens && dStrchr(mSingleTokens, c))
{
if (currPosition == 0)
{
mCurrTokenBuffer[currPosition++] = c;
mCurrPos++;
cont = false;
break;
}
else
{
// End of token
cont = false;
}
}
else
{
switch (c)
{
case ' ':
case '\t':
if (currPosition == 0)
{
// Token hasn't started yet...
mCurrPos++;
}
else
{
// End of token
mCurrPos++;
cont = false;
}
break;
case '\r':
case '\n':
if (crossLine == true)
{
// Windows line ending
if (mpBuffer[mCurrPos] == '\r' && mpBuffer[mCurrPos + 1] == '\n')
mCurrPos += 2;
// Not sure if this ever happens but just in case
else if (mpBuffer[mCurrPos] == '\n' && mpBuffer[mCurrPos + 1] == '\r')
mCurrPos += 2;
// Unix line endings should only have a single line break character
else
mCurrPos++;
}
else
{
cont = false;
break;
}
break;
default:
if (c == '\"' || c == '\'')
{
// Quoted token
U32 startLine = getCurrentLine();
mCurrPos++;
// Store the beginning of the token
mStartPos = mCurrPos;
while (mpBuffer[mCurrPos] != c)
{
AssertISV(mCurrPos < mBufferSize,
avar("End of file before quote closed. Quote started: (%s: %d)",
getFileName(), startLine));
AssertISV((mpBuffer[mCurrPos] != '\n' && mpBuffer[mCurrPos] != '\r'),
avar("End of line reached before end of quote. Quote started: (%s: %d)",
getFileName(), startLine));
mCurrTokenBuffer[currPosition++] = mpBuffer[mCurrPos++];
}
mTokenIsQuoted = true;
mCurrPos++;
cont = false;
}
else if (c == '/' && mpBuffer[mCurrPos+1] == '/')
{
// Line quote...
if (currPosition == 0)
{
// continue to end of line, then let crossLine determine on the next pass
while (mCurrPos < mBufferSize && (mpBuffer[mCurrPos] != '\n' && mpBuffer[mCurrPos] != '\r'))
mCurrPos++;
}
else
{
// This is the end of the token. Continue to EOL
while (mCurrPos < mBufferSize && (mpBuffer[mCurrPos] != '\n' && mpBuffer[mCurrPos] != '\r'))
mCurrPos++;
cont = false;
}
}
else
{
// If this is the first non-token character then store the
// beginning of the token
if (currPosition == 0)
mStartPos = mCurrPos;
mCurrTokenBuffer[currPosition++] = c;
mCurrPos++;
}
break;
}
}
if (cont == false)
break;
}
mCurrTokenBuffer[currPosition] = '\0';
if (assertAvail == true)
AssertISV(currPosition != 0, avar("Error parsing: %s at or around line: %d", getFileName(), getCurrentLine()));
if (mCurrPos == mBufferSize)
return false;
return true;
}
bool Tokenizer::regressToken(const bool crossLine)
{
if (mTokenIsCurrent == true)
{
AssertFatal(mCurrTokenBuffer[0] != '\0', "No token, but marked as current?");
mTokenIsCurrent = false;
return true;
}
U32 currPosition = 0;
mCurrTokenBuffer[0] = '\0';
mTokenIsQuoted = false;
// Store the beginning of the previous advance
// and the beginning of the current advance
mCurrPos = mStartPos;
// Back up to the first character of the previous token
mStartPos--;
while (mStartPos > 0)
{
char c = mpBuffer[mStartPos];
bool cont = true;
if (mSingleTokens && dStrchr(mSingleTokens, c))
{
if (currPosition == 0)
{
mCurrTokenBuffer[currPosition++] = c;
mStartPos--;
cont = false;
break;
}
else
{
// End of token
cont = false;
}
}
else
{
switch (c)
{
case ' ':
case '\t':
if (currPosition == 0)
{
// Token hasn't started yet...
mStartPos--;
}
else
{
// End of token
mStartPos--;
cont = false;
}
break;
case '\r':
case '\n':
if (crossLine == true && currPosition == 0)
{
// Windows line ending
if (mStartPos > 0 && mpBuffer[mStartPos] == '\r' && mpBuffer[mStartPos - 1] == '\n')
mStartPos -= 2;
// Not sure if this ever happens but just in case
else if (mStartPos > 0 && mpBuffer[mStartPos] == '\n' && mpBuffer[mStartPos - 1] == '\r')
mStartPos -= 2;
// Unix line endings should only have a single line break character
else
mStartPos--;
}
else
{
cont = false;
break;
}
break;
default:
if (c == '\"' || c == '\'')
{
// Quoted token
U32 endLine = getCurrentLine();
mStartPos--;
while (mpBuffer[mStartPos] != c)
{
AssertISV(mStartPos < 0,
avar("Beginning of file reached before finding begin quote. Quote ended: (%s: %d)",
getFileName(), endLine));
mCurrTokenBuffer[currPosition++] = mpBuffer[mStartPos--];
}
mTokenIsQuoted = true;
mStartPos--;
cont = false;
}
else if (c == '/' && mStartPos > 0 && mpBuffer[mStartPos - 1] == '/')
{
// Line quote...
// Clear out anything saved already
currPosition = 0;
mStartPos -= 2;
}
else
{
mCurrTokenBuffer[currPosition++] = c;
mStartPos--;
}
break;
}
}
if (cont == false)
break;
}
mCurrTokenBuffer[currPosition] = '\0';
// Reveres the token
for (U32 i = 0; i < currPosition / 2; i++)
{
char c = mCurrTokenBuffer[i];
mCurrTokenBuffer[i] = mCurrTokenBuffer[currPosition - i - 1];
mCurrTokenBuffer[currPosition - i - 1] = c;
}
mStartPos++;
if (mStartPos == mCurrPos)
return false;
return true;
}
bool Tokenizer::tokenAvailable()
{
// Note: this implies that when advanceToken(false) fails, it must cap the
// token buffer.
//
return mCurrTokenBuffer[0] != '\0';
}
const char* Tokenizer::getToken() const
{
return mCurrTokenBuffer;
}
const char* Tokenizer::getNextToken()
{
advanceToken(true);
return getToken();
}
bool Tokenizer::tokenICmp(const char* pCmp) const
{
return dStricmp(mCurrTokenBuffer, pCmp) == 0;
}
bool Tokenizer::findToken(U32 start, const char* pCmp)
{
// Move to the start
setCurrentPos(start);
// In case the first token is what we are looking for
if (tokenICmp(pCmp))
return true;
// Loop through the file and see if the token exists
while (advanceToken(true))
{
if (tokenICmp(pCmp))
return true;
}
return false;
}
bool Tokenizer::findToken(const char* pCmp)
{
return findToken(0, pCmp);
}
bool Tokenizer::endOfFile()
{
if (mCurrPos < mBufferSize)
return false;
else
return true;
}

View file

@ -0,0 +1,109 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _TOKENIZER_H_
#define _TOKENIZER_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _STREAM_H_
#include "core/stream/stream.h"
#endif
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
class SizedStream;
class Tokenizer
{
public:
enum
{
MaxTokenSize = 1023
};
private:
char mFileName[1024];
char* mpBuffer;
U32 mBufferSize;
S32 mCurrPos;
S32 mStartPos;
bool mTokenIsQuoted;
char mCurrTokenBuffer[MaxTokenSize + 1];
bool mTokenIsCurrent;
char* mSingleTokens;
Vector<U32> mLinePositions;
public:
Tokenizer();
~Tokenizer();
bool openFile(const char* pFileName);
bool openFile(Stream* pStream);
void setBuffer(const char* buffer, U32 bufferSize);
void setSingleTokens(const char* singleTokens);
void buildLinePositions();
bool advanceToken(const bool crossLine, const bool assertAvailable = false);
bool regressToken(const bool crossLine);
bool tokenAvailable();
const char* getToken() const;
const char* getNextToken();
bool tokenICmp(const char* pCmp) const;
bool tokenIsQuoted() const { return mTokenIsQuoted; }
bool findToken(const char* pCmp);
bool findToken(U32 start, const char* pCmp);
const char* getFileName() const { return mFileName; }
U32 getLinePosition(const U32 pos, U32 lowIndex = 0, S32 highIndex = -1);
U32 getCurrentLine();
U32 getTokenLineOffset();
U32 getCurrentPos() const { return mCurrPos; }
bool setCurrentPos(U32 pos);
// Resets our active data but leaves the buffer intact
bool reset();
// Clears the buffer and resets the active data
bool clear();
bool endOfFile();
};
#endif //_TOKENIZER_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,131 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _AUTOPTR_H_
#define _AUTOPTR_H_
#ifndef _TYPETRAITS_H_
# include "platform/typetraits.h"
#endif
template<class T>
struct AutoPtrRef
{
T* _ptr;
AutoPtrRef(T *ptr)
: _ptr(ptr)
{}
};
/// A simple smart pointer.
/// An extended version of std::auto_ptr which supports a deletion policy.
/// The delete policy indicates how the ptr is to be deleted. DeleteSingle,
/// the default, is used to delete individual objects. DeleteArray can
/// can be used to delete arrays.
/// <code>
/// AutoPtr<Object> ptr(new Object);
/// AutoPtr<Object,DeleteSingle> ptr(new Object);
/// AutoPtr<Object,DeleteArray> ptr(new Object[10]);
/// </code>
/// AutoPtrs do not perform reference counting and assume total ownership
/// of any object assigned to them. Assigning an AutoPtr to another transfers
/// that ownership and resets the source AutoPtr to 0.
template<class T, class P = DeleteSingle>
class AutoPtr
{
public:
typedef T ValueType;
explicit AutoPtr(T *ptr = 0): _ptr(ptr) {}
~AutoPtr()
{
P::destroy(_ptr);
}
// Copy constructors
AutoPtr(AutoPtr &rhs): _ptr(rhs.release()) {}
template<class U>
AutoPtr(AutoPtr<U,P> &rhs): _ptr(rhs.release()) { }
/// Transfer ownership, any object currently be referenced is deleted and
/// rhs is set to 0.
AutoPtr& operator= (AutoPtr &rhs)
{
reset(rhs.release());
return *this;
}
template<class U>
AutoPtr& operator= (AutoPtr<U,P> &rhs)
{
reset(rhs.release());
return *this;
}
// Access
T* ptr() const { return _ptr; }
T& operator*() const { return *_ptr; }
T* operator->() const { return _ptr; }
T& operator[](size_t index) { return (_ptr)[index]; }
/// Release ownership of the object without deleting it.
T* release()
{
T* tmp(_ptr);
_ptr = 0;
return tmp;
}
/// Equivalent to *this = (T*)ptr, except that operator=(T*) isn't provided for.
void reset(T* ptr = 0)
{
if (_ptr != ptr)
{
P::destroy(_ptr);
_ptr = ptr;
}
}
// Conversion to/from ref type
AutoPtr(AutoPtrRef<T> ref): _ptr(ref._ptr) {}
AutoPtr& operator= (AutoPtrRef<T> ref)
{
reset(ref._ptr);
return *this;
}
bool isNull() const { return _ptr == NULL; }
bool isValid() const { return !isNull(); }
template<class U>
operator AutoPtrRef<U>() { return AutoPtrRef<U>(release()); }
template<class U>
operator AutoPtr<U,P>() { return AutoPtr<U,P>(release()); }
private:
T *_ptr;
};
#endif

View file

@ -0,0 +1,158 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/util/byteBuffer.h"
namespace Torque
{
class PrivateBBData
{
public:
PrivateBBData()
: refCount( 1 ),
dataSize( 0 ),
data( NULL )
{
}
U32 refCount; ///< Reference count
U32 dataSize; ///< Length of buffer
U8 *data; ///< Our data buffer
};
//--------------------------------------
ByteBuffer::ByteBuffer()
{
_data = new PrivateBBData;
_data->dataSize = 0;
_data->data = NULL;
}
ByteBuffer::ByteBuffer(U8 *dataPtr, U32 bufferSize)
{
_data = new PrivateBBData;
_data->dataSize = bufferSize;
_data->data = new U8[bufferSize];
dMemcpy( _data->data, dataPtr, bufferSize );
}
ByteBuffer::ByteBuffer(U32 bufferSize)
{
_data = new PrivateBBData;
_data->dataSize = bufferSize;
_data->data = new U8[bufferSize];
}
ByteBuffer::ByteBuffer(const ByteBuffer &theBuffer)
{
_data = theBuffer._data;
_data->refCount++;
}
ByteBuffer &ByteBuffer::operator=(const ByteBuffer &theBuffer)
{
_data = theBuffer._data;
_data->refCount++;
return *this;
}
ByteBuffer::~ByteBuffer()
{
if (!--_data->refCount)
{
delete [] _data->data;
delete _data;
_data = NULL;
}
}
void ByteBuffer::setBuffer(U8 *dataPtr, U32 bufferSize, bool copyData)
{
U8 *newData = dataPtr;
if ( copyData )
{
newData = new U8[bufferSize];
dMemcpy( newData, dataPtr, bufferSize );
}
delete [] _data->data;
_data->data = newData;
_data->dataSize = bufferSize;
}
void ByteBuffer::resize(U32 newBufferSize)
{
U8 *newData = new U8[newBufferSize];
U32 copyLen = getMin( newBufferSize, _data->dataSize );
dMemcpy( newData, _data->data, copyLen );
delete [] _data->data;
_data->data = newData;
_data->dataSize = newBufferSize;
}
void ByteBuffer::appendBuffer(const U8 *dataBuffer, U32 bufferSize)
{
U32 start = _data->dataSize;
resize(start + bufferSize);
dMemcpy(_data->data + start, dataBuffer, bufferSize);
}
U32 ByteBuffer::getBufferSize() const
{
return _data->dataSize;
}
U8 *ByteBuffer::getBuffer()
{
return _data->data;
}
const U8 *ByteBuffer::getBuffer() const
{
return _data->data;
}
ByteBuffer ByteBuffer::getCopy() const
{
return ByteBuffer( _data->data, _data->dataSize );
}
void ByteBuffer::clear()
{
dMemset(_data->data, 0, _data->dataSize);
}
}

View file

@ -0,0 +1,85 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _BYTEBUFFER_H_
#define _BYTEBUFFER_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
namespace Torque
{
class PrivateBBData;
class ByteBuffer
{
public:
ByteBuffer();
/// Create a ByteBuffer from a chunk of memory.
ByteBuffer(U8 *dataPtr, U32 bufferSize);
/// Create a ByteBuffer of the specified size.
ByteBuffer(U32 bufferSize);
/// Copy constructor
ByteBuffer(const ByteBuffer &theBuffer);
ByteBuffer &operator=(const ByteBuffer &theBuffer);
~ByteBuffer();
/// Set the ByteBuffer to point to a new chunk of memory.
void setBuffer(U8 *dataPtr, U32 bufferSize, bool copyData);
/// Resize the buffer.
void resize(U32 newBufferSize);
/// Appends the specified buffer to the end of the byte buffer.
void appendBuffer(const U8 *dataBuffer, U32 bufferSize);
/// Appends the specified ByteBuffer to the end of this byte buffer.
void appendBuffer(const ByteBuffer &theBuffer)
{
appendBuffer(theBuffer.getBuffer(), theBuffer.getBufferSize());
}
U32 getBufferSize() const;
U8 *getBuffer();
const U8 *getBuffer() const;
/// Copy the data in the buffer.
ByteBuffer getCopy() const;
/// Clear the buffer.
void clear();
private:
PrivateBBData *_data;
};
}
#endif

View file

@ -0,0 +1,25 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef BYTESWAP
#define BYTESWAP(x, y) x = x ^ y; y = x ^ y; x = x ^y;
#endif //defined(BYTESWAP)

View file

@ -0,0 +1,42 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/util/swizzle.h"
namespace Swizzles
{
dsize_t _bgra[] = { 2, 1, 0, 3 };
dsize_t _bgr[] = { 2, 1, 0 };
dsize_t _rgb[] = { 0, 1, 2 };
dsize_t _argb[] = { 3, 0, 1, 2 };
dsize_t _rgba[] = { 0, 1, 2, 3 };
dsize_t _abgr[] = { 3, 2, 1, 0 };
Swizzle<U8, 4> bgra( _bgra );
Swizzle<U8, 3> bgr( _bgr );
Swizzle<U8, 3> rgb( _rgb );
Swizzle<U8, 4> argb( _argb );
Swizzle<U8, 4> rgba( _rgba );
Swizzle<U8, 4> abgr( _abgr );
NullSwizzle<U8, 4> null;
}

View file

@ -0,0 +1,61 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _UTIL_DELEGATE_H_
#define _UTIL_DELEGATE_H_
#include "core/util/FastDelegate.h"
/// @def Delegate
/// The macro which abstracts the details of the delegate implementation.
#define Delegate fastdelegate::FastDelegate
/// @typedef DelegateMemento
/// An opaque structure which can hold an arbitary delegate.
/// @see Delegate
typedef fastdelegate::DelegateMemento DelegateMemento;
template<class T>
class DelegateRemapper : public DelegateMemento
{
public:
DelegateRemapper() : mOffset(0) {}
void set(T * t, const DelegateMemento & memento)
{
SetMementoFrom(memento);
if (m_pthis)
mOffset = ((int)m_pthis) - ((int)t);
}
void rethis(T * t)
{
if (m_pthis)
m_pthis = (fastdelegate::detail::GenericClass *)(mOffset + (int)t);
}
protected:
int mOffset;
};
#endif // _UTIL_DELEGATE_H_

View file

@ -0,0 +1,108 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _DXT5nm_SWIZZLE_H_
#define _DXT5nm_SWIZZLE_H_
#include "core/util/swizzle.h"
#include "core/util/byteswap.h"
class DXT5nmSwizzle : public Swizzle<U8, 4>
{
public:
DXT5nmSwizzle() : Swizzle<U8, 4>( NULL ) {};
virtual void InPlace( void *memory, const dsize_t size ) const
{
AssertFatal( size % 4 == 0, "Bad buffer size for DXT5nm Swizzle" );
volatile U8 *u8Mem = reinterpret_cast<U8 *>( memory );
for( int i = 0; i < size >> 2; i++ )
{
// g = garbage byte
// Input: [X|Y|Z|g] (rgba)
// Output: [g|Y|0xFF|X] (bgra)
BYTESWAP( u8Mem[0], u8Mem[3] ); // Store X in Alpha
*u8Mem ^= *u8Mem; // 0 the garbage bit
u8Mem[2] |= 0xFF; // Set Red to 1.0
u8Mem += 4;
}
}
virtual void ToBuffer( void *destination, const void *source, const dsize_t size ) const
{
AssertFatal( size % 4 == 0, "Bad buffer size for DXT5nm Swizzle" );
volatile const U8 *srcU8 = reinterpret_cast<const U8 *>( source );
volatile U8 *dstU8 = reinterpret_cast<U8 *>( destination );
for( int i = 0; i < size >> 2; i++ )
{
// g = garbage byte
// Input: [X|Y|Z|g] (rgba)
// Output: [g|Y|0xFF|X] (bgra)
*dstU8++ = 0; // 0 garbage bit
*dstU8++ = srcU8[1]; // Copy Y into G
*dstU8++ |= 0xFF; // Set Red to 1.0
*dstU8++ = srcU8[0]; // Copy X into Alpha
srcU8 += 4;
}
}
};
class DXT5nmSwizzleUp24t32 : public Swizzle<U8, 3>
{
public:
DXT5nmSwizzleUp24t32() : Swizzle<U8, 3>( NULL ) {};
virtual void InPlace( void *memory, const dsize_t size ) const
{
AssertISV( false, "Cannot swizzle in place a 24->32 bit swizzle." );
}
virtual void ToBuffer( void *destination, const void *source, const dsize_t size ) const
{
AssertFatal( size % 3 == 0, "Bad buffer size for DXT5nm Swizzle" );
const int pixels = size / 3;
volatile const U8 *srcU8 = reinterpret_cast<const U8 *>( source );
volatile U8 *dstU8 = reinterpret_cast<U8 *>( destination );
// destination better damn well be the right size
for( int i = 0; i < pixels; i++ )
{
// g = garbage byte
// Input: [X|Y|Z|g] (rgba)
// Output: [g|Y|0xFF|X] (bgra)
*dstU8++ = 0; // 0 garbage bit
*dstU8++ = srcU8[1]; // Copy Y into G
*dstU8++ |= 0xFF; // Set Red to 1.0
*dstU8++ = srcU8[0]; // Copy X into Alpha
srcU8 += 3;
}
}
};
#endif

View file

@ -0,0 +1,141 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _ENDIAN_H_
#define _ENDIAN_H_
#ifndef _TORQUE_TYPES_H_
#include "platform/types.h"
#endif
//------------------------------------------------------------------------------
// Endian conversions
inline U8 endianSwap(const U8 in_swap)
{
return in_swap;
}
inline S8 endianSwap(const S8 in_swap)
{
return in_swap;
}
/**
Convert the byte ordering on the U16 to and from big/little endian format.
@param in_swap Any U16
@returns swapped U16.
*/
inline U16 endianSwap(const U16 in_swap)
{
return U16(((in_swap >> 8) & 0x00ff) |
((in_swap << 8) & 0xff00));
}
inline S16 endianSwap(const S16 in_swap)
{
return S16(endianSwap(U16(in_swap)));
}
/**
Convert the byte ordering on the U32 to and from big/little endian format.
@param in_swap Any U32
@returns swapped U32.
*/
inline U32 endianSwap(const U32 in_swap)
{
return U32(((in_swap >> 24) & 0x000000ff) |
((in_swap >> 8) & 0x0000ff00) |
((in_swap << 8) & 0x00ff0000) |
((in_swap << 24) & 0xff000000));
}
inline S32 endianSwap(const S32 in_swap)
{
return S32(endianSwap(U32(in_swap)));
}
inline U64 endianSwap(const U64 in_swap)
{
U32 *inp = (U32 *) &in_swap;
U64 ret;
U32 *outp = (U32 *) &ret;
outp[0] = endianSwap(inp[1]);
outp[1] = endianSwap(inp[0]);
return ret;
}
inline S64 endianSwap(const S64 in_swap)
{
return S64(endianSwap(U64(in_swap)));
}
inline F32 endianSwap(const F32 in_swap)
{
U32 result = endianSwap(* ((U32 *) &in_swap) );
return * ((F32 *) &result);
}
inline F64 endianSwap(const F64 in_swap)
{
U64 result = endianSwap(* ((U64 *) &in_swap) );
return * ((F64 *) &result);
}
//------------------------------------------------------------------------------
// Endian conversions
#ifdef TORQUE_LITTLE_ENDIAN
#define TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(type) \
inline type convertHostToLEndian(type i) { return i; } \
inline type convertLEndianToHost(type i) { return i; } \
inline type convertHostToBEndian(type i) { return endianSwap(i); } \
inline type convertBEndianToHost(type i) { return endianSwap(i); }
#elif defined(TORQUE_BIG_ENDIAN)
#define TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(type) \
inline type convertHostToLEndian(type i) { return endianSwap(i); } \
inline type convertLEndianToHost(type i) { return endianSwap(i); } \
inline type convertHostToBEndian(type i) { return i; } \
inline type convertBEndianToHost(type i) { return i; }
#else
#error "Endian define not set!"
#endif
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(U8)
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(S8)
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(U16)
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(S16)
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(U32)
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(S32)
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(U64)
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(S64)
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(F32)
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(F64)
#endif

View file

@ -0,0 +1,27 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef MakeFourCC
#define MakeFourCC(ch0, ch1, ch2, ch3) \
((U32)(U8)(ch0) | ((U32)(U8)(ch1) << 8) | \
((U32)(U8)(ch2) << 16) | ((U32)(U8)(ch3) << 24 ))
#endif //defined(MakeFourCC)

View file

@ -0,0 +1,271 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
// Borrowed from: http://burtleburtle.net/bob/hash/doobs.html
//
// Original code by:
//
// By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this
// code any way you wish, private, educational, or commercial. It's free.
#include "platform/platform.h"
#include "core/util/hashFunction.h"
namespace Torque
{
#define hashsize(n) ((U32)1<<(n))
#define hashmask(n) (hashsize(n)-1)
/*
--------------------------------------------------------------------
mix -- mix 3 32-bit values reversibly.
For every delta with one or two bits set, and the deltas of all three
high bits or all three low bits, whether the original value of a,b,c
is almost all zero or is uniformly distributed,
* If mix() is run forward or backward, at least 32 bits in a,b,c
have at least 1/4 probability of changing.
* If mix() is run forward, every bit of c will change between 1/3 and
2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.)
mix() was built out of 36 single-cycle latency instructions in a
structure that could supported 2x parallelism, like so:
a -= b;
a -= c; x = (c>>13);
b -= c; a ^= x;
b -= a; x = (a<<8);
c -= a; b ^= x;
c -= b; x = (b>>13);
...
Unfortunately, superscalar Pentiums and Sparcs can't take advantage
of that parallelism. They've also turned some of those single-cycle
latency instructions into multi-cycle latency instructions. Still,
this is the fastest good hash I could find. There were about 2^^68
to choose from. I only looked at a billion or so.
--------------------------------------------------------------------
*/
#define mix(a,b,c) \
{ \
a -= b; a -= c; a ^= (c>>13); \
b -= c; b -= a; b ^= (a<<8); \
c -= a; c -= b; c ^= (b>>13); \
a -= b; a -= c; a ^= (c>>12); \
b -= c; b -= a; b ^= (a<<16); \
c -= a; c -= b; c ^= (b>>5); \
a -= b; a -= c; a ^= (c>>3); \
b -= c; b -= a; b ^= (a<<10); \
c -= a; c -= b; c ^= (b>>15); \
}
/*
--------------------------------------------------------------------
hash() -- hash a variable-length key into a 32-bit value
k : the key (the unaligned variable-length array of bytes)
len : the length of the key, counting by bytes
initval : can be any 4-byte value
Returns a 32-bit value. Every bit of the key affects every bit of
the return value. Every 1-bit and 2-bit delta achieves avalanche.
About 6*len+35 instructions.
The best hash table sizes are powers of 2. There is no need to do
mod a prime (mod is sooo slow!). If you need less than 32 bits,
use a bitmask. For example, if you need only 10 bits, do
h = (h & hashmask(10));
In which case, the hash table should have hashsize(10) elements.
If you are hashing n strings (U8 **)k, do it like this:
for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);
By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this
code any way you wish, private, educational, or commercial. It's free.
See http://burtleburtle.net/bob/hash/evahash.html
Use for hash table lookup, or anything where one collision in 2^^32 is
acceptable. Do NOT use for cryptographic purposes.
--------------------------------------------------------------------
*/
U32 hash(register const U8 *k, register U32 length, register U32 initval)
{
register U32 a,b,c,len;
/* Set up the internal state */
len = length;
a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
c = initval; /* the previous hash value */
/*---------------------------------------- handle most of the key */
while (len >= 12)
{
a += (k[0] +((U32)k[1]<<8) +((U32)k[2]<<16) +((U32)k[3]<<24));
b += (k[4] +((U32)k[5]<<8) +((U32)k[6]<<16) +((U32)k[7]<<24));
c += (k[8] +((U32)k[9]<<8) +((U32)k[10]<<16)+((U32)k[11]<<24));
mix(a,b,c);
k += 12; len -= 12;
}
/*------------------------------------- handle the last 11 bytes */
c += length;
switch(len) /* all the case statements fall through */
{
case 11: c+=((U32)k[10]<<24);
case 10: c+=((U32)k[9]<<16);
case 9 : c+=((U32)k[8]<<8);
/* the first byte of c is reserved for the length */
case 8 : b+=((U32)k[7]<<24);
case 7 : b+=((U32)k[6]<<16);
case 6 : b+=((U32)k[5]<<8);
case 5 : b+=k[4];
case 4 : a+=((U32)k[3]<<24);
case 3 : a+=((U32)k[2]<<16);
case 2 : a+=((U32)k[1]<<8);
case 1 : a+=k[0];
/* case 0: nothing left to add */
}
mix(a,b,c);
/*-------------------------------------------- report the result */
return c;
}
/*
--------------------------------------------------------------------
mix -- mix 3 64-bit values reversibly.
mix() takes 48 machine instructions, but only 24 cycles on a superscalar
machine (like Intel's new MMX architecture). It requires 4 64-bit
registers for 4::2 parallelism.
All 1-bit deltas, all 2-bit deltas, all deltas composed of top bits of
(a,b,c), and all deltas of bottom bits were tested. All deltas were
tested both on random keys and on keys that were nearly all zero.
These deltas all cause every bit of c to change between 1/3 and 2/3
of the time (well, only 113/400 to 287/400 of the time for some
2-bit delta). These deltas all cause at least 80 bits to change
among (a,b,c) when the mix is run either forward or backward (yes it
is reversible).
This implies that a hash using mix64 has no funnels. There may be
characteristics with 3-bit deltas or bigger, I didn't test for
those.
--------------------------------------------------------------------
*/
#define mix64(a,b,c) \
{ \
a -= b; a -= c; a ^= (c>>43); \
b -= c; b -= a; b ^= (a<<9); \
c -= a; c -= b; c ^= (b>>8); \
a -= b; a -= c; a ^= (c>>38); \
b -= c; b -= a; b ^= (a<<23); \
c -= a; c -= b; c ^= (b>>5); \
a -= b; a -= c; a ^= (c>>35); \
b -= c; b -= a; b ^= (a<<49); \
c -= a; c -= b; c ^= (b>>11); \
a -= b; a -= c; a ^= (c>>12); \
b -= c; b -= a; b ^= (a<<18); \
c -= a; c -= b; c ^= (b>>22); \
}
/*
--------------------------------------------------------------------
hash64() -- hash a variable-length key into a 64-bit value
k : the key (the unaligned variable-length array of bytes)
len : the length of the key, counting by bytes
level : can be any 8-byte value
Returns a 64-bit value. Every bit of the key affects every bit of
the return value. No funnels. Every 1-bit and 2-bit delta achieves
avalanche. About 41+5len instructions.
The best hash table sizes are powers of 2. There is no need to do
mod a prime (mod is sooo slow!). If you need less than 64 bits,
use a bitmask. For example, if you need only 10 bits, do
h = (h & hashmask(10));
In which case, the hash table should have hashsize(10) elements.
If you are hashing n strings (ub1 **)k, do it like this:
for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);
By Bob Jenkins, Jan 4 1997. bob_jenkins@burtleburtle.net. You may
use this code any way you wish, private, educational, or commercial,
but I would appreciate if you give me credit.
See http://burtleburtle.net/bob/hash/evahash.html
Use for hash table lookup, or anything where one collision in 2^^64
is acceptable. Do NOT use for cryptographic purposes.
--------------------------------------------------------------------
*/
U64 hash64( register const U8 *k, register U32 length, register U64 initval )
{
register U64 a,b,c,len;
/* Set up the internal state */
len = length;
a = b = initval; /* the previous hash value */
c = 0x9e3779b97f4a7c13LL; /* the golden ratio; an arbitrary value */
/*---------------------------------------- handle most of the key */
while (len >= 24)
{
a += (k[0] +((U64)k[ 1]<< 8)+((U64)k[ 2]<<16)+((U64)k[ 3]<<24)
+((U64)k[4 ]<<32)+((U64)k[ 5]<<40)+((U64)k[ 6]<<48)+((U64)k[ 7]<<56));
b += (k[8] +((U64)k[ 9]<< 8)+((U64)k[10]<<16)+((U64)k[11]<<24)
+((U64)k[12]<<32)+((U64)k[13]<<40)+((U64)k[14]<<48)+((U64)k[15]<<56));
c += (k[16] +((U64)k[17]<< 8)+((U64)k[18]<<16)+((U64)k[19]<<24)
+((U64)k[20]<<32)+((U64)k[21]<<40)+((U64)k[22]<<48)+((U64)k[23]<<56));
mix64(a,b,c);
k += 24; len -= 24;
}
/*------------------------------------- handle the last 23 bytes */
c += length;
switch(len) /* all the case statements fall through */
{
case 23: c+=((U64)k[22]<<56);
case 22: c+=((U64)k[21]<<48);
case 21: c+=((U64)k[20]<<40);
case 20: c+=((U64)k[19]<<32);
case 19: c+=((U64)k[18]<<24);
case 18: c+=((U64)k[17]<<16);
case 17: c+=((U64)k[16]<<8);
/* the first byte of c is reserved for the length */
case 16: b+=((U64)k[15]<<56);
case 15: b+=((U64)k[14]<<48);
case 14: b+=((U64)k[13]<<40);
case 13: b+=((U64)k[12]<<32);
case 12: b+=((U64)k[11]<<24);
case 11: b+=((U64)k[10]<<16);
case 10: b+=((U64)k[ 9]<<8);
case 9: b+=((U64)k[ 8]);
case 8: a+=((U64)k[ 7]<<56);
case 7: a+=((U64)k[ 6]<<48);
case 6: a+=((U64)k[ 5]<<40);
case 5: a+=((U64)k[ 4]<<32);
case 4: a+=((U64)k[ 3]<<24);
case 3: a+=((U64)k[ 2]<<16);
case 2: a+=((U64)k[ 1]<<8);
case 1: a+=((U64)k[ 0]);
/* case 0: nothing left to add */
}
mix64(a,b,c);
/*-------------------------------------------- report the result */
return c;
}
} // namespace

View file

@ -0,0 +1,39 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _HASHFUNCTION_H_
#define _HASHFUNCTION_H_
#ifndef _TORQUE_TYPES_H_
#include "platform/types.h"
#endif
namespace Torque
{
extern U32 hash(register const U8 *k, register U32 length, register U32 initval);
extern U64 hash64(register const U8 *k, register U32 length, register U64 initval);
}
#endif // _HASHFUNCTION_H_

View file

@ -0,0 +1,202 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/util/journal/journal.h"
#include "core/stream/fileStream.h"
#include "core/util/safeDelete.h"
#include "console/console.h"
#include <stdlib.h>
//-----------------------------------------------------------------------------
Journal::FuncDecl* Journal::_FunctionList;
Stream *Journal::mFile;
Journal::Mode Journal::_State = Journal::StopState;
U32 Journal::_Count;
bool Journal::_Dispatching = false;
Journal Journal::smInstance;
//-----------------------------------------------------------------------------
Journal::~Journal()
{
if( mFile )
Stop();
}
//-----------------------------------------------------------------------------
Journal::Functor* Journal::_create(Id id)
{
for (FuncDecl* ptr = _FunctionList; ptr; ptr = ptr->next)
if (ptr->id == id)
return ptr->create();
return 0;
}
Journal::Id Journal::_getFunctionId(VoidPtr ptr,VoidMethod method)
{
for (FuncDecl* itr = _FunctionList; itr; itr = itr->next)
if (itr->match(ptr,method))
return itr->id;
return 0;
}
void Journal::_removeFunctionId(VoidPtr ptr,VoidMethod method)
{
FuncDecl ** itr = &_FunctionList;
do
{
if((*itr)->match(ptr, method))
{
// Unlink and break.
FuncDecl* decl = *itr;
idPool().free( decl->id );
*itr = (*itr)->next;
delete decl;
return;
}
// Advance to next...
itr = &((*itr)->next);
}
while(*itr);
}
void Journal::_start()
{
}
void Journal::_finish()
{
if (_State == PlayState)
--_Count;
else {
U32 pos = mFile->getPosition();
mFile->setPosition(0);
mFile->write(++_Count);
mFile->setPosition(pos);
}
}
void Journal::Record(const char * file)
{
if (_State == DisabledState)
{
Con::errorf("//---------------------------------------------//");
Con::errorf("Journal::Record() - Cannot record a journal after GuiCanvas or NetConnection creation!");
Con::errorf("To record before canvas/netConnection creation, run %s with the following arguments: -jSave %s",
Platform::getExecutableName(), file);
Con::errorf("//---------------------------------------------//");
return;
}
if (_State == StopState)
{
_Count = 0;
mFile = new FileStream();
if( ((FileStream*)mFile)->open(file, Torque::FS::File::Write) )
{
mFile->write(_Count);
_State = RecordState;
}
else
{
AssertWarn(false,"Journal: Could not create journal file");
Con::errorf("Journal: Could not create journal file '%s'", file);
}
}
}
void Journal::Play(const char * file)
{
if (_State == DisabledState)
{
Con::errorf("//---------------------------------------------//");
Con::errorf("Journal::Play() - Cannot playback a journal after GuiCanvas or NetConnection creation!");
Con::errorf("To playback before canvas/netConnection creation, run %s with the following arguments: -jPlay %s",
Platform::getExecutableName(), file);
Con::errorf("//---------------------------------------------//");
return;
}
if (_State == StopState)
{
SAFE_DELETE(mFile);
mFile = new FileStream();
if( ((FileStream*)mFile)->open(file, Torque::FS::File::Read) )
{
mFile->read(&_Count);
_State = PlayState;
}
else
{
AssertWarn(false,"Journal: Could not open journal file");
Con::errorf("Journal: Could not open journal file '%s'", file);
}
}
}
void Journal::Stop()
{
AssertFatal(mFile, "Journal::Stop - no file stream open!");
SAFE_DELETE( mFile );
_State = StopState;
}
bool Journal::PlayNext()
{
if (_State == PlayState) {
_start();
Id id;
mFile->read(&id);
Functor* jrn = _create(id);
AssertFatal(jrn,"Journal: Undefined function found in journal");
jrn->read(mFile);
_finish();
_Dispatching = true;
jrn->dispatch();
_Dispatching = false;
delete jrn;
if (_Count)
return true;
Stop();
//debugBreak();
}
return false;
}
void Journal::Disable()
{
if (_State == StopState)
_State = DisabledState;
}

View file

@ -0,0 +1,635 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _UTIL_JOURNAL_JOURNAL_H_
#define _UTIL_JOURNAL_JOURNAL_H_
#include "platform/platform.h"
#include "core/stream/stream.h"
#include "util/returnType.h"
#include "core/stream/ioHelper.h"
#include "core/idGenerator.h"
/// Journaling System.
///
/// The journaling system is used to record external events and
/// non-deterministic values from an execution run in order that the
/// run may be re-played for debugging purposes. The journaling
/// system is integrated into the platform library, though not all
/// platform calls are journaled.
///
/// File system calls are not journaled, so any modified files must
/// be reset to their original state before playback. Only a single
/// journal can be recored or played back at a time.
///
/// For the journals to play back correctly, journal events cannot
/// be triggered during the processing of another event.
class Journal
{
Journal() {}
~Journal();
static Journal smInstance;
typedef U32 Id;
typedef void* VoidPtr;
typedef void (Journal::*VoidMethod)();
/// Functor base classes
struct Functor
{
Functor() {}
virtual ~Functor() {}
virtual void read(Stream *s) = 0;
virtual void dispatch() = 0;
};
/// Multiple argument function functor specialization
template <class T>
struct FunctorDecl: public Functor {
typedef void(*FuncPtr)();
FuncPtr ptr;
FunctorDecl(FuncPtr p): ptr(p) {}
void read(Stream *file) {}
void dispatch() { (*ptr)(); }
};
template <class A,class B,class C,class D,class E,class F,class G,class H,class I,class J>
struct FunctorDecl< void(*)(A,B,C,D,E,F,G,H,I,J) >: public Functor {
typedef void(*FuncPtr)(A,B,C,D,E,F,G,H,I,J);
FuncPtr ptr; A a; B b; C c; D d; E e; F f; G g; H h; I i; J j;
FunctorDecl(FuncPtr p): ptr(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h,i,j); }
void dispatch() { (*ptr)(a,b,c,d,e,f,g,h,i,j); }
};
template <class A,class B,class C,class D,class E,class F,class G,class H,class I>
struct FunctorDecl< void(*)(A,B,C,D,E,F,G,H,I) >: public Functor {
typedef void(*FuncPtr)(A,B,C,D,E,F,G,H,I);
FuncPtr ptr; A a; B b; C c; D d; E e; F f; G g; H h; I i;
FunctorDecl(FuncPtr p): ptr(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h,i); }
void dispatch() { (*ptr)(a,b,c,d,e,f,g,h,i); }
};
template <class A,class B,class C,class D,class E,class F,class G,class H>
struct FunctorDecl< void(*)(A,B,C,D,E,F,G,H) >: public Functor {
typedef void(*FuncPtr)(A,B,C,D,E,F,G,H);
FuncPtr ptr; A a; B b; C c; D d; E e; F f; G g; H h;
FunctorDecl(FuncPtr p): ptr(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h); }
void dispatch() { (*ptr)(a,b,c,d,e,f,g,h); }
};
template <class A,class B,class C,class D,class E,class F,class G>
struct FunctorDecl< void(*)(A,B,C,D,E,F,G) >: public Functor {
typedef void(*FuncPtr)(A,B,C,D,E,F,G);
FuncPtr ptr; A a; B b; C c; D d; E e; F f; G g;
FunctorDecl(FuncPtr p): ptr(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g); }
void dispatch() { (*ptr)(a,b,c,d,e,f,g); }
};
template <class A,class B,class C,class D,class E,class F>
struct FunctorDecl< void(*)(A,B,C,D,E,F) >: public Functor {
typedef void(*FuncPtr)(A,B,C,D,E,F);
FuncPtr ptr; A a; B b; C c; D d; E e; F f;
FunctorDecl(FuncPtr p): ptr(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f); }
void dispatch() { (*ptr)(a,b,c,d,e,f); }
};
template <class A,class B,class C,class D,class E>
struct FunctorDecl< void(*)(A,B,C,D,E) >: public Functor {
typedef void(*FuncPtr)(A,B,C,D,E);
FuncPtr ptr; A a; B b; C c; D d; E e;
FunctorDecl(FuncPtr p): ptr(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e); }
void dispatch() { (*ptr)(a,b,c,d,e); }
};
template <class A,class B,class C,class D>
struct FunctorDecl< void(*)(A,B,C,D) >: public Functor {
typedef void(*FuncPtr)(A,B,C,D);
FuncPtr ptr; A a; B b; C c; D d;
FunctorDecl(FuncPtr p): ptr(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b,c,d); }
void dispatch() { (*ptr)(a,b,c,d); }
};
template <class A,class B,class C>
struct FunctorDecl< void(*)(A,B,C) >: public Functor {
typedef void(*FuncPtr)(A,B,C);
FuncPtr ptr; A a; B b; C c;
FunctorDecl(FuncPtr p): ptr(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b,c); }
void dispatch() { (*ptr)(a,b,c); }
};
template <class A,class B>
struct FunctorDecl< void(*)(A,B) >: public Functor {
typedef void(*FuncPtr)(A,B);
FuncPtr ptr; A a; B b;
FunctorDecl(FuncPtr p): ptr(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b); }
void dispatch() { (*ptr)(a,b); }
};
template <class A>
struct FunctorDecl< void(*)(A) >: public Functor {
typedef void(*FuncPtr)(A);
FuncPtr ptr; A a;
FunctorDecl(FuncPtr p): ptr(p) {}
void read(Stream *file) { IOHelper::reads(file,a); }
void dispatch() { (*ptr)(a); }
};
// Multiple argument object member function functor specialization
template <class T,class U>
struct MethodDecl: public Functor {
typedef T ObjPtr;
typedef U MethodPtr;
ObjPtr obj; MethodPtr method;
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
void read(Stream *file) {}
void dispatch() { (obj->*method)(); }
};
template <class T,class A,class B,class C,class D,class E,class F,class G,class H,class I,class J>
struct MethodDecl<T*, void(T::*)(A,B,C,D,E,F,G,H,I,J) >: public Functor {
typedef T* ObjPtr;
typedef void(T::*MethodPtr)(A,B,C,D,E,F,G,H,I,J);
ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e; F f; G g; H h; I i; J j;
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h,i,j); }
void dispatch() { (obj->*method)(a,b,c,d,e,f,g,h,i,j); }
};
template <class T,class A,class B,class C,class D,class E,class F,class G,class H,class I>
struct MethodDecl<T*, void(T::*)(A,B,C,D,E,F,G,H,I) >: public Functor {
typedef T* ObjPtr;
typedef void(T::*MethodPtr)(A,B,C,D,E,F,G,H,I);
ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e; F f; G g; H h; I i;
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h,i); }
void dispatch() { (obj->*method)(a,b,c,d,e,f,g,h,i); }
};
template <class T,class A,class B,class C,class D,class E,class F,class G,class H>
struct MethodDecl<T*, void(T::*)(A,B,C,D,E,F,G,H) >: public Functor {
typedef T* ObjPtr;
typedef void(T::*MethodPtr)(A,B,C,D,E,F,G,H);
ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e; F f; G g; H h;
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h); }
void dispatch() { (obj->*method)(a,b,c,d,e,f,g,h); }
};
template <class T,class A,class B,class C,class D,class E,class F,class G>
struct MethodDecl<T*, void(T::*)(A,B,C,D,E,F,G) >: public Functor {
typedef T* ObjPtr;
typedef void(T::*MethodPtr)(A,B,C,D,E,F,G);
ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e; F f; G g;
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g); }
void dispatch() { (obj->*method)(a,b,c,d,e,f,g); }
};
template <class T,class A,class B,class C,class D,class E,class F>
struct MethodDecl<T*, void(T::*)(A,B,C,D,E,F) >: public Functor {
typedef T* ObjPtr;
typedef void(T::*MethodPtr)(A,B,C,D,E,F);
ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e; F f;
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f); }
void dispatch() { (obj->*method)(a,b,c,d,e,f); }
};
template <class T,class A,class B,class C,class D,class E>
struct MethodDecl<T*, void(T::*)(A,B,C,D,E) >: public Functor {
typedef T* ObjPtr;
typedef void(T::*MethodPtr)(A,B,C,D,E);
ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e;
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e); }
void dispatch() { (obj->*method)(a,b,c,d,e); }
};
template <class T,class A,class B,class C,class D>
struct MethodDecl<T*, void(T::*)(A,B,C,D) >: public Functor {
typedef T* ObjPtr;
typedef void(T::*MethodPtr)(A,B,C,D);
ObjPtr obj; MethodPtr method; A a; B b; C c; D d;
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b,c,d); }
void dispatch() { (obj->*method)(a,b,c,d); }
};
template <class T,class A,class B,class C>
struct MethodDecl<T*, void(T::*)(A,B,C) >: public Functor {
typedef T* ObjPtr;
typedef void(T::*MethodPtr)(A,B,C);
ObjPtr obj; MethodPtr method; A a; B b; C c;
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b,c); }
void dispatch() { (obj->*method)(a,b,c); }
};
template <class T,class A,class B>
struct MethodDecl<T*, void(T::*)(A,B) >: public Functor {
typedef T* ObjPtr;
typedef void(T::*MethodPtr)(A,B);
ObjPtr obj; MethodPtr method; A a; B b;
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
void read(Stream *file) { IOHelper::reads(file,a,b); }
void dispatch() { (obj->*method)(a,b); }
};
template <class T,class A>
struct MethodDecl<T*, void(T::*)(A) >: public Functor {
typedef T* ObjPtr;
typedef void(T::*MethodPtr)(A);
ObjPtr obj; MethodPtr method; A a;
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
void read(Stream *file) { IOHelper::reads(file,a); }
void dispatch() { (obj->*method)(a); }
};
// Function declarations
struct FuncDecl {
FuncDecl* next;
Id id;
virtual ~FuncDecl() {}
virtual bool match(VoidPtr,VoidMethod) const = 0;
virtual Functor* create() const = 0;
};
template<typename T>
struct FuncRep: public FuncDecl {
typename T::FuncPtr function;
virtual bool match(VoidPtr ptr,VoidMethod) const {
return function == (typename T::FuncPtr)ptr;
}
T* create() const { return new T(function); };
};
template<typename T>
struct MethodRep: public FuncDecl {
typename T::ObjPtr obj;
typename T::MethodPtr method;
virtual bool match(VoidPtr ptr,VoidMethod func) const {
return obj == (typename T::ObjPtr)ptr && method == (typename T::MethodPtr)func;
}
T* create() const { return new T(obj,method); };
};
static FuncDecl* _FunctionList;
static inline IdGenerator &idPool()
{
static IdGenerator _IdPool(1, 65535);
return _IdPool;
}
static U32 _Count;
static Stream *mFile;
static enum Mode {
StopState, PlayState, RecordState, DisabledState
} _State;
static bool _Dispatching;
static Functor* _create(Id id);
static void _start();
static void _finish();
static Id _getFunctionId(VoidPtr ptr,VoidMethod method);
static void _removeFunctionId(VoidPtr ptr,VoidMethod method);
public:
static void Record(const char * file);
static void Play(const char * file);
static bool PlayNext();
static void Stop();
static void Disable();
/// Returns true if in recording mode.
static inline bool IsRecording() {
return _State == RecordState;
}
/// Returns true if in play mode.
static inline bool IsPlaying() {
return _State == PlayState;
}
/// Returns true if a function is being dispatched
static inline bool IsDispatching() {
return _Dispatching;
}
template<typename T>
static void Read(T *v)
{
AssertFatal(IsPlaying(), "Journal::Read - not playing right now.");
bool r = mFile->read(v);
AssertFatal(r, "Journal::Read - failed to read!");
}
static bool Read(U32 size, void *buffer)
{
AssertFatal(IsPlaying(), "Journal::Read - not playing right now.");
bool r = mFile->read(size, buffer);
AssertFatal(r, "Journal::Read - failed to read!");
return r;
}
static void ReadString(char str[256])
{
AssertFatal(IsPlaying(), "Journal::ReadString - not playing right now.");
mFile->readString(str);
}
template<typename T>
static void Write(const T &v)
{
AssertFatal(IsRecording(), "Journal::Write - not recording right now.");
bool r = mFile->write(v);
AssertFatal(r, "Journal::Write - failed to write!");
}
static bool Write(U32 size, void *buffer)
{
AssertFatal(IsRecording(), "Journal::Write - not recording right now.");
bool r = mFile->write(size, buffer);
AssertFatal(r, "Journal::Write - failed to write!");
return r;
}
static void WriteString(const char str[256])
{
AssertFatal(IsRecording(), "Journal::WriteString - not recording right now.");
mFile->writeString(str);
}
/// Register a function with the journalling system.
template<typename T>
static void DeclareFunction(T func) {
if (!_getFunctionId((VoidPtr)func,0)) {
FuncRep<FunctorDecl<T> >* decl = new FuncRep<FunctorDecl<T> >;
decl->function = func;
decl->id = idPool().alloc();
decl->next = _FunctionList;
_FunctionList = decl;
}
}
template<typename T, typename U>
static void DeclareFunction(T obj, U method)
{
if (!_getFunctionId((VoidPtr)obj,(VoidMethod)method)) {
MethodRep<MethodDecl<T,U> >* decl = new MethodRep<MethodDecl<T,U> >;
decl->obj = obj;
decl->method = method;
decl->id = idPool().alloc();
decl->next = _FunctionList;
_FunctionList = decl;
}
}
template<typename T, typename U>
static void RemoveFunction(T obj, U method)
{
_removeFunctionId((VoidPtr)obj,(VoidMethod)method);
}
/// Journal a function's return value. The return value of the
/// function is stored into the journal and retrieved during
/// playback. During playback the function is not executed.
#define Method(Func,Arg1,Arg2) \
static typename ReturnType<Func>::ValueType Result Arg1 { \
typename ReturnType<Func>::ValueType value; \
if (_Dispatching) \
return; \
if (_State == PlayState) { \
_start(); \
IOHelper::reads(mFile,value); \
_finish(); \
return value; \
} \
_Dispatching = true; \
value = (*func) Arg2; \
_Dispatching = false; \
if (_State == RecordState) { \
_start(); \
IOHelper::writes(mFile,value); \
_finish(); \
} \
return value; \
}
template<class Func,class A,class B,class C,class D,class E,class F, class G, class H, class I, class J>
Method(Func,(Func func,A a,B b,C c,D d,E e,F f, G g, H h, I i, J j),(a,b,c,d,e,f,g,h,i,j))
template<class Func,class A,class B,class C,class D,class E,class F, class G, class H, class I>
Method(Func,(Func func,A a,B b,C c,D d,E e,F f, G g, H h, I i),(a,b,c,d,e,f,g,h,i))
template<class Func,class A,class B,class C,class D,class E,class F, class G, class H>
Method(Func,(Func func,A a,B b,C c,D d,E e,F f, G g, H h),(a,b,c,d,e,f,g,h))
template<class Func,class A,class B,class C,class D,class E,class F, class G>
Method(Func,(Func func,A a,B b,C c,D d,E e,F f, G g),(a,b,c,d,e,f,g))
template<class Func,class A,class B,class C,class D,class E,class F>
Method(Func,(Func func,A a,B b,C c,D d,E e,F f),(a,b,c,d,e,f))
template<class Func,class A,class B,class C,class D,class E>
Method(Func,(Func func,A a,B b,C c,D d,E e),(a,b,c,d,e))
template<class Func,class A,class B,class C,class D>
Method(Func,(Func func,A a,B b,C c,D d),(a,b,c,d))
template<class Func,class A,class B,class C>
Method(Func,(Func func,A a,B b,C c),(a,b,c))
template<class Func,class A,class B>
Method(Func,(Func func,A a,B b),(a,b))
template<class Func,class A>
Method(Func,(Func func,A a),(a))
template<class Func>
Method(Func,(Func func),())
#undef Method
/// Journal a function call. Store the function id and all the
/// function's arguments into the journal. On journal playback the
/// function is executed with the retrieved arguments. The function
/// must have been previously declared using the declareFunction()
/// method.
#define Method(Arg1,Arg2,Arg3) \
static void Call Arg1 { \
if (_Dispatching) \
return; \
if (_State == PlayState) \
return; \
if (_State == RecordState) { \
Id id = _getFunctionId((VoidPtr)func,0); \
AssertFatal(id,"Journal: Function must be be declared before being called"); \
_start(); \
IOHelper::writes Arg2; \
_finish(); \
} \
_Dispatching = true; \
(*func) Arg3; \
_Dispatching = false; \
return; \
}
template<class Func,class A,class B,class C,class D,class E, class F, class G, class H, class I, class J>
Method((Func func,A a,B b,C c,D d,E e,F f,G g,H h,I i,J j),(mFile,id,a,b,c,d,e,f,g,h,i,j),(a,b,c,d,e,f,g,h,i,j))
template<class Func,class A,class B,class C,class D,class E, class F, class G, class H, class I>
Method((Func func,A a,B b,C c,D d,E e,F f,G g,H h,I i),(mFile,id,a,b,c,d,e,f,g,h,i),(a,b,c,d,e,f,g,h,i))
template<class Func,class A,class B,class C,class D,class E, class F, class G, class H>
Method((Func func,A a,B b,C c,D d,E e,F f,G g,H h),(mFile,id,a,b,c,d,e,f,g,h),(a,b,c,d,e,f,g,h))
template<class Func,class A,class B,class C,class D,class E, class F, class G>
Method((Func func,A a,B b,C c,D d,E e,F f,G g),(mFile,id,a,b,c,d,e,f,g),(a,b,c,d,e,f,g))
template<class Func,class A,class B,class C,class D,class E, class F>
Method((Func func,A a,B b,C c,D d,E e,F f),(mFile,id,a,b,c,d,e,f),(a,b,c,d,e,f))
template<class Func,class A,class B,class C,class D,class E>
Method((Func func,A a,B b,C c,D d,E e),(mFile,id,a,b,c,d,e),(a,b,c,d,e))
template<class Func,class A,class B,class C,class D>
Method((Func func,A a,B b,C c,D d),(mFile,id,a,b,c,d),(a,b,c,d))
template<class Func,class A,class B,class C>
Method((Func func,A a,B b,C c),(mFile,id,a,b,c),(a,b,c))
template<class Func,class A,class B>
Method((Func func,A a,B b),(mFile,id,a,b),(a,b))
template<class Func,class A>
Method((Func func,A a),(mFile,id,a),(a))
template<class Func>
Method((Func func),(mFile,id),())
#undef Method
#define Method(Arg1,Arg2,Arg3) \
static void Call Arg1 { \
if (_Dispatching) \
return; \
if (_State == PlayState) \
return; \
if (_State == RecordState) { \
Id id = _getFunctionId((VoidPtr)obj,(VoidMethod)method); \
AssertFatal(id != 0,"Journal: Function must be be declared before being called"); \
_start(); \
IOHelper::writes Arg2; \
_finish(); \
} \
_Dispatching = true; \
(obj->*method) Arg3; \
_Dispatching = false; \
return; \
}
template<class Obj,class A,class B,class C,class D,class E,class F,class G,class H,class I,class J>
Method((Obj* obj,void (Obj::*method)(A,B,C,D,E,F,G,H,I,J),A a,B b,C c,D d,E e,F f,G g,H h,I i,J j),(mFile,id,a,b,c,d,e,f,g,h,i,j),(a,b,c,d,e,f,g,h,i,j))
template<class Obj,class A,class B,class C,class D,class E,class F,class G,class H,class I>
Method((Obj* obj,void (Obj::*method)(A,B,C,D,E,F,G,H,I),A a,B b,C c,D d,E e,F f,G g,H h,I i),(mFile,id,a,b,c,d,e,f,g,h,i),(a,b,c,d,e,f,g,h,i))
template<class Obj,class A,class B,class C,class D,class E,class F,class G,class H>
Method((Obj* obj,void (Obj::*method)(A,B,C,D,E,F,G,H),A a,B b,C c,D d,E e,F f,G g,H h),(mFile,id,a,b,c,d,e,f,g,h),(a,b,c,d,e,f,g,h))
template<class Obj,class A,class B,class C,class D,class E,class F,class G>
Method((Obj* obj,void (Obj::*method)(A,B,C,D,E,F,G),A a,B b,C c,D d,E e,F f,G g),(mFile,id,a,b,c,d,e,f,g),(a,b,c,d,e,f,g))
template<class Obj,class A,class B,class C,class D,class E,class F>
Method((Obj* obj,void (Obj::*method)(A,B,C,D,E,F),A a,B b,C c,D d,E e,F f),(mFile,id,a,b,c,d,e,f),(a,b,c,d,e,f))
template<class Obj,class A,class B,class C,class D,class E>
Method((Obj* obj,void (Obj::*method)(A,B,C,D,E),A a,B b,C c,D d,E e),(mFile,id,a,b,c,d,e),(a,b,c,d,e))
template<class Obj,class A,class B,class C,class D>
Method((Obj* obj,void (Obj::*method)(A,B,C,D),A a,B b,C c,D d),(mFile,id,a,b,c,d),(a,b,c,d))
template<class Obj,class A,class B,class C>
Method((Obj* obj,void (Obj::*method)(A,B,C),A a,B b,C c),(mFile,id,a,b,c),(a,b,c))
template<class Obj,class A,class B>
Method((Obj* obj,void (Obj::*method)(A,B),A a,B b),(mFile,id,a,b),(a,b))
template<class Obj,class A>
Method((Obj* obj,void (Obj::*method)(A),A a),(mFile,id,a),(a))
template<class Obj>
Method((Obj* obj,void (Obj::*method)()),(mFile,id),())
#undef Method
/// Write data into the journal. Non-deterministic data can be stored
/// into the journal for reading during playback. The function
/// returns true if the journal is record mode.
#define Method(Arg1,Arg2) \
static inline bool Writes Arg1 { \
if (_State == RecordState) { \
_start(); IOHelper::writes Arg2; _finish(); \
return true; \
} \
return false; \
}
template<class A,class B,class C,class D,class E, class F, class G, class H,class I,class J>
Method((A& a,B& b,C& c,D& d,E& e, F& f, G& g, H& h, I& i,J& j),(mFile,a,b,c,d,e,f,g,h,i,j));
template<class A,class B,class C,class D,class E, class F, class G, class H,class I>
Method((A& a,B& b,C& c,D& d,E& e, F& f, G& g, H& h, I& i),(mFile,a,b,c,d,e,f,g,h,i));
template<class A,class B,class C,class D,class E, class F, class G, class H>
Method((A& a,B& b,C& c,D& d,E& e, F& f, G& g, H& h),(mFile,a,b,c,d,e,f,g,h));
template<class A,class B,class C,class D,class E, class F, class G>
Method((A& a,B& b,C& c,D& d,E& e, F& f, G& g),(mFile,a,b,c,d,e,f,g));
template<class A,class B,class C,class D,class E, class F>
Method((A& a,B& b,C& c,D& d,E& e, F& f),(mFile,a,b,c,d,e,f));
template<class A,class B,class C,class D,class E>
Method((A& a,B& b,C& c,D& d,E& e),(mFile,a,b,c,d,e));
template<class A,class B,class C,class D>
Method((A& a,B& b,C& c,D& d),(mFile,a,b,c,d));
template<class A,class B,class C>
Method((A& a,B& b,C& c),(mFile,a,b,c));
template<class A,class B>
Method((A& a,B& b),(mFile,a,b));
template<class A>
Method((A& a),(mFile,a));
#undef Method
/// Read data from the journal. Read non-deterministic data stored
/// during the recording phase. The function returns true if the
/// journal is play mode.
#define Method(Arg1,Arg2) \
static inline bool Reads Arg1 { \
if (_State == PlayState) { \
_start(); IOHelper::reads Arg2; _finish(); \
return true; \
} \
return false; \
}
template<class A,class B,class C,class D,class E, class F, class G, class H, class I, class J>
Method((A& a,B& b,C& c,D& d,E& e,F& f,G& g, H& h, I& i, J& j),(mFile,a,b,c,d,e,f,g,h,i,j));
template<class A,class B,class C,class D,class E, class F, class G, class H, class I>
Method((A& a,B& b,C& c,D& d,E& e,F& f,G& g, H& h, I& i),(mFile,a,b,c,d,e,f,g,h,i));
template<class A,class B,class C,class D,class E, class F, class G, class H>
Method((A& a,B& b,C& c,D& d,E& e,F& f,G& g, H& h),(mFile,a,b,c,d,e,f,g,h));
template<class A,class B,class C,class D,class E, class F, class G>
Method((A& a,B& b,C& c,D& d,E& e,F& f,G& g),(mFile,a,b,c,d,e,f,g));
template<class A,class B,class C,class D,class E, class F>
Method((A& a,B& b,C& c,D& d,E& e,F& f),(mFile,a,b,c,d,e,f));
template<class A,class B,class C,class D,class E>
Method((A& a,B& b,C& c,D& d,E& e),(mFile,a,b,c,d,e));
template<class A,class B,class C,class D>
Method((A& a,B& b,C& c,D& d),(mFile,a,b,c,d));
template<class A,class B,class C>
Method((A& a,B& b,C& c),(mFile,a,b,c));
template<class A,class B>
Method((A& a,B& b),(mFile,a,b));
template<class A>
Method((A& a),(mFile,a));
#undef Method
};
#endif

View file

@ -0,0 +1,334 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _JOURNALEDSIGNAL_H_
#define _JOURNALEDSIGNAL_H_
#ifndef _UTIL_JOURNAL_JOURNAL_H_
#include "core/util/journal/journal.h"
#endif
#ifndef _TSIGNAL_H_
#include "core/util/tSignal.h"
#endif
template<typename Signature> class JournaledSignal;
/// A specialized signal object for journaling input.
/// @see Journal
template<>
class JournaledSignal<void()> : public Signal<void()>
{
typedef Signal<void()> Parent;
public:
JournaledSignal()
{
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
}
~JournaledSignal()
{
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
}
/// Fires off the bound delegates.
/// @see Journal::Call
void trigger()
{
Journal::Call((Parent*)this, &Parent::trigger);
}
};
/// @copydoc JournaledSignal<void()>
template<class A>
class JournaledSignal<void(A)> : public Signal<void(A)>
{
typedef Signal<void(A)> Parent;
public:
JournaledSignal()
{
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
}
~JournaledSignal()
{
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
}
void trigger(A a)
{
Journal::Call((Parent*)this, &Parent::trigger, a);
}
};
/// @copydoc JournaledSignal<void()>
template<class A, class B>
class JournaledSignal<void(A,B)> : public Signal<void(A,B)>
{
typedef Signal<void(A,B)> Parent;
public:
JournaledSignal()
{
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
}
~JournaledSignal()
{
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
}
void trigger(A a, B b)
{
Journal::Call((Parent*)this, &Parent::trigger, a, b);
}
};
/// @copydoc JournaledSignal<void()>
template<class A, class B, class C>
class JournaledSignal<void(A,B,C)> : public Signal<void(A,B,C)>
{
typedef Signal<void(A,B,C)> Parent;
public:
JournaledSignal()
{
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
}
~JournaledSignal()
{
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
}
void trigger(A a, B b, C c)
{
Journal::Call((Parent*)this, &Parent::trigger, a, b, c);
}
};
/// @copydoc JournaledSignal<void()>
template<class A, class B, class C, class D>
class JournaledSignal<void(A,B,C,D)> : public Signal<void(A,B,C,D)>
{
typedef Signal<void(A,B,C,D)> Parent;
public:
JournaledSignal()
{
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
}
~JournaledSignal()
{
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
}
void trigger(A a, B b, C c, D d)
{
Journal::Call((Parent*)this, &Parent::trigger, a, b, c, d);
}
};
/// @copydoc JournaledSignal<void()>
template<class A, class B, class C, class D, class E>
class JournaledSignal<void(A,B,C,D,E)> : public Signal<void(A,B,C,D,E)>
{
typedef Signal<void(A,B,C,D,E)> Parent;
public:
JournaledSignal()
{
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
}
~JournaledSignal()
{
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
}
void trigger(A a, B b, C c, D d, E e)
{
Journal::Call((Parent*)this, &Parent::trigger, a, b, c, d, e);
}
};
/// @copydoc JournaledSignal<void()>
template<class A, class B, class C, class D, class E, class F>
class JournaledSignal<void(A,B,C,D,E,F)> : public Signal<void(A,B,C,D,E,F)>
{
typedef Signal<void(A,B,C,D,E,F)> Parent;
public:
JournaledSignal()
{
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
}
~JournaledSignal()
{
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
}
void trigger(A a, B b, C c, D d, E e, F f)
{
Journal::Call((Parent*)this, &Parent::trigger, a, b, c, d, e, f);
}
};
/// @copydoc JournaledSignal<void()>
template<class A, class B, class C, class D, class E, class F, class G>
class JournaledSignal<void(A,B,C,D,E,F,G)> : public Signal<void(A,B,C,D,E,F,G)>
{
typedef Signal<void(A,B,C,D,E,F,G)> Parent;
public:
JournaledSignal()
{
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
}
~JournaledSignal()
{
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
}
void trigger(A a, B b, C c, D d, E e, F f, G g)
{
Journal::Call((Parent*)this, &Parent::trigger, a, b, c, d, e, f, g);
}
};
/// @copydoc JournaledSignal<void()>
template<class A, class B, class C, class D, class E, class F, class G, class H>
class JournaledSignal<void(A,B,C,D,E,F,G,H)> : public Signal<void(A,B,C,D,E,F,G,H)>
{
typedef Signal<void(A,B,C,D,E,F,G,H)> Parent;
public:
JournaledSignal()
{
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
}
~JournaledSignal()
{
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
}
void trigger(A a, B b, C c, D d, E e, F f, G g, H h)
{
Journal::Call((Parent*)this, &Parent::trigger, a, b, c, d, e, f, g, h);
}
};
//-----------------------------------------------------------------------------
// Common event callbacks definitions
enum InputModifier {
IM_LALT = (1 << 1),
IM_RALT = (1 << 2),
IM_LSHIFT = (1 << 3),
IM_RSHIFT = (1 << 4),
IM_LCTRL = (1 << 5),
IM_RCTRL = (1 << 6),
IM_LOPT = (1 << 7),
IM_ROPT = (1 << 8),
IM_ALT = IM_LALT | IM_RALT,
IM_SHIFT = IM_LSHIFT | IM_RSHIFT,
IM_CTRL = IM_LCTRL | IM_RCTRL,
IM_OPT = IM_LOPT | IM_ROPT,
};
enum InputAction {
IA_MAKE = (1 << 0),
IA_BREAK = (1 << 1),
IA_REPEAT = (1 << 2),
IA_MOVE = (1 << 3),
IA_DELTA = (1 << 4),
IA_BUTTON = (1 << 5),
};
enum ApplicationMessage {
Quit,
WindowOpen, ///< Window opened
WindowClose, ///< Window closed.
WindowShown, ///< Window has been shown on screen
WindowHidden, ///< Window has become hidden
WindowDestroy, ///< Window was destroyed.
GainCapture, ///< Window will capture all input
LoseCapture, ///< Window will no longer capture all input
GainFocus, ///< Application gains focus
LoseFocus, ///< Application loses focus
DisplayChange, ///< Desktop Display mode has changed
GainScreen, ///< Window will acquire lock on the full screen
LoseScreen, ///< Window has released lock on the full screen
Timer,
};
typedef U32 WindowId;
/// void event()
typedef JournaledSignal<void()> IdleEvent;
/// void event(WindowId,U32 modifier,S32 x,S32 y, bool isRelative)
typedef JournaledSignal<void(WindowId,U32,S32,S32,bool)> MouseEvent;
/// void event(WindowId,U32 modifier,S32 wheelDeltaX, S32 wheelDeltaY)
typedef JournaledSignal<void(WindowId,U32,S32,S32)> MouseWheelEvent;
/// void event(WindowId,U32 modifier,U32 action,U16 key)
typedef JournaledSignal<void(WindowId,U32,U32,U16)> KeyEvent;
/// void event(WindowId,U32 modifier,U16 key)
typedef JournaledSignal<void(WindowId,U32,U16)> CharEvent;
/// void event(WindowId,U32 modifier,U32 action,U16 button)
typedef JournaledSignal<void(WindowId,U32,U32,U16)> ButtonEvent;
/// void event(WindowId,U32 modifier,U32 action,U32 axis,F32 value)
typedef JournaledSignal<void(WindowId,U32,U32,U32,F32)> LinearEvent;
/// void event(WindowId,U32 modifier,F32 value)
typedef JournaledSignal<void(WindowId,U32,F32)> PovEvent;
/// void event(WindowId,InputAppMessage)
typedef JournaledSignal<void(WindowId,S32)> AppEvent;
/// void event(WindowId)
typedef JournaledSignal<void(WindowId)> DisplayEvent;
/// void event(WindowId, S32 width, S32 height)
typedef JournaledSignal<void(WindowId, S32, S32)> ResizeEvent;
/// void event(S32 timeDelta)
typedef JournaledSignal<void(S32)> TimeManagerEvent;
// void event(U32 deviceInst,F32 fValue, U16 deviceType, U16 objType, U16 ascii, U16 objInst, U8 action, U8 modifier)
typedef JournaledSignal<void(U32,F32,U16,U16,U16,U16,U8,U8)> InputEvent;
/// void event(U32 popupGUID, U32 commandID, bool& returnValue)
typedef JournaledSignal<void(U32, U32)> PopupMenuEvent;
#endif

View file

@ -0,0 +1,110 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/util/journal/process.h"
#include "core/util/journal/journal.h"
#include "core/module.h"
MODULE_BEGIN( Process )
MODULE_INIT
{
Process::init();
}
MODULE_SHUTDOWN
{
Process::shutdown();
}
MODULE_END;
static Process* _theOneProcess = NULL; ///< the one instance of the Process class
//-----------------------------------------------------------------------------
void Process::requestShutdown()
{
Process::get()._RequestShutdown = true;
}
//-----------------------------------------------------------------------------
Process::Process()
: _RequestShutdown( false )
{
}
Process &Process::get()
{
struct Cleanup
{
~Cleanup()
{
if( _theOneProcess )
delete _theOneProcess;
}
};
static Cleanup cleanup;
// NOTE that this function is not thread-safe
// To make it thread safe, use the double-checked locking mechanism for singleton objects
if ( !_theOneProcess )
_theOneProcess = new Process;
return *_theOneProcess;
}
bool Process::init()
{
return Process::get()._signalInit.trigger();
}
void Process::handleCommandLine(S32 argc, const char **argv)
{
Process::get()._signalCommandLine.trigger(argc, argv);
}
bool Process::processEvents()
{
// Process all the devices. We need to call these even during journal
// playback to ensure that the OS event queues are serviced.
Process::get()._signalProcess.trigger();
if (!Process::get()._RequestShutdown)
{
if (Journal::IsPlaying())
return Journal::PlayNext();
return true;
}
// Reset the Quit flag so the function can be called again.
Process::get()._RequestShutdown = false;
return false;
}
bool Process::shutdown()
{
return Process::get()._signalShutdown.trigger();
}

View file

@ -0,0 +1,193 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _UTIL_JOURNAL_PROCESS_H_
#define _UTIL_JOURNAL_PROCESS_H_
#include "platform/platform.h"
#include "core/util/tVector.h"
#include "core/util/delegate.h"
#include "core/util/tSignal.h"
#define PROCESS_FIRST_ORDER 0.0f
#define PROCESS_NET_ORDER 0.35f
#define PROCESS_INPUT_ORDER 0.4f
#define PROCESS_DEFAULT_ORDER 0.5f
#define PROCESS_TIME_ORDER 0.75f
#define PROCESS_RENDER_ORDER 0.8f
#define PROCESS_LAST_ORDER 1.0f
class StandardMainLoop;
/// Event generation signal.
///
/// Objects that generate events need to register a callback with
/// this signal and should only generate events from within the callback.
///
/// This signal is triggered from the ProcessEvents() method.
class Process
{
public:
/// Trigger the ProcessSignal and replay journal events.
///
/// The ProcessSignal is triggered during which all events are generated,
/// journaled, and delivered using the EventSignal classes. Event producers should
/// only generate events from within the function they register with ProcessSignal.
/// ProcessSignal is also triggered during event playback, though all new events are
/// thrown away so as not to interfere with journal playback.
/// This function returns false if Process::requestShutdown() has been called, otherwise it
/// returns true.
///
/// NOTE: This should only be called from main loops - it should really be private,
/// but we need to sort out how to handle the unit test cases
static bool processEvents();
/// Ask the processEvents() function to shutdown.
static void requestShutdown();
static void notifyInit(Delegate<bool()> del, F32 order = PROCESS_DEFAULT_ORDER)
{
get()._signalInit.notify(del,order);
}
template <class T>
static void notifyInit(T func, F32 order = PROCESS_DEFAULT_ORDER)
{
get()._signalInit.notify(func,order);
}
static void notifyCommandLine(Delegate<void(S32, const char **)> del, F32 order = PROCESS_DEFAULT_ORDER)
{
get()._signalCommandLine.notify(del,order);
}
template <class T>
static void notifyCommandLine(T func, F32 order = PROCESS_DEFAULT_ORDER)
{
get()._signalCommandLine.notify(func,order);
}
static void notify(Delegate<void()> del, F32 order = PROCESS_DEFAULT_ORDER)
{
get()._signalProcess.notify(del,order);
}
template <class T>
static void notify(T func, F32 order = PROCESS_DEFAULT_ORDER)
{
get()._signalProcess.notify(func,order);
}
template <class T,class U>
static void notify(T obj,U func, F32 order = PROCESS_DEFAULT_ORDER)
{
get()._signalProcess.notify(obj,func,order);
}
static void remove(Delegate<void()> del)
{
get()._signalProcess.remove(del);
}
template <class T>
static void remove(T func)
{
get()._signalProcess.remove(func);
}
template <class T,class U>
static void remove(T obj,U func)
{
get()._signalProcess.remove(obj,func);
}
static void notifyShutdown(Delegate<bool(void)> del, F32 order = PROCESS_DEFAULT_ORDER)
{
get()._signalShutdown.notify(del,order);
}
template <class T>
static void notifyShutdown(T func, F32 order = PROCESS_DEFAULT_ORDER)
{
get()._signalShutdown.notify(func,order);
}
/// Trigger the registered init functions
static bool init();
/// Trigger the registered shutdown functions
static bool shutdown();
private:
friend class StandardMainLoop;
/// Trigger the registered command line handling functions
static void handleCommandLine(S32 argc, const char **argv);
/// Private constructor
Process();
/// Access method will construct the singleton as necessary
static Process &get();
Signal<bool()> _signalInit;
Signal<void(S32, const char **)> _signalCommandLine;
Signal<void()> _signalProcess;
Signal<bool()> _signalShutdown;
bool _RequestShutdown;
};
/// Register a command line handling function.
///
/// To use this, put it as a member variable into your module definition.
class ProcessRegisterCommandLine
{
public:
template <class T>
ProcessRegisterCommandLine( T func, F32 order = PROCESS_DEFAULT_ORDER )
{
Process::notifyCommandLine( func, order );
}
};
/// Register a processing function
///
/// To use this, put it as a member variable into your module definition.
class ProcessRegisterProcessing
{
public:
template <class T>
ProcessRegisterProcessing( T func, F32 order = PROCESS_DEFAULT_ORDER )
{
Process::notify( func, order );
}
};
#endif

View file

@ -0,0 +1,173 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "unit/test.h"
#include "core/util/journal/journaledSignal.h"
#include "core/util/safeDelete.h"
using namespace UnitTesting;
CreateUnitTest(TestsJournalRecordAndPlayback, "Journal/Basic")
{
U32 _lastTriggerValue;
void triggerReceiver(U16 msg)
{
_lastTriggerValue = msg;
}
void run()
{
// Reset the last trigger value just in case...
_lastTriggerValue = 0;
// Set up a journaled signal to test with.
JournaledSignal<void(U16)> testEvent;
testEvent.notify(this, &TestsJournalRecordAndPlayback::triggerReceiver);
// Initialize journal recording and fire off some events...
Journal::Record("test.jrn");
testEvent.trigger(16);
testEvent.trigger(17);
testEvent.trigger(18);
test(_lastTriggerValue == 18, "Should encounter last triggered value (18).");
Journal::Stop();
// Clear it...
_lastTriggerValue = 0;
// and play back - should get same thing.
Journal::Play("test.jrn");
// Since we fired 3 events, it should take three loops.
test(Journal::PlayNext(), "Should be two more events.");
test(Journal::PlayNext(), "Should be one more event.");
test(!Journal::PlayNext(), "Should be no more events.");
test(_lastTriggerValue == 18, "Should encounter last journaled value (18).");
}
};
CreateUnitTest(TestsJournalDynamicSignals, "Journal/DynamicSignals")
{
typedef JournaledSignal<void(U32, U16)> EventA;
typedef JournaledSignal<void(U8, S8)> EventB;
typedef JournaledSignal<void(U32, S32)> EventC;
EventA *dynamicA;
EventB *dynamicB;
EventC *dynamicC;
// Root, non-dynamic signal receiver.
void receiverRoot(U8 msg)
{
if(msg==1)
{
dynamicA = new EventA();
dynamicA->notify(this, &TestsJournalDynamicSignals::receiverA);
}
if(msg==2)
{
dynamicB = new EventB();
dynamicB->notify(this, &TestsJournalDynamicSignals::receiverB);
}
if(msg==3)
{
dynamicC = new EventC();
dynamicC->notify(this, &TestsJournalDynamicSignals::receiverC);
}
}
U32 recvA, recvB, recvC;
void receiverA(U32, U16 d)
{
recvA += d;
}
void receiverB(U8, S8 d)
{
recvB += d;
}
void receiverC(U32, S32 d)
{
recvC += d;
}
void run()
{
// Reset our state values.
recvA = recvB = recvC = 0;
// Set up a signal to start with.
JournaledSignal<void(U8)> testEvent;
testEvent.notify(this, &TestsJournalDynamicSignals::receiverRoot);
// Initialize journal recording and fire off some events...
Journal::Record("test.jrn");
testEvent.trigger(1);
dynamicA->trigger(8, 100);
testEvent.trigger(2);
dynamicA->trigger(8, 8);
dynamicB->trigger(9, 'a');
testEvent.trigger(3);
SAFE_DELETE(dynamicB); // Test a deletion.
dynamicC->trigger(8, 1);
dynamicC->trigger(8, 1);
// Did we end up with expected values? Check before clearing.
test(recvA == 108, "recvA wasn't 108 - something broken in signals?");
test(recvB == 'a', "recvB wasn't 'a' - something broken in signals?");
test(recvC == 2, "recvC wasn't 2 - something broken in signals?");
// Reset our state values.
recvA = recvB = recvC = 0;
// And kill the journal...
Journal::Stop();
// Also kill our remaining dynamic signals.
SAFE_DELETE(dynamicA);
SAFE_DELETE(dynamicB);
SAFE_DELETE(dynamicC);
// Play back - should get same thing.
Journal::Play("test.jrn");
// Since we fired 8 events, it should take 7+1=8 loops.
for(S32 i=0; i<7; i++)
test(Journal::PlayNext(), "Should be more events.");
test(!Journal::PlayNext(), "Should be no more events.");
test(recvA == 108, "recvA wasn't 108 - something broken in journal?");
test(recvB == 'a', "recvB wasn't 'a' - something broken in journal?");
test(recvC == 2, "recvC wasn't 2 - something broken in journal?");
}
};

View file

@ -0,0 +1,56 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "unit/test.h"
#include "core/util/journal/process.h"
#include "core/util/safeDelete.h"
using namespace UnitTesting;
CreateUnitTest(TestingProcess, "Journal/Process")
{
// How many ticks remaining?
U32 _remainingTicks;
// Callback for process list.
void process()
{
if(_remainingTicks==0)
Process::requestShutdown();
_remainingTicks--;
}
void run()
{
// We'll run 30 ticks, then quit.
_remainingTicks = 30;
// Register with the process list.
Process::notify(this, &TestingProcess::process);
// And do 30 notifies, making sure we end on the 30th.
for(S32 i=0; i<30; i++)
test(Process::processEvents(), "Should quit after 30 ProcessEvents() calls - not before!");
test(!Process::processEvents(), "Should quit after the 30th ProcessEvent() call!");
}
};

View file

@ -0,0 +1,259 @@
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*/
/* Brutally hacked by John Walker back from ANSI C to K&R (no
prototypes) to maintain the tradition that Netfone will compile
with Sun's original "cc". */
#include <memory.h> /* for memcpy() */
#include "md5.h"
#ifdef sgi
#define HIGHFIRST
#endif
#ifdef sun
#define HIGHFIRST
#endif
#ifndef HIGHFIRST
#define byteReverse(buf, len) /* Nothing */
#else
/*
* Note: this code is harmless on little-endian machines.
*/
void byteReverse(buf, longs)
unsigned char *buf; unsigned longs;
{
int t;
do {
t = (int) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
((unsigned) buf[1] << 8 | buf[0]);
*(int *) buf = t;
buf += 4;
} while (--longs);
}
#endif
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
*/
void MD5Init( MD5Context* ctx)
{
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
}
/*
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
void MD5Update( MD5Context* ctx, unsigned char* buf, unsigned int len)
{
int t;
/* Update bitcount */
t = ctx->bits[0];
if ((ctx->bits[0] = t + ((int) len << 3)) < t)
ctx->bits[1]++; /* Carry from low to high */
ctx->bits[1] += len >> 29;
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
/* Handle any leading odd-sized chunks */
if (t) {
unsigned char *p = (unsigned char *) ctx->in + t;
t = 64 - t;
if (len < t) {
memcpy(p, buf, len);
return;
}
memcpy(p, buf, t);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (int *) ctx->in);
buf += t;
len -= t;
}
/* Process data in 64-byte chunks */
while (len >= 64) {
memcpy(ctx->in, buf, 64);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (int *) ctx->in);
buf += 64;
len -= 64;
}
/* Handle any remaining bytes of data. */
memcpy(ctx->in, buf, len);
}
/*
* Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
void MD5Final( unsigned char digest[16], MD5Context* ctx)
{
unsigned count;
unsigned char *p;
/* Compute number of bytes mod 64 */
count = (ctx->bits[0] >> 3) & 0x3F;
/* Set the first char of padding to 0x80. This is safe since there is
always at least one byte free */
p = ctx->in + count;
*p++ = 0x80;
/* Bytes of padding needed to make 64 bytes */
count = 64 - 1 - count;
/* Pad out to 56 mod 64 */
if (count < 8) {
/* Two lots of padding: Pad the first block to 64 bytes */
memset(p, 0, count);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (int *) ctx->in);
/* Now fill the next block with 56 bytes */
memset(ctx->in, 0, 56);
} else {
/* Pad block to 56 bytes */
memset(p, 0, count - 8);
}
byteReverse(ctx->in, 14);
/* Append length in bits and transform */
((int *) ctx->in)[14] = ctx->bits[0];
((int *) ctx->in)[15] = ctx->bits[1];
MD5Transform(ctx->buf, (int *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
memcpy(digest, ctx->buf, 16);
memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
}
/* The four core functions - F1 is optimized somewhat */
/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new data. MD5Update blocks
* the data and converts bytes into longwords for this routine.
*/
void MD5Transform( int buf[4], int in[16])
{
register int a, b, c, d;
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}

View file

@ -0,0 +1,18 @@
#ifndef MD5_H
#define MD5_H
struct MD5Context {
int buf[4];
int bits[2];
unsigned char in[64];
};
extern void MD5Init(struct MD5Context *ctx);
extern void MD5Update(struct MD5Context *ctx, unsigned char *buf, unsigned len);
extern void MD5Final(unsigned char digest[16], struct MD5Context *ctx);
extern void MD5Transform(int but[4], int in[16]);
typedef MD5Context MD5_CTX;
#endif /* !MD5_H */

View file

@ -0,0 +1,145 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _TORQUE_CORE_UTIL_NAMEDSINGLETON_H_
#define _TORQUE_CORE_UTIL_NAMEDSINGLETON_H_
#include "platform/platform.h"
#include "core/util/safeCast.h"
#include "console/console.h"
#include "core/stringTable.h"
//--------------------------------------------------------------------------
// StaticNamedSingleton.
//--------------------------------------------------------------------------
/// Collection of statically registered named singletons.
///
/// This class is useful as a mix-in for classes that are supposed to
/// represent a range of named singleton instances from which a specific
/// instance is then selected at run-time.
///
/// @param T Arbitrary type parameter; identical types will share
/// static data.
template< class T >
struct StaticNamedSingleton
{
typedef StaticNamedSingleton This;
StaticNamedSingleton( const char* name );
virtual ~StaticNamedSingleton() {}
const char* getName();
T* getNext();
static T* staticGetFirst();
static T* staticFindSingleton( const char* name );
static EnumTable* staticCreateEnumTable();
static U32 staticGetNumSingletons();
private:
const char* mName;
This* mNext;
static This* smSingletons;
};
template< class T >
StaticNamedSingleton< T >* StaticNamedSingleton< T >::smSingletons;
template< class T >
StaticNamedSingleton< T >::StaticNamedSingleton( const char* name )
: mName( name )
{
mNext = smSingletons;
smSingletons = this;
}
template< class T >
inline const char* StaticNamedSingleton< T >::getName()
{
return mName;
}
template< class T >
inline T* StaticNamedSingleton< T >::getNext()
{
return static_cast< T* >( mNext );
}
template< class T >
T* StaticNamedSingleton< T >::staticGetFirst()
{
return static_cast< T* >( smSingletons );
}
/// Find the instance with the given name. Returns NULL if no such
/// instance exists.
template< class T >
T* StaticNamedSingleton< T >::staticFindSingleton( const char* name )
{
for( This* ptr = smSingletons; ptr != 0; ptr = ptr->mNext )
if( dStricmp( name, ptr->mName ) == 0 )
return static_cast< T* >( ptr );
return 0;
}
/// Create a TorqueScript EnumTable that contains all registered
/// instance names.
template< class T >
EnumTable* StaticNamedSingleton< T >::staticCreateEnumTable()
{
U32 numSingletons = staticGetNumSingletons();
// Create the enums.
EnumTable::Enums* enums = new EnumTable::Enums[ numSingletons ];
This* ptr = smSingletons;
for( U32 i = 0; i < numSingletons; ++ i )
{
enums[ i ].index = i;
enums[ i ].label = StringTable->insert( ptr->getName() );
ptr = ptr->mNext;
}
// Create the table.
return new EnumTable( numSingletons, enums );
}
/// Return the number of registered named singletons.
template< class T >
U32 StaticNamedSingleton< T >::staticGetNumSingletons()
{
U32 numSingletons = 0;
for( This* ptr = smSingletons; ptr != 0; ptr = ptr->mNext )
numSingletons ++;
return numSingletons;
}
#endif // _TORQUE_CORE_UTIL_NAMEDSINGLETON_H_

View file

@ -0,0 +1,37 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _CORE_NONCOPYABLE_H_
#define _CORE_NONCOPYABLE_H_
class Noncopyable
{
protected:
Noncopyable() {}
~Noncopyable() {}
private:
Noncopyable(const Noncopyable&);
const Noncopyable& operator=(const Noncopyable&);
};
#endif

Some files were not shown because too many files have changed in this diff Show more