mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-11 14:44:36 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
182
Engine/source/core/bitMatrix.h
Normal file
182
Engine/source/core/bitMatrix.h
Normal 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_
|
||||
1004
Engine/source/core/bitRender.cpp
Normal file
1004
Engine/source/core/bitRender.cpp
Normal file
File diff suppressed because it is too large
Load diff
59
Engine/source/core/bitRender.h
Normal file
59
Engine/source/core/bitRender.h
Normal 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_
|
||||
|
||||
88
Engine/source/core/bitSet.h
Normal file
88
Engine/source/core/bitSet.h
Normal 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_
|
||||
116
Engine/source/core/bitVector.cpp
Normal file
116
Engine/source/core/bitVector.cpp
Normal 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;
|
||||
}
|
||||
220
Engine/source/core/bitVector.h
Normal file
220
Engine/source/core/bitVector.h
Normal 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_
|
||||
108
Engine/source/core/bitVectorW.h
Normal file
108
Engine/source/core/bitVectorW.h
Normal 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_
|
||||
40
Engine/source/core/color.cpp
Normal file
40
Engine/source/core/color.cpp
Normal 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
608
Engine/source/core/color.h
Normal 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_
|
||||
90
Engine/source/core/crc.cpp
Normal file
90
Engine/source/core/crc.cpp
Normal 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
49
Engine/source/core/crc.h
Normal 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
|
||||
|
||||
97
Engine/source/core/dataChunker.cpp
Normal file
97
Engine/source/core/dataChunker.cpp
Normal 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;
|
||||
}
|
||||
|
||||
297
Engine/source/core/dataChunker.h
Normal file
297
Engine/source/core/dataChunker.h
Normal 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
288
Engine/source/core/dnet.cpp
Normal 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
83
Engine/source/core/dnet.h
Normal 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
|
||||
502
Engine/source/core/fileObject.cpp
Normal file
502
Engine/source/core/fileObject.cpp
Normal 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 );
|
||||
}
|
||||
|
||||
58
Engine/source/core/fileObject.h
Normal file
58
Engine/source/core/fileObject.h
Normal 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
140
Engine/source/core/fileio.h
Normal 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_
|
||||
78
Engine/source/core/filterStream.cpp
Normal file
78
Engine/source/core/filterStream.cpp
Normal 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();
|
||||
}
|
||||
|
||||
56
Engine/source/core/filterStream.h
Normal file
56
Engine/source/core/filterStream.h
Normal 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_
|
||||
37
Engine/source/core/frameAllocator.cpp
Normal file
37
Engine/source/core/frameAllocator.cpp
Normal 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
|
||||
320
Engine/source/core/frameAllocator.h
Normal file
320
Engine/source/core/frameAllocator.h
Normal 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_
|
||||
111
Engine/source/core/iTickable.cpp
Normal file
111
Engine/source/core/iTickable.cpp
Normal 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;
|
||||
}
|
||||
171
Engine/source/core/iTickable.h
Normal file
171
Engine/source/core/iTickable.h
Normal 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
|
||||
36
Engine/source/core/idGenerator.cpp
Normal file
36
Engine/source/core/idGenerator.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
93
Engine/source/core/idGenerator.h
Normal file
93
Engine/source/core/idGenerator.h
Normal 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
|
||||
511
Engine/source/core/memVolume.cpp
Normal file
511
Engine/source/core/memVolume.cpp
Normal 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
|
||||
132
Engine/source/core/memVolume.h
Normal file
132
Engine/source/core/memVolume.h
Normal 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
|
||||
385
Engine/source/core/module.cpp
Normal file
385
Engine/source/core/module.cpp
Normal 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
357
Engine/source/core/module.h
Normal 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_
|
||||
261
Engine/source/core/ogg/oggInputStream.cpp
Normal file
261
Engine/source/core/ogg/oggInputStream.cpp
Normal 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;
|
||||
}
|
||||
173
Engine/source/core/ogg/oggInputStream.h
Normal file
173
Engine/source/core/ogg/oggInputStream.h
Normal 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_
|
||||
694
Engine/source/core/ogg/oggTheoraDecoder.cpp
Normal file
694
Engine/source/core/ogg/oggTheoraDecoder.cpp
Normal 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
|
||||
}
|
||||
267
Engine/source/core/ogg/oggTheoraDecoder.h
Normal file
267
Engine/source/core/ogg/oggTheoraDecoder.h
Normal 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_
|
||||
195
Engine/source/core/ogg/oggVorbisDecoder.cpp
Normal file
195
Engine/source/core/ogg/oggVorbisDecoder.cpp
Normal 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;
|
||||
}
|
||||
91
Engine/source/core/ogg/oggVorbisDecoder.h
Normal file
91
Engine/source/core/ogg/oggVorbisDecoder.h
Normal 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_
|
||||
159
Engine/source/core/resizeStream.cpp
Normal file
159
Engine/source/core/resizeStream.cpp
Normal 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;
|
||||
}
|
||||
|
||||
69
Engine/source/core/resizeStream.h
Normal file
69
Engine/source/core/resizeStream.h
Normal 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_
|
||||
110
Engine/source/core/resource.cpp
Normal file
110
Engine/source/core/resource.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
323
Engine/source/core/resource.h
Normal file
323
Engine/source/core/resource.h
Normal 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__
|
||||
246
Engine/source/core/resourceManager.cpp
Normal file
246
Engine/source/core/resourceManager.cpp
Normal 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 );
|
||||
91
Engine/source/core/resourceManager.h
Normal file
91
Engine/source/core/resourceManager.h
Normal 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
|
||||
1142
Engine/source/core/stream/bitStream.cpp
Normal file
1142
Engine/source/core/stream/bitStream.cpp
Normal file
File diff suppressed because it is too large
Load diff
385
Engine/source/core/stream/bitStream.h
Normal file
385
Engine/source/core/stream/bitStream.h
Normal 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_
|
||||
579
Engine/source/core/stream/fileStream.cpp
Normal file
579
Engine/source/core/stream/fileStream.cpp
Normal 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;
|
||||
}
|
||||
93
Engine/source/core/stream/fileStream.h
Normal file
93
Engine/source/core/stream/fileStream.h
Normal 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
|
||||
175
Engine/source/core/stream/fileStreamObject.cpp
Normal file
175
Engine/source/core/stream/fileStreamObject.cpp
Normal 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();
|
||||
}
|
||||
67
Engine/source/core/stream/fileStreamObject.h
Normal file
67
Engine/source/core/stream/fileStreamObject.h
Normal 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_
|
||||
115
Engine/source/core/stream/ioHelper.h
Normal file
115
Engine/source/core/stream/ioHelper.h
Normal 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
|
||||
233
Engine/source/core/stream/memStream.cpp
Normal file
233
Engine/source/core/stream/memStream.cpp
Normal 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;
|
||||
}
|
||||
104
Engine/source/core/stream/memStream.h
Normal file
104
Engine/source/core/stream/memStream.h
Normal 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_
|
||||
378
Engine/source/core/stream/stream.cpp
Normal file
378
Engine/source/core/stream/stream.cpp
Normal 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;
|
||||
}
|
||||
242
Engine/source/core/stream/stream.h
Normal file
242
Engine/source/core/stream/stream.h
Normal 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_
|
||||
476
Engine/source/core/stream/streamObject.cpp
Normal file
476
Engine/source/core/stream/streamObject.cpp
Normal 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);
|
||||
}
|
||||
138
Engine/source/core/stream/streamObject.h
Normal file
138
Engine/source/core/stream/streamObject.h
Normal 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_
|
||||
373
Engine/source/core/stream/tStream.h
Normal file
373
Engine/source/core/stream/tStream.h
Normal 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_
|
||||
471
Engine/source/core/stringBuffer.cpp
Normal file
471
Engine/source/core/stringBuffer.cpp
Normal 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 ©) : mBuffer(), mBuffer8()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION( mBuffer );
|
||||
SBMAddThisStringBuffer();
|
||||
set(©);
|
||||
};
|
||||
|
||||
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
|
||||
134
Engine/source/core/stringBuffer.h
Normal file
134
Engine/source/core/stringBuffer.h
Normal 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 ©);
|
||||
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
|
||||
233
Engine/source/core/stringTable.cpp
Normal file
233
Engine/source/core/stringTable.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
176
Engine/source/core/stringTable.h
Normal file
176
Engine/source/core/stringTable.h
Normal 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_
|
||||
|
||||
163
Engine/source/core/strings/findMatch.cpp
Normal file
163
Engine/source/core/strings/findMatch.cpp
Normal 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;
|
||||
}
|
||||
63
Engine/source/core/strings/findMatch.h
Normal file
63
Engine/source/core/strings/findMatch.h
Normal 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_
|
||||
534
Engine/source/core/strings/stringFunctions.cpp
Normal file
534
Engine/source/core/strings/stringFunctions.cpp
Normal 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 );
|
||||
}
|
||||
227
Engine/source/core/strings/stringFunctions.h
Normal file
227
Engine/source/core/strings/stringFunctions.h
Normal 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
|
||||
217
Engine/source/core/strings/stringUnit.cpp
Normal file
217
Engine/source/core/strings/stringUnit.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
39
Engine/source/core/strings/stringUnit.h
Normal file
39
Engine/source/core/strings/stringUnit.h
Normal 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
|
||||
663
Engine/source/core/strings/unicode.cpp
Normal file
663
Engine/source/core/strings/unicode.cpp
Normal 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;
|
||||
}
|
||||
131
Engine/source/core/strings/unicode.h
Normal file
131
Engine/source/core/strings/unicode.h
Normal 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_
|
||||
62
Engine/source/core/tAlgorithm.h
Normal file
62
Engine/source/core/tAlgorithm.h
Normal 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_
|
||||
115
Engine/source/core/tSimpleHashTable.h
Normal file
115
Engine/source/core/tSimpleHashTable.h
Normal 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
|
||||
152
Engine/source/core/tSparseArray.h
Normal file
152
Engine/source/core/tSparseArray.h
Normal 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_
|
||||
|
||||
320
Engine/source/core/tagDictionary.cpp
Normal file
320
Engine/source/core/tagDictionary.cpp
Normal 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);
|
||||
}
|
||||
|
||||
83
Engine/source/core/tagDictionary.h
Normal file
83
Engine/source/core/tagDictionary.h
Normal 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_
|
||||
113
Engine/source/core/threadStatic.cpp
Normal file
113
Engine/source/core/threadStatic.cpp
Normal 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();
|
||||
}
|
||||
216
Engine/source/core/threadStatic.h
Normal file
216
Engine/source/core/threadStatic.h
Normal 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
|
||||
613
Engine/source/core/tokenizer.cpp
Normal file
613
Engine/source/core/tokenizer.cpp
Normal 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;
|
||||
}
|
||||
109
Engine/source/core/tokenizer.h
Normal file
109
Engine/source/core/tokenizer.h
Normal 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_
|
||||
2107
Engine/source/core/util/FastDelegate.h
Normal file
2107
Engine/source/core/util/FastDelegate.h
Normal file
File diff suppressed because it is too large
Load diff
131
Engine/source/core/util/autoPtr.h
Normal file
131
Engine/source/core/util/autoPtr.h
Normal 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
|
||||
158
Engine/source/core/util/byteBuffer.cpp
Normal file
158
Engine/source/core/util/byteBuffer.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
85
Engine/source/core/util/byteBuffer.h
Normal file
85
Engine/source/core/util/byteBuffer.h
Normal 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
|
||||
25
Engine/source/core/util/byteswap.h
Normal file
25
Engine/source/core/util/byteswap.h
Normal 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)
|
||||
42
Engine/source/core/util/commonSwizzles.cpp
Normal file
42
Engine/source/core/util/commonSwizzles.cpp
Normal 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;
|
||||
}
|
||||
61
Engine/source/core/util/delegate.h
Normal file
61
Engine/source/core/util/delegate.h
Normal 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_
|
||||
108
Engine/source/core/util/dxt5nmSwizzle.h
Normal file
108
Engine/source/core/util/dxt5nmSwizzle.h
Normal 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
|
||||
141
Engine/source/core/util/endian.h
Normal file
141
Engine/source/core/util/endian.h
Normal 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
|
||||
|
||||
27
Engine/source/core/util/fourcc.h
Normal file
27
Engine/source/core/util/fourcc.h
Normal 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)
|
||||
271
Engine/source/core/util/hashFunction.cpp
Normal file
271
Engine/source/core/util/hashFunction.cpp
Normal 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
|
||||
39
Engine/source/core/util/hashFunction.h
Normal file
39
Engine/source/core/util/hashFunction.h
Normal 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_
|
||||
202
Engine/source/core/util/journal/journal.cpp
Normal file
202
Engine/source/core/util/journal/journal.cpp
Normal 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;
|
||||
}
|
||||
635
Engine/source/core/util/journal/journal.h
Normal file
635
Engine/source/core/util/journal/journal.h
Normal 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
|
||||
334
Engine/source/core/util/journal/journaledSignal.h
Normal file
334
Engine/source/core/util/journal/journaledSignal.h
Normal 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
|
||||
110
Engine/source/core/util/journal/process.cpp
Normal file
110
Engine/source/core/util/journal/process.cpp
Normal 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();
|
||||
}
|
||||
193
Engine/source/core/util/journal/process.h
Normal file
193
Engine/source/core/util/journal/process.h
Normal 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
|
||||
173
Engine/source/core/util/journal/test/testJournal.cpp
Normal file
173
Engine/source/core/util/journal/test/testJournal.cpp
Normal 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?");
|
||||
}
|
||||
};
|
||||
56
Engine/source/core/util/journal/test/testProcess.cpp
Normal file
56
Engine/source/core/util/journal/test/testProcess.cpp
Normal 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!");
|
||||
}
|
||||
};
|
||||
259
Engine/source/core/util/md5.cpp
Normal file
259
Engine/source/core/util/md5.cpp
Normal 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;
|
||||
}
|
||||
18
Engine/source/core/util/md5.h
Normal file
18
Engine/source/core/util/md5.h
Normal 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 */
|
||||
145
Engine/source/core/util/namedSingleton.h
Normal file
145
Engine/source/core/util/namedSingleton.h
Normal 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_
|
||||
37
Engine/source/core/util/noncopyable.h
Normal file
37
Engine/source/core/util/noncopyable.h
Normal 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
Loading…
Add table
Add a link
Reference in a new issue