Engine directory for ticket #1

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,436 @@
//-----------------------------------------------------------------------------
// 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/path.h"
namespace Torque
{
//-----------------------------------------------------------------------------
void Path::_split(String name)
{
S32 pos = 0;
S32 idx = 0;
// Make sure we have platform separators
name = PathToPlatform(name);
// root:
idx = name.find(':');
if (idx >= 0)
{
mRoot = name.substr(0,idx);
pos = idx + 1;
}
else if( name[ 0 ] == '/' )
{
mRoot = "/";
}
else
{
mRoot = "";
}
// Extract path and strip trailing '/'
idx = name.find('/', 0, String::Right);
if (idx >= pos)
{
int len = idx - pos;
mPath = name.substr(pos,len? len: 1);
mPath = Path::CleanSeparators(mPath);
pos = idx + 1;
}
else
{
mPath = "";
}
// file.ext
idx = name.find('.', 0, String::Right);
if (idx >= pos)
{
mFile = name.substr(pos,idx - pos);
mExt = name.substr(idx + 1,name.length() - idx - 1);
}
else
{
mFile = name.substr(pos,name.length() - pos);
mExt = "";
}
}
String Path::_join() const
{
String name;
if( mRoot != '/' )
name = Path::Join(mRoot, ':', mPath);
else
name = mPath;
name = Path::Join(name, '/', mFile);
name = Path::Join(name, '.', mExt);
return name;
}
String Path::CleanSeparators(String path)
{
return path.replace( '\\', '/' );
}
String Path::CompressPath(String path)
{
// Remove "./" and "../" references. A ".." will also result in the
// removal of the proceeding directory.
// Also cleans separators as in CleanSeparators().
// Assumes there are no trailing "/"
// Start by cleaning separators
path = Path::CleanSeparators(path);
U32 src = 0;
U32 dst = 0;
while (path[src])
{
if (path[src] == '/' && path[src + 1] == '/')
{
src += 1;
continue;
}
else if (path[src] == '.')
{
if (path[src + 1] == 0)
{
if (dst && path[dst - 1] == '/')
dst--;
src++;
break;
}
else if (path[src + 1] == '/')
{
src += 2;
continue;
}
else if (path[src + 1] == '.')
{
if (path[src + 2] == 0)
{
if (dst && path[dst - 1] == '/')
dst = path.find('/', dst - 1, String::Right);
src += 2;
break;
}
if (dst && path[dst - 1] == '/')
dst = path.find('/', dst - 1, String::Right) + 1;
else
dst += 3;
src += 3;
continue;
}
}
if (dst != src)
{
String end = path.substr(src, path.length() - src);
if (dst > 0 && end[(String::SizeType)0] == '/' && path[dst-1] == '/')
end = end.substr(1, end.length() - 1);
path.replace(dst, path.length() - dst, end);
src = dst;
}
else
{
src++;
dst++;
}
}
if (src - dst)
path.erase(dst, src - dst);
return path;
}
Torque::Path Path::MakeRelativePath( const Path &makeRelative, const Path &relativeTo, U32 mode )
{
// We need to find the part of makeRelative that starts to diverge from
// relativeTo. We only need to check up to the end of makeRelative or realtiveTo
// (whichever comes first).
U32 minDirCount = getMin(makeRelative.getDirectoryCount(), relativeTo.getDirectoryCount());
// Store the index of the directory where we diverge. If we never diverge this
// will end up being the same as the number of directories in makeRelative
U32 divergeDirIdx = 0;
for (divergeDirIdx = 0; divergeDirIdx < minDirCount; divergeDirIdx++)
{
// If our directories don't match then this is the diverge directory
if (!makeRelative.getDirectory(divergeDirIdx).equal(relativeTo.getDirectory(divergeDirIdx), mode))
break;
}
// Get the part of makeRelative's path after it diverged from relativeTo's path
String uniquePath;
// If we never diverged then divergeDirIdx will be equal to the number of
// directories in makeRelative and this loop will immediately exit
for (U32 i = divergeDirIdx; i < makeRelative.getDirectoryCount(); i++)
uniquePath += makeRelative.getDirectory(i) + "/";
// Go ahead and add the full file name
uniquePath += makeRelative.getFullFileName();
// Now calculate the relative offset
String offsetPath;
U32 numOffset = relativeTo.getDirectoryCount() - divergeDirIdx;
// Push back an "up" for each directory we are offset
for (U32 i = 0; i < numOffset; i++)
offsetPath += "../";
return offsetPath + uniquePath;
}
String Path::Join(const String& a,String::ValueType s,const String& b)
{
switch (s)
{
case '/':
{
if (b.isEmpty() || (b.length() == 1 && (b.c_str()[0] == '/')))
return a;
if (a.isEmpty())
return b;
String::ValueType c = a[a.length()-1];
if (c == ':' || ((c == '/') ^ (b.c_str()[0] == '/')))
return a + b;
if (c == '/' && b.c_str()[0] == '/')
return a.substr(0,a.length() - 1) + b;
break;
}
case ':':
{
if (a.isEmpty())
return b;
if (b.isEmpty())
return a + ':';
break;
}
case '.':
{
if (b.isEmpty())
return a;
if (a.isEmpty())
return '.' + b;
break;
}
default:
break;
}
return a + s + b;
}
bool Path::appendPath( const Path &p )
{
mPath = CompressPath(Join(mPath,'/',p.getPath()));
mIsDirtyPath = true;
return true;
}
const String &Path::getFullFileName() const
{
if ( mIsDirtyFileName )
{
mFullFileName = mFile;
if (mExt.isNotEmpty())
mFullFileName += '.' + mExt;
mIsDirtyFileName = false;
}
return mFullFileName;
}
const String& Path::getFullPath() const
{
if ( mIsDirtyPath )
{
mFullPath = _join();
mIsDirtyPath = false;
}
return mFullPath;
}
String Path::getFullPathWithoutRoot() const
{
return Torque::Path::Join(getPath(), '/', getFullFileName());
}
String Path::getRootAndPath() const
{
if( mRoot != '/' )
return Path::Join(mRoot, ':', mPath);
else
return mPath;
}
const String& Path::setRoot(const String &s)
{
if ( mRoot != s )
{
mIsDirtyPath = true;
mRoot = s;
}
return mRoot;
}
const String& Path::setPath(const String &s)
{
String clean = CleanSeparators(s);
if ( mPath != clean )
{
mIsDirtyPath = true;
mPath = clean;
}
return mPath;
}
const String& Path::setFileName(const String &s)
{
if ( mFile != s )
{
mIsDirtyPath = true;
mIsDirtyFileName = true;
mFile = s;
}
return mFile;
}
const String& Path::setExtension(const String &s)
{
if ( mExt != s )
{
mIsDirtyPath = true;
mIsDirtyFileName = true;
mExt = s;
}
return mExt;
}
bool Path::isDirectory() const
{
return mFile.isEmpty() && mExt.isEmpty();
}
bool Path::isRelative() const
{
return (mPath.isEmpty() || mPath.c_str()[0] != '/');
}
bool Path::isAbsolute() const
{
return (!mPath.isEmpty() && mPath.c_str()[0] == '/');
}
U32 Path::getDirectoryCount() const
{
if (mPath.isEmpty())
return 0;
U32 count = 0;
U32 offset = 0;
if (mPath.c_str()[0] == '/')
offset++;
while (offset < mPath.length())
{
if (mPath[offset++] == '/')
count++;
}
return count + 1;
}
String Path::getDirectory(U32 count) const
{
if (mPath.isEmpty())
return String();
U32 offset = 0;
if (mPath.c_str()[0] == '/')
offset++;
while (count && offset < mPath.length())
{
if (mPath[offset++] == '/')
count--;
}
U32 end = offset;
while (mPath[end] != '/' && end < mPath.length())
end++;
return mPath.substr(offset,end - offset);
}
//-----------------------------------------------------------------------------
String PathToPlatform(String file)
{
if (Path::OsSeparator != '/')
file.replace( Path::OsSeparator, '/' );
return file;
}
String PathToOS(String file)
{
if (Path::OsSeparator != '/')
file.replace( '/', Path::OsSeparator );
return file;
}
} // Namespace

View file

@ -0,0 +1,147 @@
//-----------------------------------------------------------------------------
// 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 _PATH_H_
#define _PATH_H_
#ifndef _TORQUE_STRING_H_
#include "core/util/str.h"
#endif
namespace Torque
{
//-----------------------------------------------------------------------------
/// FileSystem filename representation.
/// Filenames has the following form: "root:path/file.ext"
/// @ingroup UtilString
class Path
{
public:
enum Separator
{
#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XENON)
OsSeparator = '\\'
#else
OsSeparator = '/'
#endif
};
Path()
: mIsDirtyFileName( true ),
mIsDirtyPath( true )
{
}
Path( const char *file )
: mIsDirtyFileName( true ),
mIsDirtyPath( true )
{
_split(file);
}
Path( const String &file )
: mIsDirtyFileName( true ),
mIsDirtyPath( true )
{
_split(file);
}
Path& operator = ( const String &file ) { _split(file); mIsDirtyPath = mIsDirtyFileName = true; return *this; }
operator String() const { return getFullPath(); }
bool operator == (const Path& path) const { return getFullPath().equal(path.getFullPath()); }
bool operator != (const Path& path) const { return !(*this == path); }
bool isEmpty() const { return getFullPath().isEmpty(); }
/// Join two path or file name components together.
static String Join(const String&,String::ValueType,const String&);
/// Replace all '\' with '/'
static String CleanSeparators( String path );
/// Remove "." and ".." relative paths.
static String CompressPath( String path );
/// Take two paths and return the relative path between them.
static Path MakeRelativePath( const Path &makeRelative, const Path &relativeTo, U32 mode = String::NoCase );
const String& getRoot() const { return mRoot; }
const String& getPath() const { return mPath; }
const String& getFileName() const { return mFile; }
const String& getExtension() const { return mExt; }
const String& getFullFileName() const;
const String& getFullPath() const;
/// Returns the full file path without the volume root.
String getFullPathWithoutRoot() const;
/// Returns the root and path.
String getRootAndPath() const;
const String& setRoot(const String &s);
const String& setPath(const String &s);
const String& setFileName(const String &s);
const String& setExtension(const String &s);
U32 getDirectoryCount() const;
String getDirectory(U32) const;
bool isDirectory() const;
bool isRelative() const;
bool isAbsolute() const;
/// Appends the argument's path component to the object's
/// path component. The object's root, filename and
/// extension are unaffected.
bool appendPath(const Path &path);
private:
String mRoot;
String mPath;
String mFile;
String mExt;
mutable String mFullFileName;
mutable String mFullPath;
mutable bool mIsDirtyFileName;
mutable bool mIsDirtyPath;
void _split(String name);
String _join() const;
};
/// Convert file/path name to use platform standard path separator
///@ingroup VolumeSystem
String PathToPlatform(String file);
/// Convert file/path name to use OS standard path separator
///@ingroup VolumeSystem
String PathToOS(String file);
} // Namespace
#endif

View file

@ -0,0 +1,38 @@
//-----------------------------------------------------------------------------
// 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_PREPROCESSORHELPERS_H_
#define TORQUE_CORE_UTIL_PREPROCESSORHELPERS_H_
/// @defgroup preprocess_helpers Preprocessor Helpers
/// These are some handy preprocessor macros to simplify certain tasks, like
/// preprocessor concatenation that works properly with __LINE__.
#define _TORQUE_CONCAT(x, y) x ## y
/// @ingroup preprocess_helpers
/// This command concatenates two tokens in a way that will work with things such
/// as __LINE__.
/// @hideinitializer
#define TORQUE_CONCAT(x, y) _TORQUE_CONCAT(x, y)
#endif

View file

@ -0,0 +1,157 @@
//-----------------------------------------------------------------------------
// 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 _RAWDATA_H_
#define _RAWDATA_H_
#ifndef _PLATFORM_H_
# include "platform/platform.h"
#endif
#ifndef _TYPETRAITS_H_
# include "platform/typetraits.h"
#endif
template< typename T >
class RawDataT
{
public:
typedef void Parent;
typedef RawDataT< T > ThisType;
/// Type of elements in the buffer.
typedef T ValueType;
/// If true, the structure own the data buffer and will
/// delete[] it on destruction.
bool ownMemory;
/// The data buffer.
T *data;
/// Number of elements in the buffer.
U32 size;
RawDataT()
: ownMemory(false), data(NULL), size(0)
{
}
RawDataT( T* data, U32 size, bool ownMemory = false )
: data( data ), size( size ), ownMemory( ownMemory ) {}
RawDataT(const ThisType& rd)
{
data = rd.data;
size = rd.size;
ownMemory = false;
}
~RawDataT()
{
reset();
}
void reset()
{
if (ownMemory)
delete [] data;
data = NULL;
ownMemory = false;
size = 0;
}
void alloc(const U32 newSize)
{
reset();
ownMemory = true;
size = newSize;
data = new ValueType[newSize];
}
void operator =(const ThisType& rd)
{
data = rd.data;
size = rd.size;
ownMemory = false;
}
/// Allocate a RawDataT instance inline with its data elements.
///
/// @param Self RawDataT instance type; this is a type parameter so this
/// can work with types derived from RawDataT.
template< class Self >
static Self* allocInline( U32 numElements TORQUE_TMM_ARGS_DECL )
{
const char* file = __FILE__;
U32 line = __LINE__;
#ifndef TORQUE_DISABLE_MEMORY_MANAGER
file = fileName;
line = lineNum;
#endif
Self* inst = ( Self* ) dMalloc_r( sizeof( Self ) + numElements * sizeof( ValueType ), file, line );
ValueType* data = ( ValueType* ) ( inst + 1 );
constructArray< ValueType >( data, numElements );
return constructInPlace< Self >( inst, data, numElements );
}
};
template< typename T >
struct TypeTraits< RawDataT< T >* > : public TypeTraits< typename RawDataT< T >::Parent* >
{
struct Construct
{
template< typename R >
static R* single( U32 size )
{
typedef typename TypeTraits< R >::BaseType Type;
return Type::template allocInline< Type >( size TORQUE_TMM_LOC );
}
};
struct Destruct
{
template< typename R >
static void single( R* ptr )
{
destructInPlace( ptr );
dFree( ptr );
}
};
};
/// Raw byte buffer.
/// This isn't a typedef to allow forward declarations.
class RawData : public RawDataT< S8 >
{
public:
typedef RawDataT< S8 > Parent;
RawData() {}
RawData( S8* data, U32 size, bool ownMemory = false )
: Parent( data, size, ownMemory ) {}
};
#endif // _RAWDATA_H_

View file

@ -0,0 +1,473 @@
//-----------------------------------------------------------------------------
// 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 _REFBASE_H_
#define _REFBASE_H_
#ifndef _PLATFORMASSERT_H_
# include "platform/platformAssert.h"
#endif
#ifndef _TYPETRAITS_H_
# include "platform/typetraits.h"
#endif
/// Base class for objects which can be weakly referenced
/// (i.e., reference goes away when object is destroyed).
class WeakRefBase
{
public:
/// Weak reference to WeakRefBase.
class WeakReference
{
public:
WeakRefBase * get() { return mObject; }
void incRefCount() { mRefCount++; }
void decRefCount()
{
AssertFatal( mRefCount > 0, "WeakReference - decrementing count of zero!" );
if (--mRefCount==0)
delete this;
}
U32 getRefCount() { return mRefCount; }
private:
friend class WeakRefBase;
WeakReference(WeakRefBase *object) { mObject = object; mRefCount = 0; }
~WeakReference() { AssertFatal(mObject==NULL, "Deleting weak reference which still points at an object."); }
// Object we reference
WeakRefBase *mObject;
// reference count for this structure (not WeakObjectRef itself)
U32 mRefCount;
};
public:
WeakRefBase() { mReference = NULL; }
virtual ~WeakRefBase() { clearWeakReferences(); }
WeakReference * getWeakReference();
protected:
void clearWeakReferences();
private:
WeakReference * mReference;
};
template< typename T > class SimObjectPtr;
/// Weak reference pointer class.
/// Instances of this template class can be used as pointers to
/// instances of WeakRefBase and its subclasses.
/// When the object referenced by a WeakRefPtr instance is deleted,
/// the pointer to the object is set to NULL in the WeakRefPtr instance.
template <class T> class WeakRefPtr
{
public:
WeakRefPtr() { mReference = NULL; }
WeakRefPtr(T *ptr) { mReference = NULL; set(ptr); }
WeakRefPtr(const WeakRefPtr<T> & ref) { mReference = NULL; set(ref.mReference); }
~WeakRefPtr() { set((WeakRefBase::WeakReference*)NULL); }
WeakRefPtr<T>& operator=(const WeakRefPtr<T>& ref)
{
set(ref.mReference);
return *this;
}
WeakRefPtr<T>& operator=(T *ptr)
{
set(ptr);
return *this;
}
/// Returns true if the pointer is not set.
bool isNull() const { return mReference == NULL || mReference->get() == NULL; }
/// Returns true if the pointer is set.
bool isValid() const { return mReference && mReference->get(); }
T* operator->() const { return getPointer(); }
T& operator*() const { return *getPointer(); }
operator T*() const { return getPointer(); }
/// Returns the pointer.
T* getPointer() const { return mReference ? ( T* ) mReference->get() : NULL; }
protected:
void set(WeakRefBase::WeakReference * ref)
{
if (mReference)
mReference->decRefCount();
mReference = NULL;
if (ref)
{
mReference = ref;
mReference->incRefCount();
}
}
void set(T * obj) { set(obj ? obj->getWeakReference() : (WeakRefBase::WeakReference *)NULL); }
private:
template< typename > friend class SimObjectPtr;
WeakRefBase::WeakReference * mReference;
};
/// Union of an arbitrary type with a WeakRefBase. The exposed type will
/// remain accessible so long as the WeakRefBase is not cleared. Once
/// it is cleared, accessing the exposed type will result in a NULL pointer.
template<class ExposedType>
class WeakRefUnion
{
typedef WeakRefUnion<ExposedType> Union;
public:
WeakRefUnion() : mPtr(NULL) {}
WeakRefUnion(const WeakRefPtr<WeakRefBase> & ref, ExposedType * ptr) : mPtr(NULL) { set(ref, ptr); }
WeakRefUnion(const Union & lock) : mPtr(NULL) { *this = lock; }
WeakRefUnion(WeakRefBase * ref) : mPtr(NULL) { set(ref, dynamic_cast<ExposedType*>(ref)); }
~WeakRefUnion() { mWeakReference = NULL; }
Union & operator=(const Union & ptr)
{
set(ptr.mWeakReference, ptr.getPointer());
return *this;
}
void set(const WeakRefPtr<WeakRefBase> & ref, ExposedType * ptr)
{
mWeakReference = ref;
mPtr = ptr;
}
bool operator == (const ExposedType * t ) const { return getPointer() == t; }
bool operator != (const ExposedType * t ) const { return getPointer() != t; }
bool operator == (const Union &t ) const { return getPointer() == t.getPointer(); }
bool operator != (const Union &t ) const { return getPointer() != t.getPointer(); }
bool isNull() const { return mWeakReference.isNull() || !mPtr; }
ExposedType* getPointer() const { return !mWeakReference.isNull() ? mPtr : NULL; }
ExposedType* operator->() const { return getPointer(); }
ExposedType& operator*() const { return *getPointer(); }
operator ExposedType*() const { return getPointer(); }
WeakRefPtr<WeakRefBase> getRef() const { return mWeakReference; }
private:
WeakRefPtr<WeakRefBase> mWeakReference;
ExposedType * mPtr;
};
/// Base class for objects which can be strongly referenced
/// (i.e., as long as reference exists, object will exist,
/// when all strong references go away, object is destroyed).
class StrongRefBase : public WeakRefBase
{
friend class StrongObjectRef;
public:
StrongRefBase() { mRefCount = 0; }
U32 getRefCount() const { return mRefCount; }
/// object destroy self call (from StrongRefPtr). Override if this class has specially allocated memory.
virtual void destroySelf() { delete this; }
/// Increments the reference count.
void incRefCount()
{
mRefCount++;
}
/// Decrements the reference count.
void decRefCount()
{
AssertFatal(mRefCount, "Decrementing a reference with refcount 0!");
if(!--mRefCount)
destroySelf();
}
protected:
U32 mRefCount; ///< reference counter for StrongRefPtr objects
};
/// Base class for StrongRefBase strong reference pointers.
class StrongObjectRef
{
public:
/// Constructor, assigns from the object and increments its reference count if it's not NULL
StrongObjectRef(StrongRefBase *object = NULL) : mObject( object )
{
mObject = object;
incRef();
}
/// Destructor, dereferences the object, if there is one
~StrongObjectRef() { decRef(); }
/// Assigns this reference object from an existing StrongRefBase instance
void set(StrongRefBase *object)
{
if( mObject != object )
{
decRef();
mObject = object;
incRef();
}
}
protected:
StrongRefBase *mObject; ///< the object this RefObjectRef references
/// increments the reference count on the referenced object
void incRef()
{
if(mObject)
mObject->incRefCount();
}
/// decrements the reference count on the referenced object
void decRef()
{
if(mObject)
mObject->decRefCount();
}
};
/// Reference counted object template pointer class
/// Instances of this template class can be used as pointers to
/// instances of StrongRefBase and its subclasses. The object will not
/// be deleted until all of the StrongRefPtr instances pointing to it
/// have been destructed.
template <class T> class StrongRefPtr : protected StrongObjectRef
{
public:
StrongRefPtr() : StrongObjectRef() {}
StrongRefPtr(T *ptr) : StrongObjectRef(ptr) {}
StrongRefPtr(const StrongRefPtr<T>& ref) : StrongObjectRef(ref.mObject) {}
~StrongRefPtr() {}
StrongRefPtr<T>& operator=(const StrongRefPtr<T>& ref)
{
set(ref.mObject);
return *this;
}
StrongRefPtr<T>& operator=(T *ptr)
{
set(ptr);
return *this;
}
bool isNull() const { return mObject == 0; }
bool isValid() const { return mObject != 0; }
T* operator->() const { return getPointer(); }
T& operator*() const { return *getPointer(); }
operator T*() const { return getPointer(); }
T* getPointer() const { return const_cast<T*>(static_cast<T* const>(mObject)); }
};
/// Union of an arbitrary type with a StrongRefBase. StrongRefBase will remain locked
/// until the union is destructed. Handy for when the exposed class will
/// become invalid whenever the reference becomes invalid and you want to make sure that doesn't
/// happen.
template<class ExposedType>
class StrongRefUnion
{
typedef StrongRefUnion<ExposedType> Union;
public:
StrongRefUnion() : mPtr(NULL) {}
StrongRefUnion(const StrongRefPtr<StrongRefBase> & ref, ExposedType * ptr) : mPtr(NULL) { set(ref, ptr); }
StrongRefUnion(const Union & lock) : mPtr(NULL) { *this = lock; }
StrongRefUnion(StrongRefBase * ref) : mPtr(NULL) { set(ref, dynamic_cast<ExposedType*>(ref)); }
~StrongRefUnion() { mReference = NULL; }
Union & operator=(const Union & ptr)
{
set(ptr.mReference, ptr.mPtr);
return *this;
}
void set(const StrongRefPtr<StrongRefBase> & ref, ExposedType * ptr)
{
mReference = ref;
mPtr = ptr;
}
bool operator == (const ExposedType * t ) const { return mPtr == t; }
bool operator != (const ExposedType * t ) const { return mPtr != t; }
bool operator == (const Union &t ) const { return mPtr == t.mPtr; }
bool operator != (const Union &t ) const { return mPtr != t.mPtr; }
bool isNull() const { return mReference.isNull() || !mPtr; }
bool isValid() const { return mReference.isValid() && mPtr; }
ExposedType* operator->() const { return mPtr; }
ExposedType& operator*() const { return *mPtr; }
operator ExposedType*() const { return mPtr; }
ExposedType* getPointer() const { return mPtr; }
StrongRefPtr<StrongRefBase> getRef() const { return mReference; }
private:
StrongRefPtr<StrongRefBase> mReference;
ExposedType * mPtr;
};
/// This oxymoron is a pointer that reference-counts the referenced
/// object but also NULLs out if the object goes away.
///
/// This is useful for situations where an object's lifetime is ultimately
/// governed by a superior entity but where individual objects may also die
/// independently of the superior entity. All client code should use
/// StrongWeakRefs that keep object live as long as the superior entity doesn't
/// step in and kill them (in which case, the client code sees the reference
/// disappear).
template< class T >
class StrongWeakRefPtr
{
public:
StrongWeakRefPtr() : mReference( NULL ) {}
StrongWeakRefPtr( T* ptr ) : mReference( NULL ) { _set( ptr ); }
~StrongWeakRefPtr()
{
if( mReference )
{
T* ptr = _get();
if( ptr )
ptr->decRefCount();
mReference->decRefCount();
}
}
bool isNull() const { return ( _get() == NULL ); }
bool operator ==( T* ptr ) const { return ( _get() == ptr ); }
bool operator !=( T* ptr ) const { return ( _get() != ptr ); }
bool operator !() const { return isNull(); }
T* operator ->() const { return _get(); }
T& operator *() const { return *( _get() ); }
operator T*() const { return _get(); }
T* getPointer() const { return _get(); }
StrongWeakRefPtr& operator =( T* ptr )
{
_set( ptr );
return *this;
}
private:
WeakRefBase::WeakReference* mReference;
T* _get() const
{
if( mReference )
return static_cast< T* >( mReference->get() );
else
return NULL;
}
void _set( T* ptr )
{
if( mReference )
{
T* old = _get();
if( old )
old->decRefCount();
mReference->decRefCount();
}
if( ptr )
{
ptr->incRefCount();
mReference = ptr->getWeakReference();
mReference->incRefCount();
}
else
mReference = NULL;
}
};
//---------------------------------------------------------------
inline void WeakRefBase::clearWeakReferences()
{
if (mReference)
{
mReference->mObject = NULL;
mReference->decRefCount();
mReference = NULL;
}
}
inline WeakRefBase::WeakReference * WeakRefBase::getWeakReference()
{
if (!mReference)
{
mReference = new WeakReference(this);
mReference->incRefCount();
}
return mReference;
}
//---------------------------------------------------------------
template< typename T >
struct TypeTraits< WeakRefPtr< T > > : public _TypeTraits< WeakRefPtr< T > >
{
typedef typename TypeTraits< T >::BaseType BaseType;
};
template< typename T >
struct TypeTraits< StrongRefPtr< T > > : public _TypeTraits< StrongRefPtr< T > >
{
typedef typename TypeTraits< T >::BaseType BaseType;
};
template< typename T >
struct TypeTraits< StrongWeakRefPtr< T > > : public _TypeTraits< StrongWeakRefPtr< T > >
{
typedef typename TypeTraits< T >::BaseType BaseType;
};
template< typename T >
inline T& Deref( WeakRefPtr< T >& ref )
{
return *ref;
}
template< typename T >
inline T& Deref( StrongRefPtr< T >& ref )
{
return *ref;
}
template< typename T >
inline T& Deref( StrongWeakRefPtr< T >& ref )
{
return *ref;
}
#endif

View file

@ -0,0 +1,58 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/util/rgb2luv.h"
#include "core/util/rgb2xyz.h"
#include "math/mPoint3.h"
#include "math/mPoint2.h"
namespace ConvertRGB
{
ColorF toLUV( const ColorF &rgbColor )
{
static const Point3F scXYZLUVDot( 1.0f, 15.0f, 3.0f );
static const Point2F sc49( 4.0f, 9.0f );
ColorF xyzColor = ConvertRGB::toXYZ( rgbColor );
const Point2F &xyz_xy = *((Point2F *)&xyzColor);
Point2F uvColor = sc49;
uvColor.convolve( xyz_xy );
uvColor /= mDot( *(Point3F *)&xyzColor, scXYZLUVDot );
return ColorF( uvColor.x, uvColor.y, xyzColor.green, rgbColor.alpha );
}
ColorF toLUVScaled( const ColorF &rgbColor )
{
ColorF luvColor = toLUV( rgbColor );
luvColor.red /= 0.62f;
luvColor.green /= 0.62f;
return luvColor;
}
}

View file

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

View file

@ -0,0 +1,67 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/util/rgb2xyz.h"
#include "math/mMatrix.h"
namespace ConvertRGB
{
// http://www.w3.org/Graphics/Color/sRGB
static const F32 scRGB2XYZ[] =
{
0.4124f, 0.3576f, 0.1805f, 0.0f,
0.2126f, 0.7152f, 0.0722f, 0.0f,
0.0193f, 0.1192f, 0.9505f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
};
static const F32 scXYZ2RGB[] =
{
3.2410f, -1.5374f, -0.4986f, 0.0f,
-0.9692f, 1.8760f, 0.0416f, 0.0f,
0.0556f, -0.2040f, 1.0570f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
};
ColorF toXYZ( const ColorF &rgbColor )
{
const MatrixF &rgb2xyz = *((MatrixF *)scRGB2XYZ);
ColorF retColor = rgbColor;
rgb2xyz.mul( *(Point4F *)&retColor );
return retColor;
}
ColorF fromXYZ( const ColorF &xyzColor )
{
const MatrixF &xyz2rgb = *((MatrixF *)scXYZ2RGB);
ColorF retColor = xyzColor;
xyz2rgb.mul( *(Point4F *)&retColor );
return retColor;
}
}

View file

@ -0,0 +1,34 @@
//-----------------------------------------------------------------------------
// 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 _RGB2XYZ_H_
#define _RGB2XYZ_H_
#include "core/color.h"
namespace ConvertRGB
{
ColorF toXYZ( const ColorF &rgbColor );
ColorF fromXYZ( const ColorF &xyzColor );
};
#endif

View file

@ -0,0 +1,60 @@
//-----------------------------------------------------------------------------
// 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_SAFECAST_H_
#define _TORQUE_CORE_UTIL_SAFECAST_H_
#include "platform/platform.h"
template< class T, typename I >
inline T* safeCast( I* inPtr )
{
if( !inPtr )
return 0;
else
{
T* outPtr = dynamic_cast< T* >( inPtr );
AssertFatal( outPtr != 0, "safeCast failed" );
return outPtr;
}
}
template<>
inline void* safeCast< void >( void* inPtr )
{
return inPtr;
}
template< class T, typename I >
inline T* safeCastISV( I* inPtr )
{
if( !inPtr )
return 0;
else
{
T* outPtr = dynamic_cast< T* >( inPtr );
AssertISV( outPtr != 0, "safeCast failed" );
return outPtr;
}
}
#endif // _TORQUE_CORE_UTIL_SAFECAST_H_

View file

@ -0,0 +1,94 @@
//-----------------------------------------------------------------------------
// 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_SAFEDELETE_H_
#define _TORQUE_SAFEDELETE_H_
/// @addtogroup utility_macros Utility Macros
// @{
#undef SAFE_DELETE
//-----------------------------------------------------------------------------
/// @brief Safely delete an object and set the pointer to NULL
///
/// @param a Object to delete
/// @see #SAFE_DELETE_ARRAY(), #SAFE_DELETE_OBJECT(), #SAFE_FREE(), #SAFE_FREE_REFERENCE()
//-----------------------------------------------------------------------------
#define SAFE_DELETE(a) {delete (a); (a) = NULL; }
#undef SAFE_DELETE_ARRAY
//-----------------------------------------------------------------------------
/// @brief Safely delete an array and set the pointer to NULL
///
/// @param a Array to delete
/// @see #SAFE_DELETE(), #SAFE_DELETE_OBJECT(), #SAFE_FREE(), #SAFE_FREE_REFERENCE()
//-----------------------------------------------------------------------------
#define SAFE_DELETE_ARRAY(a) { delete [] (a); (a) = NULL; }
#undef SAFE_DELETE_OBJECT
//-----------------------------------------------------------------------------
/// @brief Safely delete a SimObject and set the pointer to NULL
///
/// @param a Object to delete
/// @see #SAFE_DELETE_ARRAY(), #SAFE_DELETE(), #SAFE_FREE(), #SAFE_FREE_REFERENCE()
//-----------------------------------------------------------------------------
#define SAFE_DELETE_OBJECT(a) { if( (a) != NULL ) (a)->deleteObject(); (a) = NULL; }
#undef SAFE_FREE
//-----------------------------------------------------------------------------
/// @brief Safely free memory and set the pointer to NULL
///
/// @param a Pointer to memory to free
/// @see #SAFE_DELETE_ARRAY(), #SAFE_DELETE_OBJECT(), #SAFE_DELETE(), #SAFE_FREE_REFERENCE()
//-----------------------------------------------------------------------------
#define SAFE_FREE(a) { if( (a) != NULL ) dFree ((void *)a); (a) = NULL; }
// CodeReview: Is the NULL conditional needed? [5/14/2007 Pat]
#undef SAFE_FREE_REFERENCE
//-----------------------------------------------------------------------------
/// @brief Safely free a reference to a Message and set the pointer to NULL
///
/// @param a Pointer to message to free
/// @see #SAFE_DELETE_ARRAY(), #SAFE_DELETE_OBJECT(), #SAFE_FREE(), #SAFE_DELETE()
//-----------------------------------------------------------------------------
#define SAFE_FREE_REFERENCE(a) { if((a) != NULL) (a)->freeReference(); (a) = NULL; }
#undef SAFE_DELETE_MESSAGE
//-----------------------------------------------------------------------------
/// @brief Synonym for SAFE_FREE_REFERENCE()
///
/// @param a Object to delete
/// @see #SAFE_DELETE(), #SAFE_DELETE_ARRAY(), #SAFE_DELETE_OBJECT(), #SAFE_FREE(), #SAFE_FREE_REFERENCE()
//-----------------------------------------------------------------------------
#define SAFE_DELETE_MESSAGE SAFE_FREE_REFERENCE
// @}
#endif // _TORQUE_SAFEDELETE_H_

View file

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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,399 @@
//-----------------------------------------------------------------------------
// 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_STRING_H_
#define _TORQUE_STRING_H_
#include <cstdarg>
#ifndef _TORQUE_TYPES_H_
#include "platform/types.h"
#endif
template< class T > class Vector;
typedef UTF8 StringChar;
/// The String class represents a 0-terminated array of characters.
class String
{
public:
class StringData;
/// Default mode is case sensitive starting from the left
enum Mode
{
Case = 0, ///< Case sensitive
NoCase = 1, ///< Case insensitive
Left = 0, ///< Start at left end of string
Right = 2, ///< Start at right end of string
};
typedef U32 SizeType;
typedef StringChar ValueType;
static const SizeType NPos; ///< Indicates 'not found' when using find() functions
/// A predefined empty string.
static const String EmptyString;
String();
String(const String &str);
String(const StringChar *str);
String(const StringChar *str, SizeType size);
String(const UTF16 *str);
~String();
const UTF8 *c_str() const; ///< Return the string as a native type
const UTF16 *utf16() const;
const UTF8* utf8() const { return c_str(); }
SizeType length() const; ///< Returns the length of the string in bytes.
SizeType size() const; ///< Returns the length of the string in bytes including the NULL terminator.
SizeType numChars() const; ///< Returns the length of the string in characters.
bool isEmpty() const; ///< Is this an empty string [""]?
bool isNotEmpty() const { return !isEmpty(); } ///< Is this not an empty string [""]?
/// Erases all characters in a string.
void clear() { *this = EmptyString; }
bool isShared() const; ///< Is this string's reference count greater than 1?
bool isSame( const String& str ) const; ///< Return true if both strings refer to the same shared data.
U32 getHashCaseSensitive() const; ///< Get the case-sensitive hash of the string [only calculates the hash as necessary]
U32 getHashCaseInsensitive() const; ///< Get the case-insensitive hash of the string [only calculates the hash as necessary]
String& operator=(StringChar);
String& operator+=(StringChar);
String& operator=(const StringChar*);
String& operator+=(const StringChar*);
String& operator=(const String&);
String& operator+=(const String&);
/**
Compare this string with another.
@param str The string to compare against.
@param len If len is non-zero, then at most len characters are compared.
@param mode Comparison mode.
@return Difference between the first two characters that don't match.
*/
S32 compare(const StringChar *str, SizeType len = 0, U32 mode = Case|Left) const;
S32 compare(const String &str, SizeType len = 0, U32 mode = Case|Left) const; ///< @see compare(const StringChar *, SizeType, U32) const
/**
Compare two strings for equality.
It will use the string hashes to determine inequality.
@param str The string to compare against.
@param mode Comparison mode - case sensitive or not.
*/
bool equal(const String &str, U32 mode = Case) const;
SizeType find(StringChar c, SizeType pos = 0, U32 mode = Case|Left) const;
SizeType find(const StringChar *str, SizeType pos = 0, U32 mode = Case|Left) const;
SizeType find(const String &str, SizeType pos = 0, U32 mode = Case|Left) const;
String &insert(SizeType pos, const StringChar c) { return insert(pos,&c,1); }
String &insert(SizeType pos, const StringChar *str);
String &insert(SizeType pos, const String &str);
String &insert(SizeType pos, const StringChar *str, SizeType len);
String &erase(SizeType pos, SizeType len);
String &replace(SizeType pos, SizeType len, const StringChar *str);
String &replace(SizeType pos, SizeType len, const String &str);
/// Replace all occurrences of character 'c1' with 'c2'
String &replace( StringChar c1, StringChar c2 );
/// Replace all occurrences of StringData 's1' with StringData 's2'
String &replace(const String &s1, const String &s2);
String substr( SizeType pos, SizeType len = -1 ) const;
/// Remove leading and trailing whitespace.
String trim() const;
/// Replace all characters that need to be escaped for the string to be a valid string literal with their
/// respective escape sequences.
String expandEscapes() const;
/// Replace all escape sequences in with their respective character codes.
String collapseEscapes() const;
/// Split the string into its components separated by the given delimiter.
void split( const char* delimiter, Vector< String >& outElements ) const;
/// Return true if the string starts with the given text.
bool startsWith( const char* text ) const;
/// Return true if the string ends with the given text.
bool endsWith( const char* text ) const;
operator const StringChar*() const { return c_str(); }
StringChar operator []( U32 i ) const { return c_str()[i]; }
StringChar operator []( S32 i ) const { return c_str()[i]; }
bool operator==(const String &str) const;
bool operator!=(const String &str) const { return !(*this == str); }
bool operator==( StringChar c ) const;
bool operator!=( StringChar c ) const { return !(*this == c); }
bool operator<(const String &str) const;
bool operator>(const String &str) const;
bool operator<=(const String &str) const;
bool operator>=(const String &str) const;
friend String operator+(const String &a, StringChar c);
friend String operator+(StringChar c, const String &a);
friend String operator+(const String &a, const StringChar *b);
friend String operator+(const String &a, const String &b);
friend String operator+(const StringChar *a, const String &b);
public:
/// @name String Utility routines
/// @{
static String ToString(const char *format, ...);
static String VToString(const char* format, void* args);
static String ToString( bool v );
static inline String ToString( U32 v ) { return ToString( "%u", v ); }
static inline String ToString( S32 v ) { return ToString( "%d", v ); }
static inline String ToString( F32 v ) { return ToString( "%g", v ); }
static inline String ToString( F64 v ) { return ToString( "%Lg", v ); }
static String SpanToString(const char* start, const char* end);
static String ToLower(const String &string);
static String ToUpper(const String &string);
static String GetTrailingNumber(const char* str, S32& number);
/// @}
/// @name Interning
///
/// Interning maps identical strings to unique instances so that equality
/// amounts to simple pointer comparisons.
///
/// Note that using interned strings within global destructors is not safe
/// as table destruction runs within this phase as well. Uses o interned
/// strings in global destructors is thus dependent on object file ordering.
///
/// Also, interned strings are not reference-counted. Once interned, a
/// string will persist until shutdown. This is to avoid costly concurrent
/// reference counting that would otherwise be necessary.
///
/// @{
/// Return the interned version of the string.
/// @note Interning is case-sensitive.
String intern() const;
/// Return true if this string is interned.
bool isInterned() const;
/// @}
/** An internal support class for ToString().
StrFormat manages the formatting of arbitrary length strings.
The class starts with a default internal fixed size buffer and
moves to dynamic allocation from the heap when that is exceeded.
Constructing the class on the stack will result in its most
efficient use. This class is meant to be used as a helper class,
and not for the permanent storage of string data.
@code
char* indexString(U32 index)
{
StrFormat format("Index: %d",index);
char* str = new char[format.size()];
format.copy(str);
return str;
}
@endcode
*/
class StrFormat
{
public:
StrFormat()
: _dynamicBuffer( NULL ),
_dynamicSize( 0 ),
_len( 0 )
{
_fixedBuffer[0] = '\0';
}
StrFormat(const char *formatStr, void *args)
: _dynamicBuffer( NULL ),
_dynamicSize( 0 ),
_len( 0 )
{
format(formatStr, args);
}
~StrFormat();
S32 format( const char *format, void *args );
S32 formatAppend( const char *format, void *args );
S32 append(const char * str, S32 len);
S32 append(const char * str);
String getString() { return String(c_str(),_len); }
const char * c_str() const { return _dynamicBuffer ? _dynamicBuffer : _fixedBuffer; }
void reset()
{
_len = 0;
_fixedBuffer[0] = '\0';
}
/// Copy the formatted string into the output buffer which must be at least size() characters.
char *copy(char* buffer) const;
/// Return the length of the formated string (does not include the terminating 0)
U32 length() const { return _len; };
public:
char _fixedBuffer[2048]; //< Fixed size buffer
char *_dynamicBuffer; //< Temporary format buffer
U32 _dynamicSize; //< Dynamic buffer size
U32 _len; //< Len of the formatted string
};
private:
String(StringData *str)
: _string( str ) {}
// Generate compile error if operator bool is used. Without this we use
// operator const char *, which is always true...including operator bool
// causes an ambiguous cast compile error. Making it private is simply
// more insurance that it isn't used on different compilers.
// NOTE: disable on GCC since it causes hyper casting to U32 on gcc.
#ifndef TORQUE_COMPILER_GCC
operator const bool() const { return false; }
#endif
static void copy(StringChar *dst, const StringChar *src, U32 size);
StringData *_string;
};
// Utility class for formatting strings.
class StringBuilder
{
protected:
///
String::StrFormat mFormat;
public:
StringBuilder() {}
U32 length() const
{
return mFormat.length();
}
void copy( char* buffer ) const
{
mFormat.copy( buffer );
}
const char* data() const
{
return mFormat.c_str();
}
String end()
{
return mFormat.getString();
}
S32 append( char ch )
{
char str[2];
str[0]=ch;
str[1]='\0';
return mFormat.append(str);
}
S32 append( const char* str )
{
return mFormat.append(str);
}
S32 append( const String& str )
{
return mFormat.append( str.c_str(), str.length() );
}
S32 append( const char* str, U32 length )
{
return mFormat.append(str,length);
}
S32 format( const char* fmt, ... )
{
va_list args;
va_start(args, fmt);
return mFormat.formatAppend(fmt, &args);
}
};
// For use in hash tables and the like for explicitly requesting case sensitive hashing.
// Meant to only appear in hash table definition (casting will take care of the rest).
class StringCase : public String
{
public:
StringCase() : String() {}
StringCase(const String & s) : String(s) {}
};
// For use in hash tables and the like for explicitly requesting case insensitive hashing.
// Meant to only appear in hash table definition (casting will take care of the rest).
class StringNoCase : public String
{
public:
StringNoCase() : String() {}
StringNoCase(const String & s) : String(s) {}
};
class FileName : public String
{
public:
FileName() : String() {}
FileName(const String & s) : String(s) {}
FileName & operator=(const String & s) { String::operator=(s); return *this; }
};
//-----------------------------------------------------------------------------
extern String operator+(const String &a, StringChar c);
extern String operator+(StringChar c, const String &a);
extern String operator+(const String &a, const StringChar *b);
extern String operator+(const String &a, const String &b);
extern String operator+(const StringChar *a, const String &b);
#endif

View file

@ -0,0 +1,164 @@
//-----------------------------------------------------------------------------
// 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 _SWIZZLE_H_
#define _SWIZZLE_H_
#include "platform/platform.h"
#include "core/frameAllocator.h"
/// This class will swizzle 'sizeof( T )' length chunks of memory into different
/// patterns which are user described. The pattern is described by an instance
/// of Swizzle and this swizzle can then be executed on buffers. The following
/// must be true of the buffer size:
/// size % ( sizeof( T ) * mapLength ) == 0
template<class T, dsize_t mapLength>
class Swizzle
{
private:
/// This is an array from 0..n. Each entry in the array is a dsize_t with values
/// in the range 0..n. Each value in the range 0..n can occur any number of times.
///
/// For example:
/// This is our data set, an array of characters with 4 elements
/// { 'a', 'b', 'c', 'd' }
///
/// If the map { 3, 2, 1, 0 } was applied to this set, the result would be:
/// { 'd', 'c', 'b', 'a' }
///
/// If the map { 3, 0, 2, 2 } was applied to the set, the result would be:
/// { 'd', 'a', 'c', 'c' }
dsize_t mMap[mapLength];
public:
/// Construct a swizzle
/// @see Swizzle::mMap
Swizzle( const dsize_t *map );
virtual ~Swizzle(){}
/// This method will, in the general case, use the ToBuffer method to swizzle
/// the memory specified into a temporary buffer, allocated by FrameTemp, and
/// then copy the temporary memory into the source memory.
///
/// @param memory Pointer to the start of the buffer to swizzle
/// @param size Size of the buffer
virtual void InPlace( void *memory, const dsize_t size ) const;
/// This method copies the data from source to destination while applying the
/// re-ordering. This method is, in the non-specalized case, O(N^2) where N
/// is sizeof( T ) / size; the number of instances of type 'T' in the buffer
///
/// @param destination The destination of the swizzled data
/// @param source The source data to be swizzled
/// @param size Size of the source and destination buffers.
virtual void ToBuffer( void *destination, const void *source, const dsize_t size ) const;
};
// Null swizzle
template<class T, dsize_t mapLength>
class NullSwizzle : public Swizzle<T, mapLength>
{
public:
NullSwizzle( const dsize_t *map = NULL ) : Swizzle<T, mapLength>( map ) {};
virtual void InPlace( void *memory, const dsize_t size ) const {}
virtual void ToBuffer( void *destination, const void *source, const dsize_t size ) const
{
dMemcpy( destination, source, size );
}
};
//------------------------------------------------------------------------------
// Common Swizzles
namespace Swizzles
{
extern Swizzle<U8, 4> bgra;
extern Swizzle<U8, 4> argb;
extern Swizzle<U8, 4> rgba;
extern Swizzle<U8, 4> abgr;
extern Swizzle<U8, 3> bgr;
extern Swizzle<U8, 3> rgb;
extern NullSwizzle<U8, 4> null;
};
//------------------------------------------------------------------------------
template<class T, dsize_t mapLength>
Swizzle<T, mapLength>::Swizzle( const dsize_t *map )
{
if( map != NULL )
dMemcpy( mMap, map, sizeof( dsize_t ) * mapLength );
}
//------------------------------------------------------------------------------
template<class T, dsize_t mapLength>
inline void Swizzle<T, mapLength>::ToBuffer( void *destination, const void *source, const dsize_t size ) const
{
// TODO: OpenMP?
AssertFatal( size % ( sizeof( T ) * mapLength ) == 0, "Bad buffer size for swizzle, see docs." );
AssertFatal( destination != NULL, "Swizzle::ToBuffer - got a NULL destination pointer!" );
AssertFatal( source != NULL, "Swizzle::ToBuffer - got a NULL source pointer!" );
T *dest = reinterpret_cast<T *>( destination );
const T *src = reinterpret_cast<const T *>( source );
for( int i = 0; i < size / ( mapLength * sizeof( T ) ); i++ )
{
dMemcpy( dest, src, mapLength * sizeof( T ) );
for( int j = 0; j < mapLength; j++ )
*dest++ = src[mMap[j]];
src += mapLength;
}
}
//------------------------------------------------------------------------------
template<class T, dsize_t mapLength>
inline void Swizzle<T, mapLength>::InPlace( void *memory, const dsize_t size ) const
{
// Just in case the inliner messes up the FrameTemp scoping (not sure if it would) -patw
{
// FrameTemp should work because the PNG loading code uses the FrameAllocator, so
// it should only get used on an image w/ that size as max -patw
FrameTemp<U8> buffer( size );
dMemcpy( ~buffer, memory, size );
ToBuffer( memory, ~buffer, size );
}
}
//------------------------------------------------------------------------------
// Template specializations for certain swizzles
//#include "core/util/swizzleSpec.h"
#ifdef TORQUE_OS_XENON
# include "platformXbox/altivecSwizzle.h"
#endif
#endif

View file

@ -0,0 +1,112 @@
//-----------------------------------------------------------------------------
// 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 _SWIZZLESPEC_H_
#define _SWIZZLESPEC_H_
//------------------------------------------------------------------------------
// <U8, 4> (most common) Specialization
//------------------------------------------------------------------------------
#include "core/util/byteswap.h"
template<>
inline void Swizzle<U8, 4>::InPlace( void *memory, const dsize_t size ) const
{
AssertFatal( size % 4 == 0, "Bad buffer size for swizzle, see docs." );
U8 *dest = reinterpret_cast<U8 *>( memory );
U8 *src = reinterpret_cast<U8 *>( memory );
// Fast divide by 4 since we are assured a proper size
for( int i = 0; i < size >> 2; i++ )
{
BYTESWAP( *dest++, src[mMap[0]] );
BYTESWAP( *dest++, src[mMap[1]] );
BYTESWAP( *dest++, src[mMap[2]] );
BYTESWAP( *dest++, src[mMap[3]] );
src += 4;
}
}
template<>
inline void Swizzle<U8, 4>::ToBuffer( void *destination, const void *source, const dsize_t size ) const
{
AssertFatal( size % 4 == 0, "Bad buffer size for swizzle, see docs." );
U8 *dest = reinterpret_cast<U8 *>( destination );
const U8 *src = reinterpret_cast<const U8 *>( source );
// Fast divide by 4 since we are assured a proper size
for( int i = 0; i < size >> 2; i++ )
{
*dest++ = src[mMap[0]];
*dest++ = src[mMap[1]];
*dest++ = src[mMap[2]];
*dest++ = src[mMap[3]];
src += 4;
}
}
//------------------------------------------------------------------------------
// <U8, 3> Specialization
//------------------------------------------------------------------------------
template<>
inline void Swizzle<U8, 3>::InPlace( void *memory, const dsize_t size ) const
{
AssertFatal( size % 3 == 0, "Bad buffer size for swizzle, see docs." );
U8 *dest = reinterpret_cast<U8 *>( memory );
U8 *src = reinterpret_cast<U8 *>( memory );
for( int i = 0; i < size /3; i++ )
{
BYTESWAP( *dest++, src[mMap[0]] );
BYTESWAP( *dest++, src[mMap[1]] );
BYTESWAP( *dest++, src[mMap[2]] );
src += 3;
}
}
template<>
inline void Swizzle<U8, 3>::ToBuffer( void *destination, const void *source, const dsize_t size ) const
{
AssertFatal( size % 3 == 0, "Bad buffer size for swizzle, see docs." );
U8 *dest = reinterpret_cast<U8 *>( destination );
const U8 *src = reinterpret_cast<const U8 *>( source );
for( int i = 0; i < size / 3; i++ )
{
*dest++ = src[mMap[0]];
*dest++ = src[mMap[1]];
*dest++ = src[mMap[2]];
src += 3;
}
}
#endif

View file

@ -0,0 +1,203 @@
//-----------------------------------------------------------------------------
// 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 _ALIGNEDARRAY_H_
#define _ALIGNEDARRAY_H_
/// This is a fixed size class that will align its elements on configurable boundaries.
template<typename T>
class AlignedArray
{
public:
AlignedArray();
/// Create an AlignedArray
/// @param arraySize How many items
/// @param elementSize Size of each element (including padding)
AlignedArray(const U32 arraySize, const U32 elementSize);
/// Create an AlignedArray
/// @param arraySize How many items
/// @param elementSize Size of each element (including padding)
/// @param buffer Preallocated buffer (with data and aligned on elementSize boundaries)
/// @param takeOwn If true, this class will take ownership of the buffer and free on destruct
AlignedArray(const U32 arraySize, const U32 elementSize, U8* buffer, bool takeOwn);
~AlignedArray();
void setCapacity(const U32 arraySize, const U32 elementSize);
void setCapacity(const U32 arraySize, const U32 elementSize, U8* buffer, bool takeOwn);
/// Size of the array
U32 size() const;
/// Set a new array size (up to initial size created returned by capacity)
void setSize(U32 newsize);
/// Capacity of the array (you can setCapacity the size this high)
U32 capacity() const;
/// Returns the size of an element (useful for asserting, etc)
U32 getElementSize() const;
/// Returns a pointer to the raw buffer data.
const void* getBuffer() const;
void* getBuffer();
// Returns the buffer size in bytes.
U32 getBufferSize() const { return mElementSize * mElementCount; }
// Operators
T& operator[](const U32);
const T& operator[](const U32) const;
protected:
// How big an element is, this includes padding need to align
U32 mElementSize;
// How many elements do we have
U32 mElementCount;
// How many elements can we have
U32 mCapacity;
// Storage, we use placement new and reinterpret casts to deal with
// alignment
U8* mBuffer;
// Do we own this buffer? Or are we just wrapping it?
bool mOwnBuffer;
};
template<typename T>
inline AlignedArray<T>::AlignedArray()
{
mElementSize = 0;
mElementCount = 0;
mCapacity = 0;
mBuffer = NULL;
mOwnBuffer = true;
}
template<typename T>
inline AlignedArray<T>::AlignedArray(const U32 arraySize, const U32 elementSize)
{
mElementCount = 0; // avoid debug assert
setCapacity(arraySize, elementSize);
}
template<typename T>
inline AlignedArray<T>::AlignedArray(const U32 arraySize, const U32 elementSize, U8* buffer, bool takeOwn)
{
mElementCount = 0; // avoid debug assert
setCapacity(arraySize, elementSize, buffer, takeOwn);
}
template<typename T>
inline AlignedArray<T>::~AlignedArray()
{
if (mOwnBuffer)
delete[] mBuffer;
}
template<typename T>
inline void AlignedArray<T>::setCapacity(const U32 arraySize, const U32 elementSize)
{
AssertFatal(mElementCount == 0, "Unable to set array properties after they are init'ed");
AssertFatal(elementSize >= sizeof(T), "Element size is too small!");
AssertFatal(arraySize > 0, "0 length AlignedArrays are not allowed!");
mElementSize = elementSize;
mElementCount = arraySize;
mCapacity = arraySize;
mBuffer = new U8[mElementSize * mElementCount];
dMemset(mBuffer, 0xFF, mElementSize * mElementCount);
U32 bufIndex = 0;
for (U32 i = 0; i < mElementCount; i++)
{
T* item = reinterpret_cast<T*>(&mBuffer[bufIndex]);
constructInPlace(item);
bufIndex += mElementSize;
}
mOwnBuffer = true;
}
template<typename T>
inline void AlignedArray<T>::setCapacity(const U32 arraySize, const U32 elementSize, U8* buffer, bool takeOwn)
{
AssertFatal(mElementCount == 0, "Unable to set array properties after they are init'ed");
AssertFatal(elementSize >= sizeof(T), "Element size is too small!");
AssertFatal(arraySize > 0, "0 length AlignedArrays are not allowed!");
AssertFatal(buffer, "NULL buffer!");
mElementSize = elementSize;
mElementCount = arraySize;
mCapacity = arraySize;
mBuffer = buffer;
mOwnBuffer = takeOwn;
}
/// Set a new array size (up to initial size created returned by capacity)
template<typename T>
inline void AlignedArray<T>::setSize(U32 newsize)
{
AssertFatal(newsize <= mCapacity, "Unable to grow this type of array!");
mElementCount = newsize;
}
template<typename T>
inline U32 AlignedArray<T>::size() const
{
return mElementCount;
}
template<typename T>
inline U32 AlignedArray<T>::capacity() const
{
return mCapacity;
}
/// Returns the size of an element (useful for asserting, etc)
template<typename T>
U32 AlignedArray<T>::getElementSize() const
{
return mElementSize;
}
template<typename T>
inline T& AlignedArray<T>::operator[](const U32 index)
{
AssertFatal(index < mElementCount, "AlignedArray<T>::operator[] - out of bounds array access!");
return reinterpret_cast<T&>(mBuffer[index*mElementSize]);
}
template<typename T>
inline const T& AlignedArray<T>::operator[](const U32 index) const
{
AssertFatal(index < mElementCount, "AlignedArray<T>::operator[] - out of bounds array access!");
return reinterpret_cast<const T&>(mBuffer[index*mElementSize]);
}
template<typename T>
const void* AlignedArray<T>::getBuffer() const
{
return reinterpret_cast<const void*>(mBuffer);
}
template<typename T>
void* AlignedArray<T>::getBuffer()
{
return reinterpret_cast<void*>(mBuffer);
}
#endif // _ALIGNEDARRAY_H_

View file

@ -0,0 +1,47 @@
//-----------------------------------------------------------------------------
// 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/tDictionary.h"
namespace DictHash
{
static U32 Primes[] = {
53ul, 97ul, 193ul, 389ul, 769ul,
1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
1610612741ul, 3221225473ul, 4294967291ul
};
U32 nextPrime(U32 size)
{
U32 len = sizeof(Primes) / sizeof(U32);
for(U32 i=0; i<len; i++)
if(Primes[i] > size)
return Primes[i];
return Primes[len-1];
}
};

View file

@ -0,0 +1,867 @@
//-----------------------------------------------------------------------------
// 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 _TDICTIONARY_H_
#define _TDICTIONARY_H_
#ifndef _STRINGFUNCTIONS_H_
#include "core/strings/stringFunctions.h"
#endif
#ifndef _HASHFUNCTION_H_
#include "core/util/hashFunction.h"
#endif
#ifndef _TORQUE_STRING_H_
#include "core/util/str.h"
#endif
#ifndef _DATACHUNKER_H_
#include "core/dataChunker.h"
#endif
// TODO: Maybe move these into a more general Tuple class?
template<class A, class B>
struct CompoundKey
{
A key1;
B key2;
CompoundKey() {};
CompoundKey(const A & a, const B & b) { key1 = a; key2 = b; };
bool operator==(const CompoundKey & compound) const { return key1==compound.key1 && key2==compound.key2; }
};
template<class A, class B, class C>
struct CompoundKey3
{
A key1;
B key2;
C key3;
CompoundKey3() {};
CompoundKey3(const A & a, const B & b, const C & c) { key1 = a; key2 = b; key3 = c;};
bool operator==(const CompoundKey3 & compound) const { return key1==compound.key1 && key2==compound.key2 && key3==compound.key3; }
};
namespace DictHash
{
inline U32 hash(U32 data)
{
return data;
}
inline U32 hash(const StringCase &data)
{
return data.getHashCaseSensitive();
}
inline U32 hash(const StringNoCase &data)
{
return data.getHashCaseInsensitive();
}
inline U32 hash(const String &data)
{
return data.getHashCaseInsensitive();
}
inline U32 hash(const char *data)
{
return Torque::hash( (const U8 *)data, dStrlen( data ), 0 );
}
inline U32 hash(const void *data)
{
return (U32)data;
}
template<class A, class B>
inline U32 hash(const CompoundKey<A,B> & compound)
{
return hash(compound.key1) + hash(compound.key2);
}
template<class A, class B, class C>
inline U32 hash(const CompoundKey3<A,B,C> & compound)
{
return hash(compound.key1) + hash(compound.key2) + hash(compound.key3);
}
U32 nextPrime(U32);
};
namespace KeyCmp
{
template<typename Key>
inline bool equals( const Key &keya, const Key &keyb )
{
return ( keya == keyb );
}
template<>
inline bool equals<>( const StringCase &keya, const StringCase &keyb )
{
return ( keya.equal( keyb, String::Case ) );
}
template<>
inline bool equals<>( const StringNoCase &keya, const StringNoCase &keyb )
{
return ( keya.equal( keyb, String::NoCase ) );
}
template<>
inline bool equals<>( const String &keya, const String &keyb )
{
return ( keya.equal( keyb, String::NoCase ) );
}
template<>
inline bool equals<>( const char * const &keya, const char * const &keyb )
{
return ( dStrcmp( keya, keyb ) == 0 );
}
};
/// A HashTable template class.
///
/// The hash table class maps between a key and an associated value. Access
/// using the key is performed using a hash table. The class provides
/// methods for both unique and equal keys. The global ::hash(Type) function
/// is used for hashing, see util/hash.h
/// @ingroup UtilContainers
template<typename Key, typename Value >
class HashTable
{
public:
struct Pair
{
Key key;
Value value;
Pair() {}
Pair(Key k,Value v)
: key(k),
value(v)
{}
};
private:
struct Node
{
Node* mNext;
Pair mPair;
Node(): mNext(0) {}
Node(Pair p,Node* n)
: mNext(n),
mPair(p)
{}
};
Node** mTable; ///< Hash table
S32 mTableSize; ///< Hash table size
U32 mSize; ///< Number of keys in the table
ClassChunker<Node> mNodeAllocator;
U32 _hash(const Key& key) const;
U32 _index(const Key& key) const;
Node* _next(U32 index) const;
void _resize(U32 size);
void _destroy();
public:
// Iterator support
template<typename U,typename E, typename M>
class _Iterator {
friend class HashTable;
E* mLink;
M* mHashTable;
operator E*();
public:
typedef U ValueType;
typedef U* Pointer;
typedef U& Reference;
_Iterator()
{
mHashTable = 0;
mLink = 0;
}
_Iterator(M* table,E* ptr)
{
mHashTable = table;
mLink = ptr;
}
_Iterator& operator++()
{
mLink = mLink->mNext? mLink->mNext :
mHashTable->_next(mHashTable->_index(mLink->mPair.key) + 1);
return *this;
}
_Iterator operator++(int)
{
_Iterator itr(*this);
++(*this);
return itr;
}
bool operator==(const _Iterator& b) const
{
return mHashTable == b.mHashTable && mLink == b.mLink;
}
bool operator!=(const _Iterator& b) const
{
return !(*this == b);
}
U* operator->() const
{
return &mLink->mPair;
}
U& operator*() const
{
return mLink->mPair;
}
};
// Types
typedef Pair ValueType;
typedef Pair& Reference;
typedef const Pair& ConstReference;
typedef _Iterator<Pair,Node,HashTable> Iterator;
typedef _Iterator<const Pair,const Node,const HashTable> ConstIterator;
typedef S32 DifferenceType;
typedef U32 SizeType;
// Initialization
HashTable();
~HashTable();
HashTable(const HashTable& p);
// Management
U32 size() const; ///< Return the number of elements
U32 tableSize() const; ///< Return the size of the hash bucket table
void clear(); ///< Empty the HashTable
void resize(U32 size);
bool isEmpty() const; ///< Returns true if the table is empty
F32 collisions() const; ///< Returns the average number of nodes per bucket
// Insert & erase elements
Iterator insertEqual(const Key& key, const Value&);
Iterator insertUnique(const Key& key, const Value&);
void erase(Iterator); ///< Erase the given entry
void erase(const Key& key); ///< Erase all matching keys from the table
void erase(const Key & key, const Value & value); ///< Erase entry for this key-value pair
// HashTable lookup
Iterator findOrInsert(const Key& key);
Iterator find(const Key&); ///< Find the first entry for the given key
ConstIterator find(const Key&) const; ///< Find the first entry for the given key
bool find(const Key & key, Value & value); ///< Find the first entry for the given key
S32 count(const Key&) const; ///< Count the number of matching keys in the table
// Forward Iterator access
Iterator begin(); ///< Iterator to first element
ConstIterator begin() const; ///< Iterator to first element
Iterator end(); ///< Iterator to last element + 1
ConstIterator end() const; ///< Iterator to last element + 1
void operator=(const HashTable& p);
};
template<typename Key, typename Value> HashTable<Key,Value>::HashTable() : mNodeAllocator(512)
{
mTableSize = 0;
mTable = 0;
mSize = 0;
}
template<typename Key, typename Value> HashTable<Key,Value>::HashTable(const HashTable& p) : mNodeAllocator(512)
{
mSize = 0;
mTableSize = 0;
mTable = 0;
*this = p;
}
template<typename Key, typename Value> HashTable<Key,Value>::~HashTable()
{
_destroy();
}
//-----------------------------------------------------------------------------
template<typename Key, typename Value>
inline U32 HashTable<Key,Value>::_hash(const Key& key) const
{
return DictHash::hash(key);
}
template<typename Key, typename Value>
inline U32 HashTable<Key,Value>::_index(const Key& key) const
{
return _hash(key) % mTableSize;
}
template<typename Key, typename Value>
typename HashTable<Key,Value>::Node* HashTable<Key,Value>::_next(U32 index) const
{
for (; index < mTableSize; index++)
if (Node* node = mTable[index])
return node;
return 0;
}
template<typename Key, typename Value>
void HashTable<Key,Value>::_resize(U32 size)
{
S32 currentSize = mTableSize;
mTableSize = DictHash::nextPrime(size);
Node** table = new Node*[mTableSize];
dMemset(table,0,mTableSize * sizeof(Node*));
for (S32 i = 0; i < currentSize; i++)
for (Node* node = mTable[i]; node; )
{
// Get groups of matching keys
Node* last = node;
while (last->mNext && last->mNext->mPair.key == node->mPair.key)
last = last->mNext;
// Move the chain to the new table
Node** link = &table[_index(node->mPair.key)];
Node* tmp = last->mNext;
last->mNext = *link;
*link = node;
node = tmp;
}
delete[] mTable;
mTable = table;
}
template<typename Key, typename Value>
void HashTable<Key,Value>::_destroy()
{
// Call destructors.
for (S32 i = 0; i < mTableSize; i++)
for (Node* ptr = mTable[i]; ptr; )
{
Node *tmp = ptr;
ptr = ptr->mNext;
destructInPlace( tmp );
}
mNodeAllocator.freeBlocks();
delete[] mTable;
mTable = NULL;
}
//-----------------------------------------------------------------------------
// management
template<typename Key, typename Value>
inline U32 HashTable<Key,Value>::size() const
{
return mSize;
}
template<typename Key, typename Value>
inline U32 HashTable<Key,Value>::tableSize() const
{
return mTableSize;
}
template<typename Key, typename Value>
inline void HashTable<Key,Value>::clear()
{
_destroy();
mTableSize = 0;
mSize = 0;
}
/// Resize the bucket table for an estimated number of elements.
/// This method will optimize the hash bucket table size to hold the given
/// number of elements. The size argument is a hint, and will not be the
/// exact size of the table. If the table is sized down below it's optimal
/// size, the next insert will cause it to be resized again. Normally this
/// function is used to avoid resizes when the number of elements that will
/// be inserted is known in advance.
template<typename Key, typename Value>
inline void HashTable<Key,Value>::resize(U32 size)
{
// Attempt to resize the datachunker as well.
mNodeAllocator.setChunkSize(sizeof(Node) * size);
_resize(size);
}
template<typename Key, typename Value>
inline bool HashTable<Key,Value>::isEmpty() const
{
return mSize == 0;
}
template<typename Key, typename Value>
inline F32 HashTable<Key,Value>::collisions() const
{
S32 chains = 0;
for (S32 i = 0; i < mTableSize; i++)
if (mTable[i])
chains++;
return F32(mSize) / chains;
}
//-----------------------------------------------------------------------------
// add & remove elements
/// Insert the key value pair but don't insert duplicates.
/// This insert method does not insert duplicate keys. If the key already exists in
/// the table the function will fail and end() is returned.
template<typename Key, typename Value>
typename HashTable<Key,Value>::Iterator HashTable<Key,Value>::insertUnique(const Key& key, const Value& x)
{
if (mSize >= mTableSize)
_resize(mSize + 1);
Node** table = &mTable[_index(key)];
for (Node* itr = *table; itr; itr = itr->mNext)
if ( KeyCmp::equals<Key>( itr->mPair.key, key) )
return end();
mSize++;
Node* newNode = mNodeAllocator.alloc();
newNode->mPair = Pair(key, x);
newNode->mNext = *table;
*table = newNode;
return Iterator(this,*table);
}
/// Insert the key value pair and allow duplicates.
/// This insert method allows duplicate keys. Keys are grouped together but
/// are not sorted.
template<typename Key, typename Value>
typename HashTable<Key,Value>::Iterator HashTable<Key,Value>::insertEqual(const Key& key, const Value& x)
{
if (mSize >= mTableSize)
_resize(mSize + 1);
// The new key is inserted at the head of any group of matching keys.
Node** prev = &mTable[_index(key)];
for (Node* itr = *prev; itr; prev = &itr->mNext, itr = itr->mNext)
if ( KeyCmp::equals<Key>( itr->mPair.key, key ) )
break;
mSize++;
Node* newNode = mNodeAllocator.alloc();
newNode->mPair = Pair(key, x);
newNode->mNext = *prev;
*prev = newNode;
return Iterator(this,*prev);
}
template<typename Key, typename Value>
void HashTable<Key,Value>::erase(const Key& key)
{
if (mTable==NULL)
return;
Node** prev = &mTable[_index(key)];
for (Node* itr = *prev; itr; prev = &itr->mNext, itr = itr->mNext)
if ( KeyCmp::equals<Key>( itr->mPair.key, key ) ) {
// Delete matching keys, which should be grouped together.
do {
Node* tmp = itr;
itr = itr->mNext;
mNodeAllocator.free(tmp);
mSize--;
} while (itr && KeyCmp::equals<Key>( itr->mPair.key, key ) );
*prev = itr;
return;
}
}
template<typename Key, typename Value>
void HashTable<Key,Value>::erase(Iterator node)
{
if (mTable==NULL)
return;
Node** prev = &mTable[_index(node->key)];
for (Node* itr = *prev; itr; prev = &itr->mNext, itr = itr->mNext)
{
if (itr == node.mLink)
{
*prev = itr->mNext;
mNodeAllocator.free(itr);
mSize--;
return;
}
}
}
template<typename Key, typename Value>
void HashTable<Key,Value>::erase(const Key & key, const Value & value)
{
if (mTable==NULL)
return;
Node** prev = &mTable[_index(key)];
for (Node* itr = *prev; itr; prev = &itr->mNext, itr = itr->mNext)
{
if ( KeyCmp::equals<Key>( itr->mPair.key, key ) && itr->mPair.value == value)
{
*prev = itr->mNext;
mNodeAllocator.free(itr);
mSize--;
return;
}
}
}
//-----------------------------------------------------------------------------
/// Find the key, or insert a one if it doesn't exist.
/// Returns the first key in the table that matches, or inserts one if there
/// are none.
template<typename Key, typename Value>
typename HashTable<Key,Value>::Iterator HashTable<Key,Value>::findOrInsert(const Key& key)
{
if (mSize >= mTableSize)
_resize(mSize + 1);
Node** table = &mTable[_index(key)];
for (Node* itr = *table; itr; itr = itr->mNext)
if ( KeyCmp::equals<Key>( itr->mPair.key, key ) )
return Iterator(this,itr);
mSize++;
Node* newNode = mNodeAllocator.alloc();
newNode->mPair = Pair(key, Value());
newNode->mNext = *table;
*table = newNode;
return Iterator(this,*table);
}
template<typename Key, typename Value>
typename HashTable<Key,Value>::Iterator HashTable<Key,Value>::find(const Key& key)
{
if (mTableSize)
for (Node* itr = mTable[_index(key)]; itr; itr = itr->mNext)
if ( KeyCmp::equals<Key>( itr->mPair.key, key ) )
return Iterator(this,itr);
return Iterator(this,0);
}
template<typename Key, typename Value>
typename HashTable<Key,Value>::ConstIterator HashTable<Key,Value>::find(const Key& key) const
{
if (mTableSize)
{
for (Node* itr = mTable[_index(key)]; itr; itr = itr->mNext)
{
if ( KeyCmp::equals<Key>( itr->mPair.key, key ) )
return ConstIterator(this,itr);
}
}
return ConstIterator(this,0);
}
template<typename Key, typename Value>
bool HashTable<Key,Value>::find(const Key & key, Value & value)
{
if (mTableSize)
{
for (Node* itr = mTable[_index(key)]; itr; itr = itr->mNext)
if ( KeyCmp::equals<Key>( itr->mPair.key, key ) )
{
value = itr->mPair.value;
return true;
}
}
return false;
}
template<typename Key, typename Value>
S32 HashTable<Key,Value>::count(const Key& key) const
{
S32 count = 0;
if (mTableSize)
for (Node* itr = mTable[_index(key)]; itr; itr = itr->mNext)
if ( KeyCmp::equals<Key>( itr->mPair.key, key ) ) {
// Matching keys should be grouped together.
do {
count++;
itr = itr->mNext;
} while (itr && KeyCmp::equals<Key>( itr->mPair.key, key ) );
break;
}
return count;
}
//-----------------------------------------------------------------------------
// Iterator access
template<typename Key, typename Value>
inline typename HashTable<Key,Value>::Iterator HashTable<Key,Value>::begin()
{
return Iterator(this,_next(0));
}
template<typename Key, typename Value>
inline typename HashTable<Key,Value>::ConstIterator HashTable<Key,Value>::begin() const
{
return ConstIterator(this,_next(0));
}
template<typename Key, typename Value>
inline typename HashTable<Key,Value>::Iterator HashTable<Key,Value>::end()
{
return Iterator(this,0);
}
template<typename Key, typename Value>
inline typename HashTable<Key,Value>::ConstIterator HashTable<Key,Value>::end() const
{
return ConstIterator(this,0);
}
//-----------------------------------------------------------------------------
// operators
template<typename Key, typename Value>
void HashTable<Key,Value>::operator=(const HashTable& p)
{
_destroy();
mTableSize = p.mTableSize;
mTable = new Node*[mTableSize];
mSize = p.mSize;
for (S32 i = 0; i < mTableSize; i++)
if (Node* itr = p.mTable[i])
{
Node** head = &mTable[i];
do
{
*head = new Node(itr->mPair,0);
head = &(*head)->mNext;
itr = itr->mNext;
} while (itr);
}
else
mTable[i] = 0;
}
//-----------------------------------------------------------------------------
// Iterator class
/// A Map template class.
/// The map class maps between a key and an associated value. Keys
/// are unique.
/// The hash table class is used as the default implementation so the
/// the key must be hashable, see util/hash.h for details.
/// @ingroup UtilContainers
template<typename Key, typename Value, class Sequence = HashTable<Key,Value> >
class Map
{
public:
// types
typedef typename Sequence::Pair Pair;
typedef Pair ValueType;
typedef Pair& Reference;
typedef const Pair& ConstReference;
typedef typename Sequence::Iterator Iterator;
typedef typename Sequence::ConstIterator ConstIterator;
typedef S32 DifferenceType;
typedef U32 SizeType;
// initialization
Map() {}
~Map() {}
Map(const Map& p);
// management
U32 size() const; ///< Return the number of elements
void resize(U32 size);
void clear(); ///< Empty the Map
bool isEmpty() const; ///< Returns true if the map is empty
// insert & erase elements
Iterator insert(const Key& key, const Value&); // Documented below...
void erase(Iterator); ///< Erase the given entry
void erase(const Key& key); ///< Erase the key from the map
// Map lookup
Iterator find(const Key&); ///< Find entry for the given key
ConstIterator find(const Key&) const; ///< Find entry for the given key
bool contains(const Key&a) const
{
return mMap.count(a) > 0;
}
/// Try to get the value of the specified key. Returns true and fills in the value
/// if the key exists. Returns false otherwise.
/// Unlike operator [], this function does not create the key/value if the key
/// is not found.
bool tryGetValue(const Key& key, Value& val) const
{
ConstIterator iter = find(key);
if (iter != end())
{
val = (*iter).value;
return true;
}
return false;
}
// forward Iterator access
Iterator begin(); ///< Iterator to first element
ConstIterator begin() const; ///< Iterator to first element
Iterator end(); ///< IIterator to last element + 1
ConstIterator end() const; ///< Iterator to last element + 1
// operators
/// Index using the given key. If the key is not currently in the map it is added.
/// If you just want to try to get the value without the side effect of creating the
/// key, use tryGetValue() instead.
Value& operator[](const Key&);
private:
Sequence mMap;
};
template<typename Key, typename Value, class Sequence> Map<Key,Value,Sequence>::Map(const Map& p)
{
*this = p;
}
//-----------------------------------------------------------------------------
// management
template<typename Key, typename Value, class Sequence>
inline U32 Map<Key,Value,Sequence>::size() const
{
return mMap.size();
}
template<typename Key, typename Value, class Sequence>
inline void Map<Key,Value,Sequence>::resize(U32 size)
{
return mMap.resize(size);
}
template<typename Key, typename Value, class Sequence>
inline void Map<Key,Value,Sequence>::clear()
{
mMap.clear();
}
template<typename Key, typename Value, class Sequence>
inline bool Map<Key,Value,Sequence>::isEmpty() const
{
return mMap.isEmpty();
}
//-----------------------------------------------------------------------------
// add & remove elements
/// Insert the key value pair but don't allow duplicates.
/// The map class does not allow duplicates keys. If the key already exists in
/// the map the function will fail and return end().
template<typename Key, typename Value, class Sequence>
typename Map<Key,Value,Sequence>::Iterator Map<Key,Value,Sequence>::insert(const Key& key, const Value& x)
{
return mMap.insertUnique(key,x);
}
template<typename Key, typename Value, class Sequence>
void Map<Key,Value,Sequence>::erase(const Key& key)
{
mMap.erase(key);
}
template<typename Key, typename Value, class Sequence>
void Map<Key,Value,Sequence>::erase(Iterator node)
{
mMap.erase(node);
}
//-----------------------------------------------------------------------------
// Searching
template<typename Key, typename Value, class Sequence>
typename Map<Key,Value,Sequence>::Iterator Map<Key,Value,Sequence>::find(const Key& key)
{
return mMap.find(key);
}
template<typename Key, typename Value, class Sequence>
typename Map<Key,Value,Sequence>::ConstIterator Map<Key,Value,Sequence>::find(const Key& key) const
{
return mMap.find(key);
}
//-----------------------------------------------------------------------------
// Iterator access
template<typename Key, typename Value, class Sequence>
inline typename Map<Key,Value,Sequence>::Iterator Map<Key,Value,Sequence>::begin()
{
return mMap.begin();
}
template<typename Key, typename Value, class Sequence>
inline typename Map<Key,Value,Sequence>::ConstIterator Map<Key,Value,Sequence>::begin() const
{
return mMap.begin();
}
template<typename Key, typename Value, class Sequence>
inline typename Map<Key,Value,Sequence>::Iterator Map<Key,Value,Sequence>::end()
{
return mMap.end();
}
template<typename Key, typename Value, class Sequence>
inline typename Map<Key,Value,Sequence>::ConstIterator Map<Key,Value,Sequence>::end() const
{
return mMap.end();
}
//-----------------------------------------------------------------------------
// operators
template<typename Key, typename Value, class Sequence>
inline Value& Map<Key,Value,Sequence>::operator[](const Key& key)
{
return mMap.findOrInsert(key)->value;
}
#endif

View file

@ -0,0 +1,185 @@
//-----------------------------------------------------------------------------
// 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 _TFIXEDSIZEDEQUE_H_
#define _TFIXEDSIZEDEQUE_H_
#ifndef _PLATFORM_H_
# include "platform/platform.h"
#endif
/// A double-ended queue with a fixed number of entries set at
/// construction time. Implemented as an array.
///
/// @param T Element type of the queue.
template< typename T >
class FixedSizeDeque
{
public:
/// Type for elements stored in the deque.
typedef T ValueType;
protected:
///
U32 mBufferSize;
///
U32 mSize;
/// Index
U32 mStart;
///
U32 mEnd;
///
ValueType* mBuffer;
public:
///
FixedSizeDeque( U32 bufferSize )
: mBufferSize( bufferSize ), mSize( 0 ), mStart( 0 ), mEnd( 0 )
{
// Don't use new() so we don't get a buffer full of instances.
mBuffer = ( ValueType* ) dMalloc( sizeof( ValueType ) * bufferSize );
}
~FixedSizeDeque()
{
// Destruct remaining instances.
while( mSize )
{
destructInPlace( &mBuffer[ mStart ] );
mStart ++;
if( mStart == mBufferSize )
mStart = 0;
mSize --;
}
// Free buffer.
dFree( mBuffer );
}
///
bool isEmpty() const { return !size(); }
///
U32 size() const
{
return mSize;
}
///
U32 capacity() const
{
return ( mBufferSize - size() );
}
///
void clear()
{
while( size() )
popFront();
}
/// Return the leftmost value in the queue.
ValueType front() const
{
AssertFatal( !isEmpty(), "FixedSizeDeque::front() - queue is empty" );
return mBuffer[ mStart ];
}
/// Return the rightmost value in the queue.
ValueType back() const
{
AssertFatal( !isEmpty(), "FixedSizeDeque::back() - queue is empty" );
if( !mEnd )
return mBuffer[ mBufferSize - 1 ];
else
return mBuffer[ mEnd - 1 ];
}
/// Prepend "value" to the left end of the queue.
void pushFront( const ValueType& value )
{
AssertFatal( capacity() != 0, "FixedSizeDeque::pushFront() - queue is full" );
if( mStart == 0 )
mStart = mBufferSize - 1;
else
mStart --;
mBuffer[ mStart ] = value;
mSize ++;
}
/// Append "value" to the right end of the queue.
void pushBack( const ValueType& value )
{
AssertFatal( capacity() != 0, "FixedSizeDeque::pushBack() - queue is full" );
mBuffer[ mEnd ] = value;
mEnd ++;
if( mEnd == mBufferSize )
mEnd = 0;
mSize ++;
}
/// Remove and return the leftmost value in the queue.
ValueType popFront()
{
AssertFatal( !isEmpty(), "FixedSizeDeque::popFront() - queue is empty" );
ValueType value = mBuffer[ mStart ];
destructInPlace( &mBuffer[ mStart ] );
mStart ++;
if( mStart == mBufferSize )
mStart = 0;
mSize --;
return value;
}
/// Remove and return the rightmost value in the queue.
ValueType popBack()
{
AssertFatal( !isEmpty(), "FixedSizeDeque::popBack() - queue is empty" );
U32 idx;
if( !mEnd )
idx = mBufferSize - 1;
else
idx = mEnd - 1;
ValueType value = mBuffer[ idx ];
destructInPlace( &mBuffer[ idx ] );
mEnd = idx;
mSize --;
return value;
}
};
#endif // _TFIXEDSIZEDEQUE_H_

View file

@ -0,0 +1,106 @@
//-----------------------------------------------------------------------------
// 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 _TFIXEDSIZEVECTOR_H_
#define _TFIXEDSIZEVECTOR_H_
/// A vector with a compile-time constant size.
template< typename T, int SIZE >
class FixedSizeVector
{
protected:
T mArray[ SIZE ];
public:
typedef T* iterator;
typedef const T* const_iterator;
FixedSizeVector() {}
FixedSizeVector( const T& a ) { mArray[ 0 ] = a; }
FixedSizeVector( const T& a, const T& b ) { mArray[ 0 ] = a; mArray[ 1 ] = b; }
FixedSizeVector( const T& a, const T& b, const T& c ) { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; }
FixedSizeVector( const T& a, const T& b, const T& c, const T& d ) { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; }
FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e ) { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; }
FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f )
{ mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; }
FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g )
{ mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; }
FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h )
{ mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; }
FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h, const T& i )
{ mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; mArray[ 8 ] = i; }
FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h, const T& i, const T& j )
{ mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; mArray[ 8 ] = i; mArray[ 9 ] = j; }
FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h, const T& i, const T& j, const T& k )
{ mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; mArray[ 8 ] = i; mArray[ 9 ] = j; mArray[ 10 ] = k; }
FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h, const T& i, const T& j, const T& k, const T& l )
{ mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; mArray[ 8 ] = i; mArray[ 9 ] = j; mArray[ 10 ] = k; mArray[ 11 ] = l; }
FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h, const T& i, const T& j, const T& k, const T& l, const T& m )
{ mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; mArray[ 8 ] = i; mArray[ 9 ] = j; mArray[ 10 ] = k; mArray[ 11 ] = l; mArray[ 12 ] =m; }
U32 size() const { return SIZE; }
bool empty() const { return ( SIZE == 0 ); }
const T* address() const { return mArray; }
T* address() { return mArray; }
iterator begin() { return &mArray[ 0 ]; }
iterator end() { return &mArray[ SIZE ]; }
const_iterator begin() const { return &mArray[ 0 ]; }
const_iterator end() const { return &mArray[ SIZE ]; }
const T& first() const
{
AssertFatal( !empty(), "FixedSizeVector::first - Vector is empty" );
return mArray[ 0 ];
}
T& first()
{
AssertFatal( !empty(), "FixedSizeVector::first - Vector is empty" );
return mArray[ 0 ];
}
const T& last() const
{
AssertFatal( !empty(), "FixedSizeVector::last - Vector is empty" );
return mArray[ size() - 1 ];
}
T& last()
{
AssertFatal( !empty(), "FixedSizeVector::last - Vector is empty" );
return mArray[ size() - 1 ];
}
const T& operator []( U32 index ) const
{
AssertFatal( index <= size(), "FixedSizeVector::operator[] - Index out of range" );
return mArray[ index ];
}
T& operator []( U32 index )
{
AssertFatal( index <= size(), "FixedSizeVector::operator[] - Index out of range" );
return mArray[ index ];
}
};
#endif // !_TFIXEDSIZEVECTOR_H_

View file

@ -0,0 +1,490 @@
//-----------------------------------------------------------------------------
// 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_LIST_
#define _TORQUE_LIST_
#ifndef _TORQUE_TYPES_H_
#include "platform/types.h"
#endif
namespace Torque
{
/// A list template class.
/// A classic list class similar to the STL list class. The list
/// class supports fast insert and erase operations, but slow dynamic
/// access. The list is circular and the iterator supports iterating
/// past the end() or rend() in order to loop around.
/// @ingroup UtilContainers
template<typename Type>
class List
{
struct Link;
struct PrivatePersist {}; ///< Used instead of a (void *) for type-safety of persistent iterators
public:
/// A PersistentIter is used for the special cases where you need to store an iterator for
/// efficiency reasons. The _Iterator class handles the conversion to/from PersistentIter.
/// @note The data in the node this points to could go away, so only use these in cases where
/// you control the allocation/deallocation of the nodes in the list.
typedef PrivatePersist *PersistentIter;
// Iterator support
template<typename U,typename E>
class _Iterator
{
public:
typedef U ValueType;
typedef U* Pointer;
typedef U& Reference;
_Iterator();
_Iterator(PersistentIter &iter) { _link = (Link *)iter; }
operator PersistentIter() const { return (PrivatePersist *)_link; }
_Iterator& operator++();
_Iterator operator++(int);
_Iterator& operator--();
_Iterator operator--(int);
bool operator==(const _Iterator&) const;
bool operator!=(const _Iterator&) const;
U* operator->() const;
U& operator*() const;
private:
friend class List;
E* _link;
_Iterator(E*);
};
// types
typedef Type ValueType;
typedef Type& Reference;
typedef const Type& ConstReference;
typedef _Iterator<Type,Link> Iterator;
typedef _Iterator<const Type,const Link> ConstIterator;
typedef S32 DifferenceType;
typedef U32 SizeType;
// initialization
List();
~List();
List(const List& p);
// management
U32 getSize() const; ///< Return the number of elements
void resize(U32); ///< Set the list size
void clear(); ///< Empty the List
bool isEmpty() const; ///< Node count == 0?
// insert elements
Iterator insert(U32 index, const Type& = Type()); ///< Insert new element at the given index
Iterator insert(Iterator, const Type& = Type()); ///< Insert at the given iter
Iterator pushFront(const Type& = Type()); ///< Insert at first element
Iterator pushBack(const Type& = Type()); ///< Insert after the last element
// erase elements
void erase(U32); ///< Preserves element order
void erase(Iterator); ///< Preserves element order
void popFront(); ///< Remove the first element
void popBack(); ///< Remove the last element
// forward Iterator access
Iterator begin(); ///< _Iterator to first element
ConstIterator begin() const; ///< _Iterator to first element
Iterator end(); ///< _Iterator to last element + 1
ConstIterator end() const; ///< _Iterator to last element + 1
// reverse Iterator access
Iterator rbegin(); ///< _Iterator to first element - 1
ConstIterator rbegin() const; ///< _Iterator to first element - 1
Iterator rend(); ///< _Iterator to last element
ConstIterator rend() const; ///< _Iterator to last element
// type access
Type& first(); ///< First element
const Type& first() const; ///< First element
Type& last(); ///< Last element
const Type& last() const; ///< Last element
// operators
void operator=(const List& p);
Type& operator[](U32);
const Type& operator[](U32) const;
private:
struct Link
{
Link* next;
Link* prev;
Link() {}
Link(Link* p,Link* n): next(n),prev(p) {}
};
struct Node: Link
{
Type value;
Node() {}
Node(Link* p,Link* n,const Type& x): Link(p,n),value(x) {}
};
Link _head;
U32 _size;
Link* _node(U32 index) const;
void _destroy();
};
template<class Type> List<Type>::List()
{
_size = 0;
_head.next = &_head;
_head.prev = &_head;
}
template<class Type> List<Type>::List(const List& p)
{
_size = 0;
_head.next = &_head;
_head.prev = &_head;
*this = p;
}
template<class Type> List<Type>::~List()
{
_destroy();
}
//-----------------------------------------------------------------------------
template<class Type> typename List<Type>::Link* List<Type>::_node(U32 index) const
{
Iterator itr(_head.next);
while (index--)
itr++;
return itr._link;
}
template<class Type> void List<Type>::_destroy()
{
for (Iterator itr(begin()); itr != end(); )
{
Node* n = static_cast<Node*>((itr++)._link);
delete n;
}
}
//-----------------------------------------------------------------------------
// management
template<class Type> inline U32 List<Type>::getSize() const
{
return _size;
}
template<class Type> void List<Type>::resize(U32 size)
{
if (size > _size)
{
while (_size != size)
pushBack(Type());
}
else
{
while (_size != size)
popBack();
}
}
template<class Type> void List<Type>::clear()
{
_destroy();
_head.next = &_head;
_head.prev = &_head;
_size = 0;
}
template<class Type> inline bool List<Type>::isEmpty() const
{
return _size == 0;
}
//-----------------------------------------------------------------------------
// add Nodes
template<class Type> typename List<Type>::Iterator List<Type>::insert(Iterator itr, const Type& x)
{
Link* node = itr._link;
Node* n = new Node(node->prev,node,x);
node->prev->next = n;
node->prev = n;
_size++;
return n;
}
template<class Type> inline typename List<Type>::Iterator List<Type>::insert(U32 index, const Type& x)
{
return insert(_node(index),x);
}
template<class Type> inline typename List<Type>::Iterator List<Type>::pushFront(const Type& x)
{
return insert(_head.next,x);
}
template<class Type> inline typename List<Type>::Iterator List<Type>::pushBack(const Type& x)
{
return insert(&_head,x);
}
//-----------------------------------------------------------------------------
// remove elements
template<class Type> void List<Type>::erase(Iterator itr)
{
Node* node = static_cast<Node*>(itr._link);
node->prev->next = node->next;
node->next->prev = node->prev;
delete node;
_size--;
}
template<class Type> inline void List<Type>::erase(U32 index)
{
erase(_node(index));
}
template<class Type> inline void List<Type>::popFront()
{
erase(_head.next);
}
template<class Type> inline void List<Type>::popBack()
{
erase(_head.prev);
}
//-----------------------------------------------------------------------------
// Iterator access
template<class Type> inline typename List<Type>::Iterator List<Type>::begin()
{
return _head.next;
}
template<class Type> inline typename List<Type>::ConstIterator List<Type>::begin() const
{
return _head.next;
}
template<class Type> inline typename List<Type>::Iterator List<Type>::end()
{
return &_head;
}
template<class Type> inline typename List<Type>::ConstIterator List<Type>::end() const
{
return &_head;
}
template<class Type> inline typename List<Type>::Iterator List<Type>::rbegin()
{
return _head.prev;
}
template<class Type> inline typename List<Type>::ConstIterator List<Type>::rbegin() const
{
return _head.prev;
}
template<class Type> inline typename List<Type>::Iterator List<Type>::rend()
{
return &_head;
}
template<class Type> inline typename List<Type>::ConstIterator List<Type>::rend() const
{
return &_head;
}
//-----------------------------------------------------------------------------
// type access
template<class Type> inline Type& List<Type>::first()
{
return static_cast<Node*>(_head.next)->value;
}
template<class Type> inline const Type& List<Type>::first() const
{
return static_cast<Node*>(_head.next)->value;
}
template<class Type> inline Type& List<Type>::last()
{
return static_cast<Node*>(_head.prev)->value;
}
template<class Type> inline const Type& List<Type>::last() const
{
return static_cast<Node*>(_head.prev)->value;
}
//-----------------------------------------------------------------------------
// operators
template<class Type> void List<Type>::operator=(const List& p)
{
if (_size >= p._size)
{
// Destroy the elements we're not going to use and copy the rest
while (_size != p._size)
popBack();
}
U32 count = _size;
ConstIterator src = p.begin();
Iterator dst = begin();
while (count--)
*dst++ = *src++;
while (src != p.end())
pushBack(*src++);
}
template<class Type> inline Type& List<Type>::operator[](U32 index)
{
return static_cast<Node*>(_node(index))->value;
}
template<class Type> inline const Type& List<Type>::operator[](U32 index) const
{
return static_cast<Node*>(_node(index))->value;
}
//-----------------------------------------------------------------------------
// _Iterator class
template<class Type> template<class U,typename E>
inline List<Type>::_Iterator<U,E>::_Iterator()
{
_link = 0;
}
template<class Type> template<class U,typename E>
inline List<Type>::_Iterator<U,E>::_Iterator(E* ptr)
{
_link = ptr;
}
// The checking for _MSC_VER on the ++/-- operators is due to a bug with the VS2008 compiler
// recheck this and remove if fixed with VS2008 SP1
template<class Type> template<class U,typename E>
#ifdef _MSC_VER
inline typename List<Type>:: _Iterator<U,E>& List<Type>::_Iterator<U,E>::operator++()
#else
inline typename List<Type>::template _Iterator<U,E>& List<Type>::_Iterator<U,E>::operator++()
#endif
{
_link = _link->next;
return *this;
}
template<class Type> template<class U,typename E>
#ifdef _MSC_VER
inline typename List<Type>:: _Iterator<U,E> List<Type>::_Iterator<U,E>::operator++(int)
#else
inline typename List<Type>::template _Iterator<U,E> List<Type>::_Iterator<U,E>::operator++(int)
#endif
{
_Iterator itr(*this);
_link = _link->next;
return itr;
}
template<class Type> template<class U,typename E>
#ifdef _MSC_VER
inline typename List<Type>:: _Iterator<U,E>& List<Type>::_Iterator<U,E>::operator--()
#else
inline typename List<Type>::template _Iterator<U,E>& List<Type>::_Iterator<U,E>::operator--()
#endif
{
_link = _link->prev;
return *this;
}
template<class Type> template<class U,typename E>
#ifdef _MSC_VER
inline typename List<Type>:: _Iterator<U,E> List<Type>::_Iterator<U,E>::operator--(int)
#else
inline typename List<Type>::template _Iterator<U,E> List<Type>::_Iterator<U,E>::operator--(int)
#endif
{
_Iterator itr(*this);
_link = _link->prev;
return itr;
}
template<class Type> template<class U,typename E>
inline bool List<Type>::_Iterator<U,E>::operator==(const _Iterator& b) const
{
return _link == b._link;
}
template<class Type> template<class U,typename E>
inline bool List<Type>::_Iterator<U,E>::operator!=(const _Iterator& b) const
{
return !(_link == b._link);
}
template<class Type> template<class U,typename E>
inline U* List<Type>::_Iterator<U,E>::operator->() const
{
// Can't use static_cast because of const / non-const versions of Iterator.
// Could use reflection functions, but C-style cast is easier in this case.
return &((Node*)_link)->value;
}
template<class Type> template<class U,typename E>
inline U& List<Type>::_Iterator<U,E>::operator*() const
{
// Can't use static_cast because of const / non-const versions of Iterator.
// Could use reflection functions, but C-style cast is easier in this case.
return ((Node*)_link)->value;
}
} // Namespace
#endif // _TORQUE_LIST_

View file

@ -0,0 +1,83 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _TFACTORY_H_
#define _TFACTORY_H_
#ifndef _TDICTIONARY_H_
#include "core/util/tDictionary.h"
#endif
#ifndef _TORQUE_STRING_H_
#include "core/util/str.h"
#endif
/// A helper template class for registering creation
/// methods to name strings.
template <typename Type>
class NamedFactory
{
protected:
typedef Type*(*FactorySig)();
typedef Map<String,FactorySig> FactoryMap;
///
static FactoryMap& getFactoryMap()
{
static FactoryMap theMap;
return theMap;
}
public:
/// Add a new creation method to the factory.
static void add( const char *name, FactorySig func )
{
getFactoryMap().insert( name, func );
}
/// Create an object instance by name.
static Type* create( const char *name )
{
typename FactoryMap::Iterator iter = getFactoryMap().find( name );
if ( iter == getFactoryMap().end() )
return NULL;
return iter->value();
}
/// Create an object instance of the first entry in the factory.
static Type* create()
{
typename FactoryMap::Iterator iter = getFactoryMap().begin();
if ( iter == getFactoryMap().end() )
return NULL;
return iter->value();
}
};
#endif //_TFACTORY_H_

View file

@ -0,0 +1,72 @@
//-----------------------------------------------------------------------------
// 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/tSignal.h"
void SignalBase::DelegateLink::insert(DelegateLink* node, float order)
{
// Note: can only legitimately be called on list head
DelegateLink * walk = next;
while (order >= walk->order && walk->next != this)
walk = walk->next;
if (order >= walk->order)
{
// insert after walk
node->prev = walk;
node->next = walk->next;
walk->next->prev = node;
walk->next = node;
}
else
{
// insert before walk
node->prev = walk->prev;
node->next = walk;
walk->prev->next = node;
walk->prev = node;
}
node->order = order;
}
void SignalBase::DelegateLink::unlink()
{
// Unlink this node
next->prev = prev;
prev->next = next;
}
SignalBase::~SignalBase()
{
removeAll();
}
void SignalBase::removeAll()
{
while (mList.next != &mList)
{
DelegateLink* ptr = mList.next;
ptr->unlink();
delete ptr;
}
}

View file

@ -0,0 +1,666 @@
//-----------------------------------------------------------------------------
// 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 _TSIGNAL_H_
#define _TSIGNAL_H_
#ifndef _UTIL_DELEGATE_H_
#include "core/util/delegate.h"
#endif
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
/// Signals (Multi-cast Delegates)
///
/// Signals are used throughout this engine to allow subscribers to listen
/// for generated events for various things.
///
/// Signals are called according to their order parameter (lower
/// numbers first).
///
/// Signal functions can return bool or void. If bool then returning false
/// from a signal function will cause entries in the ordered signal list after
/// that one to not be called.
///
/// This allows handlers of a given event to say to the signal generator, "I handled this message, and
/// it is no longer appropriate for other listeners to handle it"
class SignalBase
{
public:
SignalBase()
{
mList.next = mList.prev = &mList;
mList.order = 0.5f;
mTriggerNext = NULL;
}
~SignalBase();
/// Removes all the delegates from the signal.
void removeAll();
/// Returns true if the delegate list is empty.
bool isEmpty() const
{
return mList.next == &mList;
}
protected:
struct DelegateLink
{
DelegateLink *next,*prev;
F32 order;
void insert(DelegateLink* node, F32 order);
void unlink();
};
DelegateLink mList;
/// We need to protect the delegate list against removal of the currently
/// triggering node as well removal of the next node in the list. When not
/// handling these two cases correctly, removing delegates from a signal
/// while it is triggering will lead to crashes.
///
/// So this field stores the next node of each active traversal so that when
/// we unlink a node, we can check it against this field and move the traversal
/// along if needed.
Vector<DelegateLink*> mTriggerNext;
};
template<typename Signature> class SignalBaseT : public SignalBase
{
public:
/// The delegate signature for this signal.
typedef Delegate<Signature> DelegateSig;
SignalBaseT() {}
SignalBaseT( const SignalBaseT &base )
{
mList.next = mList.prev = &mList;
merge( base );
}
void operator =( const SignalBaseT &base )
{
removeAll();
merge( base );
}
void merge( const SignalBaseT &base )
{
for ( DelegateLink *ptr = base.mList.next; ptr != &base.mList; ptr = ptr->next )
{
DelegateLinkImpl *del = static_cast<DelegateLinkImpl*>( ptr );
notify( del->mDelegate, del->order );
}
}
void notify( const DelegateSig &dlg, F32 order = 0.5f)
{
mList.insert(new DelegateLinkImpl(dlg), order);
}
void remove( DelegateSig dlg )
{
for( DelegateLink* ptr = mList.next;ptr != &mList; ptr = ptr->next )
{
if( DelegateLinkImpl* del = static_cast< DelegateLinkImpl* >( ptr ) )
{
if( del->mDelegate == dlg )
{
for ( int i = 0; i < mTriggerNext.size(); i++ )
{
if( mTriggerNext[i] == ptr )
mTriggerNext[i] = ptr->next;
}
del->unlink();
delete del;
return;
}
}
}
}
template <class T,class U>
void notify(T obj,U func, F32 order = 0.5f)
{
DelegateSig dlg(obj, func);
notify(dlg, order);
}
template <class T>
void notify(T func, F32 order = 0.5f)
{
DelegateSig dlg(func);
notify(dlg, order);
}
template <class T,class U>
void remove(T obj,U func)
{
DelegateSig compDelegate(obj, func);
remove(compDelegate);
}
template <class T>
void remove(T func)
{
DelegateSig compDelegate(func);
remove(compDelegate);
}
/// Returns true if the signal already contains this delegate.
bool contains( const DelegateSig &dlg ) const
{
for ( DelegateLink *ptr = mList.next; ptr != &mList; ptr = ptr->next )
{
DelegateLinkImpl *del = static_cast<DelegateLinkImpl*>( ptr );
if ( del->mDelegate == dlg )
return true;
}
return false;
}
protected:
struct DelegateLinkImpl : public SignalBase::DelegateLink
{
DelegateSig mDelegate;
DelegateLinkImpl(DelegateSig dlg) : mDelegate(dlg) {}
};
DelegateSig & getDelegate(SignalBase::DelegateLink * link)
{
return ((DelegateLinkImpl*)link)->mDelegate;
}
};
//-----------------------------------------------------------------------------
template<typename Signature> class Signal;
// Short-circuit signal implementations
template<>
class Signal<bool()> : public SignalBaseT<bool()>
{
public:
bool trigger()
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
if( !this->getDelegate( ptr )() )
{
this->mTriggerNext.pop_back();
return false;
}
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
return true;
}
};
template<class A>
class Signal<bool(A)> : public SignalBaseT<bool(A)>
{
public:
bool trigger( A a )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
if( !this->getDelegate( ptr )( a ) )
{
this->mTriggerNext.pop_back();
return false;
}
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
return true;
}
};
template<class A, class B>
class Signal<bool(A,B)> : public SignalBaseT<bool(A,B)>
{
public:
bool trigger( A a, B b )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
if( !this->getDelegate( ptr )( a, b ) )
{
this->mTriggerNext.pop_back();
return false;
}
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
return true;
}
};
template<class A, class B, class C>
class Signal<bool(A,B,C)> : public SignalBaseT<bool(A,B,C)>
{
public:
bool trigger( A a, B b, C c )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
if( !this->getDelegate( ptr )( a, b, c ) )
{
this->mTriggerNext.pop_back();
return false;
}
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
return true;
}
};
template<class A, class B, class C, class D>
class Signal<bool(A,B,C,D)> : public SignalBaseT<bool(A,B,C,D)>
{
public:
bool trigger( A a, B b, C c, D d )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
if( !this->getDelegate( ptr )( a, b, c, d ) )
{
this->mTriggerNext.pop_back();
return false;
}
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
return true;
}
};
template<class A, class B, class C, class D, class E>
class Signal<bool(A,B,C,D,E)> : public SignalBaseT<bool(A,B,C,D,E)>
{
public:
bool trigger( A a, B b, C c, D d, E e )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
if( !this->getDelegate( ptr )( a, b, c, d, e ) )
{
this->mTriggerNext.pop_back();
return false;
}
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
return true;
}
};
template<class A, class B, class C, class D, class E, class F>
class Signal<bool(A,B,C,D,E,F)> : public SignalBaseT<bool(A,B,C,D,E,F)>
{
public:
bool trigger( A a, B b, C c, D d, E e, F f )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
if( !this->getDelegate( ptr )( a, b, c, d, e, f ) )
{
this->mTriggerNext.pop_back();
return false;
}
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
return true;
}
};
template<class A, class B, class C, class D, class E, class F, class G>
class Signal<bool(A,B,C,D,E,F,G)> : public SignalBaseT<bool(A,B,C,D,E,F,G)>
{
public:
bool trigger( A a, B b, C c, D d, E e, F f, G g )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
if( !this->getDelegate( ptr )( a, b, c, d, e, f, g ) )
{
this->mTriggerNext.pop_back();
return false;
}
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
return true;
}
};
template<class A, class B, class C, class D, class E, class F, class G, class H>
class Signal<bool(A,B,C,D,E,F,G,H)> : public SignalBaseT<bool(A,B,C,D,E,F,G,H)>
{
public:
bool trigger( A a, B b, C c, D d, E e, F f, G g, H h )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
if( !this->getDelegate( ptr )( a, b, c, d, e, f, g, h ) )
{
this->mTriggerNext.pop_back();
return false;
}
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
return true;
}
};
template<class A, class B, class C, class D, class E, class F, class G, class H, class I>
class Signal<bool(A,B,C,D,E,F,G,H,I)> : public SignalBaseT<bool(A,B,C,D,E,F,G,H,I)>
{
public:
bool trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
if( !this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i ) )
{
this->mTriggerNext.pop_back();
return false;
}
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
return true;
}
};
template<class A, class B, class C, class D, class E, class F, class G, class H, class I, class J>
class Signal<bool(A,B,C,D,E,F,G,H,I,J)> : public SignalBaseT<bool(A,B,C,D,E,F,G,H,I,J)>
{
public:
bool trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
if( !this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i, j ) )
{
this->mTriggerNext.pop_back();
return false;
}
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
return true;
}
};
// Non short-circuit signal implementations
template<>
class Signal<void()> : public SignalBaseT<void()>
{
public:
void trigger()
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
this->getDelegate( ptr )();
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
}
};
template<class A>
class Signal<void(A)> : public SignalBaseT<void(A)>
{
public:
void trigger( A a )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
this->getDelegate( ptr )( a );
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
}
};
template<class A, class B>
class Signal<void(A,B)> : public SignalBaseT<void(A,B)>
{
public:
void trigger( A a, B b )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
this->getDelegate( ptr )( a, b );
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
}
};
template<class A, class B, class C>
class Signal<void(A,B,C)> : public SignalBaseT<void(A,B,C)>
{
public:
void trigger( A a, B b, C c )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
this->getDelegate( ptr )( a, b, c );
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
}
};
template<class A, class B, class C, class D>
class Signal<void(A,B,C,D)> : public SignalBaseT<void(A,B,C,D)>
{
public:
void trigger( A a, B b, C c, D d )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
this->getDelegate( ptr )( a, b, c, d );
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
}
};
template<class A, class B, class C, class D, class E>
class Signal<void(A,B,C,D,E)> : public SignalBaseT<void(A,B,C,D,E)>
{
public:
void trigger( A a, B b, C c, D d, E e )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
this->getDelegate( ptr )( a, b, c, d, e );
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
}
};
template<class A, class B, class C, class D, class E, class F>
class Signal<void(A,B,C,D,E,F)> : public SignalBaseT<void(A,B,C,D,E,F)>
{
public:
void trigger( A a, B b, C c, D d, E e, F f )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
this->getDelegate( ptr )( a, b, c, d, e, f );
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
}
};
template<class A, class B, class C, class D, class E, class F, class G>
class Signal<void(A,B,C,D,E,F,G)> : public SignalBaseT<void(A,B,C,D,E,F,G)>
{
public:
void trigger( A a, B b, C c, D d, E e, F f, G g )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
this->getDelegate( ptr )( a, b, c, d, e, f, g );
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
}
};
template<class A, class B, class C, class D, class E, class F, class G, class H>
class Signal<void(A,B,C,D,E,F,G,H)> : public SignalBaseT<void(A,B,C,D,E,F,G,H)>
{
public:
void trigger( A a, B b, C c, D d, E e, F f, G g, H h )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
this->getDelegate( ptr )( a, b, c, d, e, f, g, h );
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
}
};
template<class A, class B, class C, class D, class E, class F, class G, class H, class I>
class Signal<void(A,B,C,D,E,F,G,H,I)> : public SignalBaseT<void(A,B,C,D,E,F,G,H,I)>
{
public:
void trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i );
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
}
};
template<class A, class B, class C, class D, class E, class F, class G, class H, class I, class J>
class Signal<void(A,B,C,D,E,F,G,H,I,J)> : public SignalBaseT<void(A,B,C,D,E,F,G,H,I,J)>
{
public:
void trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j )
{
this->mTriggerNext.push_back(NULL);
for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; )
{
this->mTriggerNext.last() = ptr->next;
this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i, j );
ptr = this->mTriggerNext.last();
}
this->mTriggerNext.pop_back();
}
};
#endif // _TSIGNAL_H_

View file

@ -0,0 +1,151 @@
//-----------------------------------------------------------------------------
// 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 _TSINGLETON_H_
#define _TSINGLETON_H_
#ifndef _PLATFORMASSERT_H_
#include "platform/platformAssert.h"
#endif
/// This is a simple thread safe singleton class based on the
/// design of boost::singleton_default (see http://www.boost.org/).
///
/// This singleton is guaranteed to be constructed before main() is called
/// and destroyed after main() exits. It will also be created on demand
/// if Singleton<T>::instance() is called before main() begins.
///
/// This thread safety only works within the execution context of main().
/// Access to the singleton from multiple threads outside of main() is
/// is not guaranteed to be thread safe.
///
/// To create a singleton you only need to access it once in your code:
///
/// Singleton<MySingletonClass>::instance()->myFunction();
///
/// You do not need to derive from this class.
///
/// @note Generally stay away from this class (!!) except if your class T
/// has no meaningful constructor. Otherwise, it is very easy to make
/// execution of global ctors ordering dependent.
template <typename T>
class Singleton
{
private:
// This is the creator object which ensures that the
// singleton is created before main() begins.
struct SingletonCreator
{
SingletonCreator() { Singleton<T>::instance(); }
// This dummy function is used below to force
// singleton creation at startup.
inline void forceSafeCreate() const {}
};
// The creator object instance.
static SingletonCreator smSingletonCreator;
/// This is private on purpose.
Singleton();
public:
/// Returns the singleton instance.
static T* instance()
{
// The singleton.
static T theSingleton;
// This is here to force the compiler to create
// the singleton before main() is called.
smSingletonCreator.forceSafeCreate();
return &theSingleton;
}
};
template <typename T>
typename Singleton<T>::SingletonCreator Singleton<T>::smSingletonCreator;
/// This is a managed singleton class with explict creation
/// and destruction functions which must be called at startup
/// and shutdown of the engine.
///
/// Your class to be managed must implement the following
/// function:
///
/// static const char* getSingletonName() { return "YourClassName"; }
///
/// This allow us to provide better asserts.
///
template <typename T>
class ManagedSingleton
{
private:
static T *smSingleton;
public:
/// Create the singleton instance.
/// @note Asserts when the singleton instance has already been constructed.
static void createSingleton()
{
AssertFatal( smSingleton == NULL, String::ToString( "%s::createSingleton() - The singleton is already created!", T::getSingletonName() ) );
smSingleton = new T();
}
/// Destroy the singleton instance.
/// @note Asserts when no singleton has been constructed.
static void deleteSingleton()
{
AssertFatal( smSingleton, String::ToString( "%s::deleteSingleton() - The singleton doest not exist!", T::getSingletonName() ) );
delete smSingleton;
smSingleton = NULL;
}
/// Return the singleton instance.
/// @note Asserts when called before createSingleton().
static T* instance()
{
AssertFatal( smSingleton, String::ToString( "%s::instance() - The singleton has not been created!", T::getSingletonName() ) );
return smSingleton;
}
/// Return the singleton instance or NULL if it has been deleted or not yet constructed.
static T* instanceOrNull()
{
return smSingleton;
}
};
///
template <typename T>
T* ManagedSingleton<T>::smSingleton = NULL;
#endif //_TSINGLETON_H_

View file

@ -0,0 +1,95 @@
//-----------------------------------------------------------------------------
// 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 _TUNMANAGEDVECTOR_H_
#define _TUNMANAGEDVECTOR_H_
/// An array that does not manage the memory it gets passed. Conversely, the
/// array cannot be enlarged.
///
/// @note As the array does not manage the instances it uses, it will also not
/// destruct them when it is itself destructed.
template< typename T >
class UnmanagedVector
{
protected:
U32 mCount;
T* mArray;
public:
typedef T* iterator;
typedef const T* const_iterator;
UnmanagedVector()
: mCount( 0 ), mArray( NULL ) {}
UnmanagedVector( T* array, U32 count )
: mArray( array ), mCount( count ) {}
U32 size() const { return mCount; }
bool empty() const { return ( mCount == 0 ); }
const T* address() const { return mArray; }
T* address() { return mArray; }
void clear() { mCount = 0; mArray = NULL; }
iterator begin() { return &mArray[ 0 ]; }
iterator end() { return &mArray[ mCount ]; }
const_iterator begin() const { return &mArray[ 0 ]; }
const_iterator end() const { return &mArray[ mCount ]; }
const T& first() const
{
AssertFatal( !empty(), "UnmanagedVector::first - Vector is empty" );
return mArray[ 0 ];
}
T& first()
{
AssertFatal( !empty(), "UnmanagedVector::first - Vector is empty" );
return mArray[ 0 ];
}
const T& last() const
{
AssertFatal( !empty(), "UnmanagedVector::last - Vector is empty" );
return mArray[ mCount - 1 ];
}
T& last()
{
AssertFatal( !empty(), "UnmanagedVector::last - Vector is empty" );
return mArray[ mCount - 1 ];
}
const T& operator []( U32 index ) const
{
AssertFatal( index <= size(), "UnmanagedVector::operator[] - Index out of range" );
return mArray[ index ];
}
T& operator []( U32 index )
{
AssertFatal( index <= size(), "UnmanagedVector::operator[] - Index out of range" );
return mArray[ index ];
}
};
#endif // !_TUNMANAGEDVECTOR_H_

View file

@ -0,0 +1,101 @@
//-----------------------------------------------------------------------------
// 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/tVector.h"
#include "platform/profiler.h"
#ifdef TORQUE_DEBUG_GUARD
bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize,
const char* fileName,
const U32 lineNum)
{
PROFILE_SCOPE( VectorResize );
if (newCount > 0)
{
U32 blocks = newCount / VectorBlockSize;
if (newCount % VectorBlockSize)
blocks++;
S32 mem_size = blocks * VectorBlockSize * elemSize;
const char* pUseFileName = fileName != NULL ? fileName : __FILE__;
U32 useLineNum = fileName != NULL ? lineNum : __LINE__;
if (*arrayPtr != NULL)
*arrayPtr = dRealloc_r(*arrayPtr, mem_size, pUseFileName, useLineNum);
else
*arrayPtr = dMalloc_r(mem_size, pUseFileName, useLineNum);
AssertFatal( *arrayPtr, "VectorResize - Allocation failed." );
*aCount = newCount;
*aSize = blocks * VectorBlockSize;
return true;
}
if (*arrayPtr)
{
dFree(*arrayPtr);
*arrayPtr = 0;
}
*aSize = 0;
*aCount = 0;
return true;
}
#else
bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize)
{
PROFILE_SCOPE( VectorResize );
if (newCount > 0)
{
U32 blocks = newCount / VectorBlockSize;
if (newCount % VectorBlockSize)
blocks++;
S32 mem_size = blocks * VectorBlockSize * elemSize;
*arrayPtr = *arrayPtr ? dRealloc(*arrayPtr,mem_size) :
dMalloc(mem_size);
*aCount = newCount;
*aSize = blocks * VectorBlockSize;
return true;
}
if (*arrayPtr)
{
dFree(*arrayPtr);
*arrayPtr = 0;
}
*aSize = 0;
*aCount = 0;
return true;
}
#endif

View file

@ -0,0 +1,964 @@
//-----------------------------------------------------------------------------
// 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 _TVECTOR_H_
#define _TVECTOR_H_
// TODO: This shouldn't be included in headers... it should
// be included by the source file before all other includes.
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
//-----------------------------------------------------------------------------
// Helper definitions for the vector class.
/// Size of memory blocks to allocate at a time for vectors.
const static S32 VectorBlockSize = 16;
#ifdef TORQUE_DEBUG_GUARD
extern bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize,
const char* fileName,
const U32 lineNum);
#else
extern bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize);
#endif
/// Use the following macro to bind a vector to a particular line
/// of the owning class for memory tracking purposes
#ifdef TORQUE_DEBUG_GUARD
#define VECTOR_SET_ASSOCIATION(x) x.setFileAssociation(__FILE__, __LINE__)
#else
#define VECTOR_SET_ASSOCIATION(x)
#endif
// =============================================================================
/// A dynamic array template class.
///
/// The vector grows as you insert or append
/// elements. Insertion is fastest at the end of the array. Resizing
/// of the array can be avoided by pre-allocating space using the
/// reserve() method.
///
/// @nosubgrouping
template<class T>
class Vector
{
protected:
U32 mElementCount; ///< Number of elements currently in the Vector.
U32 mArraySize; ///< Number of elements allocated for the Vector.
T* mArray; ///< Pointer to the Vector elements.
#ifdef TORQUE_DEBUG_GUARD
const char* mFileAssociation;
U32 mLineAssociation;
#endif
bool resize(U32); // resizes, but does no construction/destruction
void destroy(U32 start, U32 end); ///< Destructs elements from <i>start</i> to <i>end-1</i>
void construct(U32 start, U32 end); ///< Constructs elements from <i>start</i> to <i>end-1</i>
void construct(U32 start, U32 end, const T* array);
public:
Vector(const U32 initialSize = 0);
Vector(const U32 initialSize, const char* fileName, const U32 lineNum);
Vector(const char* fileName, const U32 lineNum);
Vector(const Vector&);
~Vector();
#ifdef TORQUE_DEBUG_GUARD
void setFileAssociation(const char* file, const U32 line);
#endif
/// @name STL interface
/// @{
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
typedef T* iterator;
typedef const T* const_iterator;
typedef S32 difference_type;
typedef U32 size_type;
typedef difference_type (QSORT_CALLBACK *compare_func)(const T *a, const T *b);
Vector<T>& operator=(const Vector<T>& p);
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
S32 size() const;
bool empty() const;
bool contains(const T&) const;
void insert(iterator, const T&);
void erase(iterator);
T& front();
const T& front() const;
T& back();
const T& back() const;
void push_front(const T&);
void push_back(const T&);
U32 push_front_unique(const T&);
U32 push_back_unique(const T&);
S32 find_next( const T&, U32 start = 0 ) const;
void pop_front();
void pop_back();
T& operator[](U32);
const T& operator[](U32) const;
T& operator[](S32 i) { return operator[](U32(i)); }
const T& operator[](S32 i ) const { return operator[](U32(i)); }
void reserve(U32);
U32 capacity() const;
/// @}
/// @name Extended interface
/// @{
U32 memSize() const;
T* address() const;
U32 setSize(U32);
void increment();
void decrement();
void increment(U32);
void decrement(U32);
void insert(U32);
void insert(U32, const T&);
void erase(U32);
void erase_fast(U32);
void erase(U32 index, U32 count);
void erase_fast(iterator);
void clear();
void compact();
void sort(compare_func f);
void fill( const T& value );
/// Finds the first matching element and erases it.
/// @return Returns true if a match is found.
bool remove( const T& );
T& first();
T& last();
const T& first() const;
const T& last() const;
void set(void * addr, U32 sz);
/// Appends the content of the vector to this one.
void merge( const Vector &p );
/// Appends the content of the array to the vector.
///
/// @param addr A pointer to the first item of the array to merge.
/// @param count The number of elements in the array to merge.
///
void merge( const T *addr, U32 count );
/// @}
};
template<class T> inline Vector<T>::~Vector()
{
clear();
dFree(mArray);
}
template<class T> inline Vector<T>::Vector(const U32 initialSize)
{
#ifdef TORQUE_DEBUG_GUARD
mFileAssociation = NULL;
mLineAssociation = 0;
#endif
mArray = 0;
mElementCount = 0;
mArraySize = 0;
if(initialSize)
reserve(initialSize);
}
template<class T> inline Vector<T>::Vector(const U32 initialSize,
const char* fileName,
const U32 lineNum)
{
#ifdef TORQUE_DEBUG_GUARD
mFileAssociation = fileName;
mLineAssociation = lineNum;
#else
// TORQUE_UNUSED(fileName);
// TORQUE_UNUSED(lineNum);
#endif
mArray = 0;
mElementCount = 0;
mArraySize = 0;
if(initialSize)
reserve(initialSize);
}
template<class T> inline Vector<T>::Vector(const char* fileName,
const U32 lineNum)
{
#ifdef TORQUE_DEBUG_GUARD
mFileAssociation = fileName;
mLineAssociation = lineNum;
#else
// TORQUE_UNUSED(fileName);
// TORQUE_UNUSED(lineNum);
#endif
mArray = 0;
mElementCount = 0;
mArraySize = 0;
}
template<class T> inline Vector<T>::Vector(const Vector& p)
{
#ifdef TORQUE_DEBUG_GUARD
mFileAssociation = p.mFileAssociation;
mLineAssociation = p.mLineAssociation;
#endif
mArray = 0;
resize(p.mElementCount);
construct(0, p.mElementCount, p.mArray);
}
#ifdef TORQUE_DEBUG_GUARD
template<class T> inline void Vector<T>::setFileAssociation(const char* file,
const U32 line)
{
mFileAssociation = file;
mLineAssociation = line;
}
#endif
template<class T> inline void Vector<T>::destroy(U32 start, U32 end) // destroys from start to end-1
{
// This check is a little generous as we can legitimately get (0,0) as
// our parameters... so it won't detect every invalid case but it does
// remain simple.
AssertFatal(start <= mElementCount && end <= mElementCount, "Vector<T>::destroy - out of bounds start/end.");
// destroys from start to end-1
while(start < end)
destructInPlace(&mArray[start++]);
}
template<class T> inline void Vector<T>::construct(U32 start, U32 end) // destroys from start to end-1
{
AssertFatal(start <= mElementCount && end <= mElementCount, "Vector<T>::construct - out of bounds start/end.");
while(start < end)
constructInPlace(&mArray[start++]);
}
template<class T> inline void Vector<T>::construct(U32 start, U32 end, const T* array) // destroys from start to end-1
{
AssertFatal(start <= mElementCount && end <= mElementCount, "Vector<T>::construct - out of bounds start/end.");
while(start < end)
{
constructInPlace(&mArray[start], &array[start]);
start++;
}
}
template<class T> inline U32 Vector<T>::memSize() const
{
return capacity() * sizeof(T);
}
template<class T> inline T* Vector<T>::address() const
{
return mArray;
}
template<class T> inline U32 Vector<T>::setSize(U32 size)
{
const U32 oldSize = mElementCount;
if(size > mElementCount)
{
if (size > mArraySize)
resize(size);
// Set count first so we are in a valid state for construct.
mElementCount = size;
construct(oldSize, size);
}
else if(size < mElementCount)
{
destroy(size, oldSize);
mElementCount = size;
}
return mElementCount;
}
template<class T> inline void Vector<T>::increment()
{
if(mElementCount == mArraySize)
resize(mElementCount + 1);
else
mElementCount++;
constructInPlace(&mArray[mElementCount - 1]);
}
template<class T> inline void Vector<T>::decrement()
{
AssertFatal(mElementCount != 0, "Vector<T>::decrement - cannot decrement zero-length vector.");
mElementCount--;
destructInPlace(&mArray[mElementCount]);
}
template<class T> inline void Vector<T>::increment(U32 delta)
{
U32 count = mElementCount;
if ((mElementCount += delta) > mArraySize)
resize(mElementCount);
construct(count, mElementCount);
}
template<class T> inline void Vector<T>::decrement(U32 delta)
{
AssertFatal(mElementCount != 0, "Vector<T>::decrement - cannot decrement zero-length vector.");
const U32 count = mElementCount;
// Determine new count after decrement...
U32 newCount = mElementCount;
if (mElementCount > delta)
newCount -= delta;
else
newCount = 0;
// Destruct removed items...
destroy(newCount, count);
// Note new element count.
mElementCount = newCount;
}
template<class T> inline void Vector<T>::insert(U32 index)
{
AssertFatal(index <= mElementCount, "Vector<T>::insert - out of bounds index.");
if(mElementCount == mArraySize)
resize(mElementCount + 1);
else
mElementCount++;
dMemmove(&mArray[index + 1],
&mArray[index],
(mElementCount - index - 1) * sizeof(value_type));
constructInPlace(&mArray[index]);
}
template<class T> inline void Vector<T>::insert(U32 index,const T& x)
{
insert(index);
mArray[index] = x;
}
template<class T> inline void Vector<T>::erase(U32 index)
{
AssertFatal(index < mElementCount, "Vector<T>::erase - out of bounds index!");
destructInPlace(&mArray[index]);
if (index < (mElementCount - 1))
{
dMemmove(&mArray[index],
&mArray[index + 1],
(mElementCount - index - 1) * sizeof(value_type));
}
mElementCount--;
}
template<class T> inline bool Vector<T>::remove( const T& x )
{
iterator i = begin();
while (i != end())
{
if (*i == x)
{
erase( i );
return true;
}
i++;
}
return false;
}
template<class T> inline void Vector<T>::erase(U32 index, U32 count)
{
AssertFatal(index < mElementCount, "Vector<T>::erase - out of bounds index!");
AssertFatal(count > 0, "Vector<T>::erase - count must be greater than zero!");
AssertFatal(index+count <= mElementCount, "Vector<T>::erase - out of bounds count!");
destroy( index, index+count );
dMemmove( &mArray[index],
&mArray[index + count],
(mElementCount - index - count) * sizeof(value_type));
mElementCount -= count;
}
template<class T> inline void Vector<T>::erase_fast(U32 index)
{
AssertFatal(index < mElementCount, "Vector<T>::erase_fast - out of bounds index.");
// CAUTION: this operator does NOT maintain list order
// Copy the last element into the deleted 'hole' and decrement the
// size of the vector.
destructInPlace(&mArray[index]);
if (index < (mElementCount - 1))
dMemmove(&mArray[index], &mArray[mElementCount - 1], sizeof(value_type));
mElementCount--;
}
template<class T> inline bool Vector<T>::contains(const T& x) const
{
const_iterator i = begin();
while (i != end())
{
if (*i == x)
return true;
i++;
}
return false;
}
template< class T > inline void Vector< T >::fill( const T& value )
{
for( U32 i = 0; i < size(); ++ i )
mArray[ i ] = value;
}
template<class T> inline T& Vector<T>::first()
{
AssertFatal(mElementCount != 0, "Vector<T>::first - Error, no first element of a zero sized array!");
return mArray[0];
}
template<class T> inline const T& Vector<T>::first() const
{
AssertFatal(mElementCount != 0, "Vector<T>::first - Error, no first element of a zero sized array! (const)");
return mArray[0];
}
template<class T> inline T& Vector<T>::last()
{
AssertFatal(mElementCount != 0, "Vector<T>::last - Error, no last element of a zero sized array!");
return mArray[mElementCount - 1];
}
template<class T> inline const T& Vector<T>::last() const
{
AssertFatal(mElementCount != 0, "Vector<T>::last - Error, no last element of a zero sized array! (const)");
return mArray[mElementCount - 1];
}
template<class T> inline void Vector<T>::clear()
{
destroy(0, mElementCount);
mElementCount = 0;
}
template<class T> inline void Vector<T>::compact()
{
resize(mElementCount);
}
typedef int (QSORT_CALLBACK *qsort_compare_func)(const void *, const void *);
template<class T> inline void Vector<T>::sort(compare_func f)
{
qsort(address(), size(), sizeof(T), (qsort_compare_func) f);
}
//-----------------------------------------------------------------------------
template<class T> inline Vector<T>& Vector<T>::operator=(const Vector<T>& p)
{
if(mElementCount > p.mElementCount)
{
destroy(p.mElementCount, mElementCount);
}
U32 count = getMin( mElementCount, p.mElementCount );
U32 i;
for( i=0; i < count; i++ )
{
mArray[i] = p.mArray[i];
}
resize( p.mElementCount );
if( i < p.mElementCount )
{
construct(i, p.mElementCount, p.mArray);
}
return *this;
}
template<class T> inline typename Vector<T>::iterator Vector<T>::begin()
{
return mArray;
}
template<class T> inline typename Vector<T>::const_iterator Vector<T>::begin() const
{
return mArray;
}
template<class T> inline typename Vector<T>::iterator Vector<T>::end()
{
return mArray + mElementCount;
}
template<class T> inline typename Vector<T>::const_iterator Vector<T>::end() const
{
return mArray +mElementCount;
}
template<class T> inline S32 Vector<T>::size() const
{
return (S32)mElementCount;
}
template<class T> inline bool Vector<T>::empty() const
{
return (mElementCount == 0);
}
template<class T> inline void Vector<T>::insert(iterator p,const T& x)
{
U32 index = (U32) (p - mArray);
insert(index);
mArray[index] = x;
}
template<class T> inline void Vector<T>::erase(iterator q)
{
erase(U32(q - mArray));
}
template<class T> inline void Vector<T>::erase_fast(iterator q)
{
erase_fast(U32(q - mArray));
}
template<class T> inline T& Vector<T>::front()
{
return *begin();
}
template<class T> inline const T& Vector<T>::front() const
{
return *begin();
}
template<class T> inline T& Vector<T>::back()
{
AssertFatal(mElementCount != 0, "Vector<T>::back - cannot access last element of zero-length vector.");
return *(end()-1);
}
template<class T> inline const T& Vector<T>::back() const
{
AssertFatal(mElementCount != 0, "Vector<T>::back - cannot access last element of zero-length vector.");
return *(end()-1);
}
template<class T> inline void Vector<T>::push_front(const T& x)
{
insert(0);
mArray[0] = x;
}
template<class T> inline void Vector<T>::push_back(const T& x)
{
increment();
mArray[mElementCount - 1] = x;
}
template<class T> inline U32 Vector<T>::push_front_unique(const T& x)
{
S32 index = find_next(x);
if (index == -1)
{
index = 0;
insert(index);
mArray[index] = x;
}
return index;
}
template<class T> inline U32 Vector<T>::push_back_unique(const T& x)
{
S32 index = find_next(x);
if (index == -1)
{
increment();
index = mElementCount - 1;
mArray[index] = x;
}
return index;
}
template<class T> inline S32 Vector<T>::find_next( const T& x, U32 start ) const
{
S32 index = -1;
if (start < mElementCount)
{
for (U32 i = start; i < mElementCount; i++)
{
if (mArray[i] == x)
{
index = i;
break;
}
}
}
return index;
}
template<class T> inline void Vector<T>::pop_front()
{
AssertFatal(mElementCount != 0, "Vector<T>::pop_front - cannot pop the front of a zero-length vector.");
erase(U32(0));
}
template<class T> inline void Vector<T>::pop_back()
{
AssertFatal(mElementCount != 0, "Vector<T>::pop_back - cannot pop the back of a zero-length vector.");
decrement();
}
template<class T> inline T& Vector<T>::operator[](U32 index)
{
AssertFatal(index < mElementCount, "Vector<T>::operator[] - out of bounds array access!");
return mArray[index];
}
template<class T> inline const T& Vector<T>::operator[](U32 index) const
{
AssertFatal(index < mElementCount, "Vector<T>::operator[] - out of bounds array access!");
return mArray[index];
}
template<class T> inline void Vector<T>::reserve(U32 size)
{
if (size <= mArraySize)
return;
const U32 ec = mElementCount;
if (resize(size))
mElementCount = ec;
}
template<class T> inline U32 Vector<T>::capacity() const
{
return mArraySize;
}
template<class T> inline void Vector<T>::set(void * addr, U32 sz)
{
if ( !addr )
sz = 0;
setSize( sz );
if ( addr && sz > 0 )
dMemcpy(address(),addr,sz*sizeof(T));
}
//-----------------------------------------------------------------------------
template<class T> inline bool Vector<T>::resize(U32 ecount)
{
#ifdef TORQUE_DEBUG_GUARD
return VectorResize(&mArraySize, &mElementCount, (void**) &mArray, ecount, sizeof(T),
mFileAssociation, mLineAssociation);
#else
return VectorResize(&mArraySize, &mElementCount, (void**) &mArray, ecount, sizeof(T));
#endif
}
template<class T> inline void Vector<T>::merge( const Vector &p )
{
if ( !p.size() )
return;
const U32 oldSize = mElementCount;
const U32 newSize = oldSize + p.size();
if ( newSize > mArraySize )
resize( newSize );
T *dest = mArray + oldSize;
const T *src = p.mArray;
while ( dest < mArray + newSize )
constructInPlace( dest++, src++ );
mElementCount = newSize;
}
template<class T> inline void Vector<T>::merge( const T *addr, U32 count )
{
const U32 oldSize = mElementCount;
const U32 newSize = oldSize + count;
if ( newSize > mArraySize )
resize( newSize );
T *dest = mArray + oldSize;
while ( dest < mArray + newSize )
constructInPlace( dest++, addr++ );
mElementCount = newSize;
}
//-----------------------------------------------------------------------------
/// Template for vectors of pointers.
template <class T>
class VectorPtr : public Vector<void *>
{
/// @deprecated Disallowed.
VectorPtr(const VectorPtr&); // Disallowed
public:
VectorPtr();
VectorPtr(const char* fileName, const U32 lineNum);
/// @name STL interface
/// @{
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
typedef T* iterator;
typedef const T* const_iterator;
typedef U32 difference_type;
typedef U32 size_type;
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
void insert(iterator,const T&);
void insert(int idx) { Parent::insert(idx); }
void erase(iterator);
T& front();
const T& front() const;
T& back();
const T& back() const;
void push_front(const T&);
void push_back(const T&);
T& operator[](U32);
const T& operator[](U32) const;
/// @}
/// @name Extended interface
/// @{
typedef Vector<void*> Parent;
T& first();
T& last();
const T& first() const;
const T& last() const;
void erase_fast(U32);
void erase_fast(iterator);
/// @}
};
//-----------------------------------------------------------------------------
template<class T> inline VectorPtr<T>::VectorPtr()
{
//
}
template<class T> inline VectorPtr<T>::VectorPtr(const char* fileName,
const U32 lineNum)
: Vector<void*>(fileName, lineNum)
{
//
}
template<class T> inline T& VectorPtr<T>::first()
{
return (T&)Parent::first();
}
template<class T> inline const T& VectorPtr<T>::first() const
{
return (const T)Parent::first();
}
template<class T> inline T& VectorPtr<T>::last()
{
return (T&)Parent::last();
}
template<class T> inline const T& VectorPtr<T>::last() const
{
return (const T&)Parent::last();
}
template<class T> inline typename VectorPtr<T>::iterator VectorPtr<T>::begin()
{
return (iterator)Parent::begin();
}
template<class T> inline typename VectorPtr<T>::const_iterator VectorPtr<T>::begin() const
{
return (const_iterator)Parent::begin();
}
template<class T> inline typename VectorPtr<T>::iterator VectorPtr<T>::end()
{
return (iterator)Parent::end();
}
template<class T> inline typename VectorPtr<T>::const_iterator VectorPtr<T>::end() const
{
return (const_iterator)Parent::end();
}
template<class T> inline void VectorPtr<T>::insert(iterator i,const T& x)
{
Parent::insert( (Parent::iterator)i, (Parent::reference)x );
}
template<class T> inline void VectorPtr<T>::erase(iterator i)
{
Parent::erase( (Parent::iterator)i );
}
template<class T> inline void VectorPtr<T>::erase_fast(U32 index)
{
AssertFatal(index < mElementCount, "VectorPtr<T>::erase_fast - out of bounds index." );
// CAUTION: this operator does not maintain list order
// Copy the last element into the deleted 'hole' and decrement the
// size of the vector.
// Assert: index >= 0 && index < mElementCount
if (index < (mElementCount - 1))
mArray[index] = mArray[mElementCount - 1];
decrement();
}
template<class T> inline void VectorPtr<T>::erase_fast(iterator i)
{
erase_fast(U32(i - iterator(mArray)));
}
template<class T> inline T& VectorPtr<T>::front()
{
return *begin();
}
template<class T> inline const T& VectorPtr<T>::front() const
{
return *begin();
}
template<class T> inline T& VectorPtr<T>::back()
{
AssertFatal(mElementCount != 0, "Vector<T>::back - cannot access last element of zero-length vector.");
return *(end()-1);
}
template<class T> inline const T& VectorPtr<T>::back() const
{
AssertFatal(mElementCount != 0, "Vector<T>::back - cannot access last element of zero-length vector.");
return *(end()-1);
}
template<class T> inline void VectorPtr<T>::push_front(const T& x)
{
Parent::push_front((Parent::const_reference)x);
}
template<class T> inline void VectorPtr<T>::push_back(const T& x)
{
Parent::push_back((Parent::const_reference)x);
}
template<class T> inline T& VectorPtr<T>::operator[](U32 index)
{
return (T&)Parent::operator[](index);
}
template<class T> inline const T& VectorPtr<T>::operator[](U32 index) const
{
return (const T&)Parent::operator[](index);
}
//------------------------------------------------------------------------------
template <class T> class VectorSet : public Vector<T>
{
public:
void insert(T dat)
{
if(find(this->begin(), this->end(), dat) == this->end())
push_back(dat);
}
};
// Include vector specializations
#ifndef _VECTORSPEC_H_
#include "core/util/tVectorSpecializations.h"
#endif
#endif //_TVECTOR_H_

View file

@ -0,0 +1,46 @@
//-----------------------------------------------------------------------------
// 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 _TVECTORSPEC_H_
#define _TVECTORSPEC_H_
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
// Not exactly a specialization, just a vector to use when speed is a concern
template<class T>
class FastVector : public Vector<T>
{
public:
// This method was re-written to prevent load-hit-stores during the simple-case.
void push_back_noresize(const T &x)
{
#ifdef TORQUE_DEBUG
AssertFatal(Vector<T>::mElementCount < Vector<T>::mArraySize, "use of push_back_noresize requires that you reserve enough space in the FastVector");
#endif
Vector<T>::mArray[Vector<T>::mElementCount++] = x;
}
};
#endif //_TVECTORSPEC_H_

View file

@ -0,0 +1,54 @@
//-----------------------------------------------------------------------------
// 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/tFixedSizeDeque.h"
using namespace UnitTesting;
#define TEST( x ) test( ( x ), "FAIL: " #x )
CreateUnitTest( TestFixedSizeDeque, "Util/FixedSizeDeque" )
{
void run()
{
enum { DEQUE_SIZE = 3 };
FixedSizeDeque< U32 > deque( DEQUE_SIZE );
TEST( deque.capacity() == DEQUE_SIZE );
TEST( deque.size() == 0 );
deque.pushFront( 1 );
TEST( deque.capacity() == ( DEQUE_SIZE - 1 ) );
TEST( deque.size() == 1 );
TEST( !deque.isEmpty() );
deque.pushBack( 2 );
TEST( deque.capacity() == ( DEQUE_SIZE - 2 ) );
TEST( deque.size() == 2 );
TEST( deque.popFront() == 1 );
TEST( deque.popFront() == 2 );
TEST( deque.isEmpty() );
}
};

View file

@ -0,0 +1,44 @@
//-----------------------------------------------------------------------------
// 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/path.h"
using namespace UnitTesting;
#define TEST( x ) test( ( x ), "FAIL: " #x )
CreateUnitTest(TestPathMakeRelativePath, "Core/Util/Path/MakeRelativePath")
{
void run()
{
TEST(Torque::Path::MakeRelativePath("art/interiors/burg/file.png", "art/interiors/") == "burg/file.png");
TEST(Torque::Path::MakeRelativePath("art/interiors/file.png", "art/interiors/burg/") == "../file.png");
TEST(Torque::Path::MakeRelativePath("art/file.png", "art/interiors/burg/") == "../../file.png");
TEST(Torque::Path::MakeRelativePath("file.png", "art/interiors/burg/") == "../../../file.png");
TEST(Torque::Path::MakeRelativePath("art/interiors/burg/file.png", "art/interiors/burg/") == "file.png");
TEST(Torque::Path::MakeRelativePath("art/interiors/camp/file.png", "art/interiors/burg/") == "../camp/file.png");
TEST(Torque::Path::MakeRelativePath("art/interiors/burg/file.png", "art/shapes/") == "../interiors/burg/file.png");
TEST(Torque::Path::MakeRelativePath("levels/den/file.png", "art/interiors/burg/") == "../../../levels/den/file.png");
TEST(Torque::Path::MakeRelativePath("art/interiors/burg/file.png", "art/dts/burg/") == "../../interiors/burg/file.png");
}
};

View file

@ -0,0 +1,358 @@
//-----------------------------------------------------------------------------
// 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/str.h"
#include "core/util/tVector.h"
#include "core/strings/stringFunctions.h"
#include "core/strings/unicode.h"
#ifndef TORQUE_SHIPPING
using namespace UnitTesting;
#define TEST( x ) test( ( x ), "FAIL: " #x )
#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x )
CreateUnitTest( TestString, "Util/String" )
{
struct StrTest
{
const UTF8* mData;
const UTF16* mUTF16;
U32 mLength;
StrTest() : mData( 0 ), mUTF16( 0 ) {}
StrTest( const char* str )
: mData( str ), mLength( str ? dStrlen( str ) : 0 ), mUTF16( NULL )
{
if( str )
mUTF16 = convertUTF8toUTF16( mData );
}
~StrTest()
{
if( mUTF16 )
delete [] mUTF16;
}
};
Vector< StrTest* > mStrings;
template< class T >
void runTestOnStrings()
{
for( U32 i = 0; i < mStrings.size(); ++ i )
T::run( this, *mStrings[ i ] );
}
template< class T >
void runPairwiseTestOnStrings()
{
for( U32 i = 0; i < mStrings.size(); ++ i )
for( U32 j = 0; j < mStrings.size(); ++ j )
T::run( this, *mStrings[ i ], *mStrings[ j ] );
}
struct Test1
{
static void run( TestString* test, StrTest& data )
{
String str( data.mData );
String str16( data.mUTF16 );
XTEST( test, str.length() == data.mLength );
XTEST( test, str.size() == data.mLength + 1 );
XTEST( test, str.isEmpty() || str.length() > 0 );
XTEST( test, str.length() == str16.length() );
XTEST( test, str.size() == str16.size() );
XTEST( test, dMemcmp( str.utf16(), str16.utf16(), str.length() * sizeof( UTF16 ) ) == 0 );
XTEST( test, !data.mData || dMemcmp( str.utf16(), data.mUTF16, str.length() * sizeof( UTF16 ) ) == 0 );
XTEST( test, !data.mData || dMemcmp( str16.utf8(), data.mData, str.length() ) == 0 );
XTEST( test, !data.mData || dStrcmp( str.utf8(), data.mData ) == 0 );
XTEST( test, !data.mData || dStrcmp( str.utf16(), data.mUTF16 ) == 0 );
}
};
struct Test2
{
static void run( TestString* test, StrTest& data )
{
String str( data.mData );
XTEST( test, str == str );
XTEST( test, !( str != str ) );
XTEST( test, !( str < str ) );
XTEST( test, !( str > str ) );
XTEST( test, str.equal( str ) );
XTEST( test, str.equal( str, String::NoCase ) );
}
};
struct Test3
{
static void run( TestString* test, StrTest& d1, StrTest& d2 )
{
if( &d1 != &d2 )
XTEST( test, String( d1.mData ) != String( d2.mData )
|| ( String( d1.mData ).isEmpty() && String( d2.mData ).isEmpty() ) );
else
XTEST( test, String( d1.mData ) == String( d2.mData ) );
}
};
void testEmpty()
{
TEST( String().length() == 0 );
TEST( String( "" ).length() == 0 );
TEST( String().size() == 1 );
TEST( String( "" ).size() == 1 );
TEST( String().isEmpty() );
TEST( String( "" ).isEmpty() );
}
void testTrim()
{
TEST( String( " Foobar Barfoo \n\t " ).trim() == String( "Foobar Barfoo" ) );
TEST( String( "Foobar" ).trim() == String( "Foobar" ) );
TEST( String( " " ).trim().isEmpty() );
}
void testCompare()
{
String str( "Foobar" );
TEST( str.compare( "Foo", 3 ) == 0 );
TEST( str.compare( "bar", 3, String::NoCase | String::Right ) == 0 );
TEST( str.compare( "foo", 3, String::NoCase ) == 0 );
TEST( str.compare( "BAR", 3, String::NoCase | String::Right ) == 0 );
TEST( str.compare( "Foobar" ) == 0 );
TEST( str.compare( "Foo" ) != 0 );
TEST( str.compare( "foobar", 0, String::NoCase ) == 0 );
TEST( str.compare( "FOOBAR", 0, String::NoCase ) == 0 );
TEST( str.compare( "Foobar", 0, String::Right ) == 0 );
TEST( str.compare( "foobar", 0, String::NoCase | String::Right ) == 0 );
}
void testOrder()
{
Vector< String > strs;
strs.push_back( "a" );
strs.push_back( "a0" );
strs.push_back( "a1" );
strs.push_back( "a1a" );
strs.push_back( "a1b" );
strs.push_back( "a2" );
strs.push_back( "a10" );
strs.push_back( "a20" );
for( U32 i = 0; i < strs.size(); ++ i )
{
for( U32 j = 0; j < i; ++ j )
{
TEST( strs[ j ] < strs[ i ] );
TEST( strs[ i ] > strs[ j ] );
TEST( !( strs[ j ] > strs[ i ] ) );
TEST( !( strs[ i ] < strs[ i ] ) );
TEST( strs[ j ] <= strs[ i ] );
TEST( strs[ i ] >= strs[ j ] );
}
TEST( !( strs[ i ] < strs[ i ] ) );
TEST( !( strs[ i ] > strs[ i ] ) );
TEST( strs[ i ] <= strs[ i ] );
TEST( strs[ i ] >= strs[ i ] );
for( U32 j = i + 1; j < strs.size(); ++ j )
{
TEST( strs[ j ] > strs[ i ] );
TEST( strs[ i ] < strs[ j ] );
TEST( !( strs[ j ] < strs[ i ] ) );
TEST( !( strs[ i ] > strs[ j ] ) );
TEST( strs[ j ] >= strs[ i ] );
TEST( strs[ i ] <= strs[ j ] );
}
}
}
void testFind()
{
//TODO
}
void testInsert()
{
// String.insert( Pos, Char )
TEST( String( "aa" ).insert( 1, 'c' ) == String( "aca" ) );
// String.insert( Pos, String )
TEST( String( "aa" ).insert( 1, "cc" ) == String( "acca" ) );
TEST( String( "aa" ).insert( 1, String( "cc" ) ) == String( "acca" ) );
// String.insert( Pos, String, Len )
TEST( String( "aa" ).insert( 1, "ccdddd", 2 ) == String( "acca" ) );
}
void testErase()
{
TEST( String( "abba" ).erase( 1, 2 ) == String( "aa" ) );
TEST( String( "abba" ).erase( 0, 4 ).isEmpty() );
}
void testReplace()
{
// String.replace( Pos, Len, String )
TEST( String( "abba" ).replace( 1, 2, "ccc" ) == String( "accca" ) );
TEST( String( "abba" ).replace( 1, 2, String( "ccc" ) ) == String( "accca" ) );
TEST( String( "abba" ).replace( 0, 4, "" ).isEmpty() );
TEST( String( "abba" ).replace( 2, 2, "c" ) == String( "abc" ) );
// String.replace( Char, Char )
TEST( String().replace( 'a', 'b' ).isEmpty() );
TEST( String( "ababc" ).replace( 'a', 'b' ) == String( "bbbbc" ) );
TEST( String( "ababc" ).replace( 'd', 'e' ) == String( "ababc" ) );
// String.replace( String, String )
TEST( String().replace( "foo", "bar" ).isEmpty() );
TEST( String( "foobarfoo" ).replace( "foo", "bar" ) == String( "barbarbar" ) );
TEST( String( "foobar" ).replace( "xx", "yy" ) == String( "foobar" ) );
TEST( String( "foofoofoo" ).replace( "foo", "" ).isEmpty() );
}
void testSubstr()
{
TEST( String( "foobar" ).substr( 0, 3 ) == String( "foo" ) );
TEST( String( "foobar" ).substr( 3 ) == String( "bar" ) );
TEST( String( "foobar" ).substr( 2, 2 ) == String( "ob" ) );
TEST( String( "foobar" ).substr( 2, 0 ).isEmpty() );
TEST( String( "foobar" ).substr( 0, 6 ) == String( "foobar" ) );
}
void testToString()
{
TEST( String::ToString( U32( 1 ) ) == String( "1" ) );
TEST( String::ToString( S32( -1 ) ) == String( "-1" ) );
TEST( String::ToString( F32( 1.01 ) ) == String( "1.01" ) );
TEST( String::ToString( "%s%i", "foo", 1 ) == String( "foo1" ) );
}
void testCaseConversion()
{
TEST( String::ToLower( "foobar123." ) == String( "foobar123." ) );
TEST( String::ToLower( "FOOBAR123." ) == String( "foobar123." ) );
TEST( String::ToUpper( "barfoo123." ) == String( "BARFOO123." ) );
TEST( String::ToUpper( "BARFOO123." ) == String( "BARFOO123." ) );
}
void testConcat()
{
TEST( String( "foo" ) + String( "bar" ) == String( "foobar" ) );
TEST( String() + String( "bar" ) == String( "bar" ) );
TEST( String( "foo" ) + String() == String( "foo" ) );
TEST( String() + String() == String() );
TEST( String( "fo" ) + 'o' == String( "foo" ) );
TEST( 'f' + String( "oo" ) == String( "foo" ) );
TEST( String( "foo" ) + "bar" == String( "foobar" ) );
TEST( "foo" + String( "bar" ) == String( "foobar" ) );
}
void testHash()
{
TEST( String( "foo" ).getHashCaseSensitive() == String( "foo" ).getHashCaseSensitive() );
TEST( String( "foo" ).getHashCaseSensitive() != String( "bar" ).getHashCaseSensitive() );
TEST( String( "foo" ).getHashCaseInsensitive() == String( "FOO" ).getHashCaseInsensitive() );
}
void testIntern()
{
TEST( String( "foo" ).intern().isSame( String( "foo" ).intern() ) );
TEST( !String( "foo" ).intern().isSame( String( "bar" ).intern() ) );
TEST( !String( "foo" ).intern().isSame( String( "Foo" ).intern() ) );
TEST( String( "foo" ).intern() == String( "foo" ).intern() );
TEST( String( "foo" ).intern() != String( "bar" ).intern() );
TEST( String( "foo" ).intern().isInterned() );
}
void run()
{
mStrings.push_back( new StrTest( NULL ) );
mStrings.push_back( new StrTest( "" ) );
mStrings.push_back( new StrTest( "Torque" ) );
mStrings.push_back( new StrTest( "TGEA" ) );
mStrings.push_back( new StrTest( "GarageGames" ) );
mStrings.push_back( new StrTest( "TGB" ) );
mStrings.push_back( new StrTest( "games" ) );
mStrings.push_back( new StrTest( "engine" ) );
mStrings.push_back( new StrTest( "rocks" ) );
mStrings.push_back( new StrTest( "technology" ) );
mStrings.push_back( new StrTest( "Torque 3D" ) );
mStrings.push_back( new StrTest( "Torque 2D" ) );
runTestOnStrings< Test1 >();
runTestOnStrings< Test2 >();
runPairwiseTestOnStrings< Test3 >();
testEmpty();
testTrim();
testCompare();
testOrder();
testFind();
testInsert();
testReplace();
testErase();
testSubstr();
testToString();
testCaseConversion();
testConcat();
testHash();
testIntern();
for( U32 i = 0; i < mStrings.size(); ++ i )
delete mStrings[ i ];
mStrings.clear();
}
};
CreateUnitTest( TestStringBuilder, "Util/StringBuilder" )
{
void run()
{
StringBuilder str;
str.append( 'f' );
str.append( "oo" );
str.append( String( "ba" ) );
str.append( "rfajskfdj", 1 );
str.format( "%s", "barfoo" );
TEST( str.end() == String( "foobarbarfoo" ) );
}
};
#endif // !TORQUE_SHIPPING

View file

@ -0,0 +1,138 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "unit/test.h"
#include "console/console.h"
#include "core/util/tVector.h"
#ifndef TORQUE_SHIPPING
using namespace UnitTesting;
#define TEST( x ) test( ( x ), "FAIL: " #x )
#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x )
CreateUnitTest( TestVector, "Util/Vector" )
{
bool dtorVals[ 10 ];
struct Dtor
{
bool* ptr;
Dtor() {}
Dtor( bool* ptr )
: ptr( ptr ) { *ptr = false; }
~Dtor()
{
*ptr = true;
}
};
void testDestruction()
{
Vector< Dtor > v;
for( U32 i = 0; i < 9; ++ i )
v.push_back( Dtor( &dtorVals[ i ] ) );
v.decrement();
v.decrement( 2 );
v.pop_back();
v.increment();
v.last() = Dtor( &dtorVals[ 9 ] );
v.clear();
TEST( dtorVals[ 0 ] );
TEST( dtorVals[ 1 ] );
TEST( dtorVals[ 2 ] );
TEST( dtorVals[ 3 ] );
TEST( dtorVals[ 4 ] );
TEST( dtorVals[ 5 ] );
TEST( dtorVals[ 6 ] );
TEST( dtorVals[ 7 ] );
TEST( dtorVals[ 8 ] );
TEST( dtorVals[ 9 ] );
}
static S32 QSORT_CALLBACK sortInts( const int* a, const int* b )
{
int av = *a;
int bv = *b;
if( av < bv )
return -1;
else if( av > bv )
return 1;
else
return 0;
}
void testSort()
{
Vector< int > v;
v.push_back( 0 );
v.push_back( 10 );
v.push_back( 2 );
v.push_back( 3 );
v.push_back( 14 );
v.push_back( 4 );
v.push_back( 12 );
v.push_back( 6 );
v.push_back( 16 );
v.push_back( 7 );
v.push_back( 8 );
v.push_back( 1 );
v.push_back( 11 );
v.push_back( 5 );
v.push_back( 13 );
v.push_back( 9 );
v.push_back( 15 );
v.sort( sortInts );
TEST( v[ 0 ] == 0 );
TEST( v[ 1 ] == 1 );
TEST( v[ 2 ] == 2 );
TEST( v[ 3 ] == 3 );
TEST( v[ 4 ] == 4 );
TEST( v[ 5 ] == 5 );
TEST( v[ 6 ] == 6 );
TEST( v[ 7 ] == 7 );
TEST( v[ 8 ] == 8 );
TEST( v[ 9 ] == 9 );
TEST( v[ 10 ] == 10 );
TEST( v[ 11 ] == 11 );
TEST( v[ 12 ] == 12 );
TEST( v[ 13 ] == 13 );
TEST( v[ 14 ] == 14 );
TEST( v[ 15 ] == 15 );
TEST( v[ 16 ] == 16 );
}
void run()
{
testSort();
testDestruction();
}
};
#endif

View file

@ -0,0 +1,198 @@
//-----------------------------------------------------------------------------
// 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 <time.h>
#include "core/util/timeClass.h"
namespace Torque
{
//Micro 0.000001 10-6
//Milli 0.001 10-3
static S8 _DaysInMonth[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static S8 _DaysInMonthLeap[13]= {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static S32 _DayNumber[13] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
static S32 _DayNumberLeap[13] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366};
/**----------------------------------------------------------------------------
* PRIVATE Test to see if a year is a leap year.
*
* @param year Year to test for leap year
* @return true if year is a leap year
*/
inline bool Time::_isLeapYear(S32 year) const
{
return ((year & 3) == 0) && ( ((year % 100) != 0) || ((year % 400) == 0) );
}
/**----------------------------------------------------------------------------
* PRIVATE Determine the number of days in any given month/year
*
* @param month Month to be tested
* @param year Year to be tested
* @return Number of days in month/year
*/
S32 Time::_daysInMonth(S32 month, S32 year) const
{
if (_isLeapYear(year))
return _DaysInMonthLeap[month];
else
return _DaysInMonth[month];
}
//-----------------------------------------------------------------------------
void Time::getCurrentDateTime(DateTime &dateTime)
{
time_t long_time;
time( &long_time );
struct tm *systime = localtime( &long_time );
dateTime.year = systime->tm_year;
dateTime.month = systime->tm_mon;
dateTime.day = systime->tm_mday;
dateTime.hour = systime->tm_hour;
dateTime.minute = systime->tm_min;
dateTime.second = systime->tm_sec;
dateTime.microsecond = 0;
}
Time Time::getCurrentTime()
{
return Torque::UnixTimeToTime( time( NULL ) );
}
bool Time::set(S32 year, S32 month, S32 day, S32 hour, S32 minute, S32 second, S32 microsecond)
{
second += microsecond / 100000;
microsecond %= 100000;
minute += second / 60;
second %= 60;
hour += minute / 60;
minute %= 60;
S32 carryDays = hour / 24;
hour %= 24;
bool leapYear = _isLeapYear(year);
year -= 1; // all the next operations need (year-1) so do it ahead of time
S32 gregorian = 365 * year // number of days since the epoch
+ (year/4) // add Julian leap year days
- (year/100) // subtract century leap years
+ (year/400) // add gregorian 400 year leap adjustment
+ ((367*month-362)/12) // days in prior months
+ day // add days
+ carryDays; // add days from time overflow/underflow
// make days in this year adjustment if leap year
if (leapYear)
{
if (month > 2)
gregorian -= 1;
}
else
{
if (month > 2)
gregorian -= 2;
}
_time = S64(gregorian) * OneDay;
_time += S64((hour * OneHour) +
(minute * OneMinute) +
(second * OneSecond) +
microsecond);
return true;
}
//-----------------------------------------------------------------------------
void Time::get(S32 *pyear, S32 *pmonth, S32 *pday, S32 *phour, S32 *pminute, S32 *psecond, S32 *pmicrosecond) const
{
// extract date if requested
if (pyear || pmonth || pday)
{
S32 gregorian = (S32)(_time / OneDay);
S32 prior = gregorian - 1; // prior days
S32 years400 = prior / 146097L; // number of 400 year cycles
S32 days400 = prior % 146097L; // days NOT in years400
S32 years100 = days400 / 36524L; // number 100 year cycles not checked
S32 days100 = days400 % 36524L; // days NOT already included
S32 years4 = days100 / 1461L; // number 4 year cycles not checked
S32 days4 = days100 % 1461L; // days NOT already included
S32 year1 = days4 / 365L; // number years not already checked
S32 day1 = days4 % 365L; // days NOT already included
S32 day;
S32 year = (400 * years400) + (100 * years100) + (4 * years4) + year1;
// December 31 of leap year
if (years100 == 4 || year1 == 4)
{
day = 366;
}
else
{
year += 1;
day = day1 + 1;
}
const S32 *dayNumber = _isLeapYear(year) ? _DayNumberLeap : _DayNumber;
// find month and day in month given computed year and day number,
S32 month = 1;
while(day >= dayNumber[month])
month++;
day -= dayNumber[month-1];
if(pyear)
*pyear = year;
if(pmonth)
*pmonth = month;
if(pday)
*pday = day;
}
// extract time
if (phour)
*phour = (S32)((_time % OneDay) / OneHour);
S32 time = (S32)(_time % OneHour);
if (pminute)
*pminute = time / (S32)OneMinute;
time %= OneMinute;
if (psecond)
*psecond = time / (S32)OneSecond;
if (pmicrosecond)
*pmicrosecond = time % OneSecond;
}
} // Namespace

View file

@ -0,0 +1,278 @@
//-----------------------------------------------------------------------------
// 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 _TIMECLASS_H_
#define _TIMECLASS_H_
#ifndef _TORQUE_TYPES_H_
#include "platform/types.h"
#endif
#if defined(TORQUE_COMPILER_VISUALC)
#define TORQUE_CONSTANT_S64(a) (a##I64)
#define TORQUE_CONSTANT_U64(a) (a##UI64)
#else
#define TORQUE_CONSTANT_S64(a) (a##LL) ///< Used to declare signed 64 bit constants @hideinitializer
#define TORQUE_CONSTANT_U64(a) (a##ULL) ///< Used to declare unsigned 64 bit constants @hideinitializer
#endif
namespace Torque
{
//-----------------------------------------------------------------------------
/// 64 bit time representation with ten microsecond resolution.
class Time
{
class Tester;
public:
struct DateTime
{
S32 year;
S32 month;
S32 day;
S32 hour;
S32 minute;
S32 second;
S32 microsecond;
};
static void getCurrentDateTime(DateTime &dateTime);
static Time getCurrentTime();
static const S64 OneDay = TORQUE_CONSTANT_S64(8640000000);
static const S64 OneHour = TORQUE_CONSTANT_S64( 360000000);
static const S64 OneMinute = TORQUE_CONSTANT_S64( 6000000);
static const S64 OneSecond = TORQUE_CONSTANT_S64( 100000);
static const S64 OneMillisecond = TORQUE_CONSTANT_S64( 100);
Time();
explicit Time(S64 time);
Time(S32 year, S32 month, S32 day, S32 hour, S32 minute, S32 second, S32 microsecond);
Time(const DateTime &dateTime);
bool set(S32 year, S32 month, S32 day, S32 hour, S32 minute, S32 second, S32 microsecond);
void get(S32 *year, S32 *month, S32 *day, S32 *hour, S32 *minute, S32 *second, S32 *microsecond) const;
Time operator+() const;
Time operator-() const;
Time operator+(const Time &time) const;
Time operator-(const Time &time) const;
S64 operator/(const Time &time) const;
const Time& operator+=(const Time time);
const Time& operator-=(const Time time);
template<typename T> Time operator*(T scaler) const { return Time(_time * scaler); }
template<typename T> Time operator/(T scaler) const { return Time(_time / scaler); }
template<typename T> friend Time operator*(T scaler,Time t) { return t * scaler; }
bool operator==(const Time &time) const;
bool operator!=(const Time &time) const;
bool operator<(const Time &time) const;
bool operator>(const Time &time) const;
bool operator<=(const Time &time) const;
bool operator>=(const Time &time) const;
operator Tester*() const
{
static Tester test;
return (_time == 0)? 0: &test;
}
bool operator!() const;
S64 getSeconds() const;
S64 getMilliseconds() const;
S64 getMicroseconds() const;
S64 getInternalRepresentation() const;
private:
class Tester
{
void operator delete(void*) {}
};
S64 _time;
bool _isLeapYear(S32 year) const;
S32 _daysInMonth(S32 month, S32 year) const;
};
namespace TimeConstant
{
const Time OneDay (Time::OneDay);
const Time OneHour (Time::OneHour);
const Time OneMinute (Time::OneMinute);
const Time OneSecond (Time::OneSecond);
const Time OneMillisecond (Time::OneMillisecond);
}
//-----------------------------------------------------------------------------
inline Time::Time()
{
_time = 0;
}
inline Time::Time(S64 time)
{
_time = time;
}
inline Time::Time(S32 year, S32 month, S32 day, S32 hour, S32 minute, S32 second, S32 microsecond)
{
set(year, month, day, hour, minute, second, microsecond);
}
inline Time::Time(const Time::DateTime &dateTime)
{
set(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second, dateTime.microsecond);
}
inline Time Time::operator+() const
{
return Time(_time);
}
inline Time Time::operator-() const
{
return Time(-_time);
}
inline Time Time::operator+(const Time &time) const
{
return Time(_time + time._time);
}
inline Time Time::operator-(const Time &time) const
{
return Time(_time - time._time);
}
inline S64 Time::operator/(const Time &time) const
{
return S64(_time / time._time);
}
inline const Time& Time::operator+=(const Time time)
{
_time += time._time;
return *this;
}
inline const Time& Time::operator-=(const Time time)
{
_time -= time._time;
return *this;
}
inline bool Time::operator==(const Time &time) const
{
return (_time == time._time);
}
inline bool Time::operator!=(const Time &time) const
{
return (_time != time._time);
}
inline bool Time::operator<(const Time &time) const
{
return (_time < time._time);
}
inline bool Time::operator>(const Time &time) const
{
return (_time > time._time);
}
inline bool Time::operator<=(const Time &time) const
{
return (_time <= time._time);
}
inline bool Time::operator>=(const Time &time) const
{
return (_time >= time._time);
}
inline bool Time::operator !() const
{
return _time == 0;
}
inline S64 Time::getSeconds() const
{
return _time / TimeConstant::OneSecond._time;
}
inline S64 Time::getMilliseconds() const
{
return _time / TimeConstant::OneMillisecond._time;
}
inline S64 Time::getMicroseconds() const
{
return _time * 10;
}
inline S64 Time::getInternalRepresentation() const
{
return _time;
}
//-----------------------------------------------------------------------------
// time i/o time functions
template<class S> inline bool read(S &stream, Time *theTime)
{
S64 time;
bool ret = read(stream, &time);
*theTime = Time(time);
return ret;
}
template<class S> inline bool write(S &stream, const Time &theTime)
{
S64 time = theTime.getInternalRepresentation();
return write(stream, time);
}
//-----------------------------------------------------------------------------
inline Time UnixTimeToTime(U32 t)
{
// Converts "unix" time, seconds since 00:00:00 UTC, January 1, 1970
return Time(((S64)(t)) * 100000 + TORQUE_CONSTANT_S64(6213568320000000));
}
inline Time Win32FileTimeToTime(U32 low,U32 high)
{
// Converts Win32 "file" time, 100 nanosecond intervals since 00:00:00 UTC, January 1, 1601
S64 t = (((S64)high) << 32) + low;
return Time(t / 100 + TORQUE_CONSTANT_S64(5049120960000000));
}
} // Namespace
#endif

View file

@ -0,0 +1,168 @@
//-----------------------------------------------------------------------------
// 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 _TIMESOURCE_H_
#define _TIMESOURCE_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _TSTREAM_H_
#include "core/stream/tStream.h"
#endif
#ifndef _SIM_H_
#include "console/sim.h"
#endif
/// Timer that queries the real-time ticker.
struct RealMSTimer
{
typedef U32 TickType;
static TickType getTick()
{
return Platform::getRealMilliseconds();
}
};
/// Timer that queries the simulation-time ticker.
struct VirtualMSTimer
{
typedef U32 TickType;
static TickType getTick()
{
return Platform::getVirtualMilliseconds();
}
};
/// Timer that queries Sim::getCurrentTime().
struct SimMSTimer
{
typedef U32 TickType;
static TickType getTick()
{
return Sim::getCurrentTime();
}
};
///
template< class Timer = RealMSTimer, typename Tick = typename Timer::TickType >
class GenericTimeSource : public IPositionable< Tick >,
public IProcess,
public IResettable
{
public:
typedef IPositionable< Tick > Parent;
typedef Tick TickType;
protected:
///
TickType mStartTime;
///
TickType mPauseTime;
///
Timer mTimer;
public:
GenericTimeSource()
: mStartTime( TypeTraits< TickType >::MAX ),
mPauseTime( TypeTraits< TickType >::MAX ) {}
bool isStarted() const
{
return ( mStartTime != TypeTraits< TickType >::MAX );
}
bool isPaused() const
{
return ( mPauseTime != TypeTraits< TickType >::MAX );
}
/// Return the number of ticks since the time source
/// has been started.
TickType getPosition() const
{
if( !isStarted() )
return TypeTraits< TickType >::ZERO;
else if( isPaused() )
return ( mPauseTime - mStartTime );
else
return ( mTimer.getTick() - mStartTime );
}
///
void setPosition( TickType pos )
{
if( !isStarted() )
mStartTime = pos;
else
{
TickType savedStartTime = mStartTime;
mStartTime = ( mTimer.getTick() - pos );
if( isPaused() )
mPauseTime = ( mStartTime + ( mPauseTime - savedStartTime ) );
}
}
// IResettable.
virtual void reset()
{
mStartTime = TypeTraits< TickType >::MAX;
mPauseTime = TypeTraits< TickType >::MAX;
}
// IProcess.
virtual void start()
{
if( !isStarted() )
{
TickType now = mTimer.getTick();
if( isPaused() )
{
mStartTime += now - mPauseTime;
mPauseTime = TypeTraits< TickType >::MAX;
}
else
mStartTime = now;
}
}
virtual void stop()
{
reset();
}
virtual void pause()
{
if( !isPaused() )
mPauseTime = mTimer.getTick();
}
};
#endif // _TIMESOURCE_H_

View file

@ -0,0 +1,453 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
// Original code:
/*
** Copyright (C) 1998-1999 Greg Stein. All Rights Reserved.
**
** By using this file, you agree to the terms and conditions set forth in
** the LICENSE.html file which can be found at the top level of the mod_dav
** distribution or at http://www.webdav.org/mod_dav/license-1.html.
**
** Contact information:
** Greg Stein, PO Box 3151, Redmond, WA, 98073
** gstein@lyra.org, http://www.webdav.org/mod_dav/
*/
/*
** DAV opaquelocktoken scheme implementation
**
** Written 5/99 by Keith Wannamaker, wannamak@us.ibm.com
** Adapted from ISO/DCE RPC spec and a former Internet Draft
** by Leach and Salz:
** http://www.ics.uci.edu/pub/ietf/webdav/uuid-guid/draft-leach-uuids-guids-01
**
** Portions of the code are covered by the following license:
**
** Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc.
** Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. &
** Digital Equipment Corporation, Maynard, Mass.
** Copyright (c) 1998 Microsoft.
** To anyone who acknowledges that this file is provided "AS IS"
** without any express or implied warranty: permission to use, copy,
** modify, and distribute this file for any purpose is hereby
** granted without fee, provided that the above copyright notices and
** this notice appears in all source code copies, and that none of
** the names of Open Software Foundation, Inc., Hewlett-Packard
** Company, or Digital Equipment Corporation be used in advertising
** or publicity pertaining to distribution of the software without
** specific, written prior permission. Neither Open Software
** Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital Equipment
** Corporation makes any representations about the suitability of
** this software for any purpose.
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
#include "core/util/md5.h"
typedef unsigned long unsigned32;
typedef unsigned short unsigned16;
typedef unsigned char unsigned8;
typedef struct {
char nodeID[6];
} uuid_node_t;
#undef xuuid_t
typedef struct _uuid_t
{
unsigned32 time_low;
unsigned16 time_mid;
unsigned16 time_hi_and_version;
unsigned8 clock_seq_hi_and_reserved;
unsigned8 clock_seq_low;
unsigned8 node[6];
} xuuid_t;
/* data type for UUID generator persistent state */
typedef struct {
uuid_node_t node; /* saved node ID */
unsigned16 cs; /* saved clock sequence */
} uuid_state;
#if defined(_XBOX)
#include <xtl.h>
#elif defined(_WIN32)
#include <windows.h>
#else
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#ifdef XP_BEOS
#include <be/net/netdb.h>
#endif
#endif
/* set the following to the number of 100ns ticks of the actual resolution of
your system's clock */
#define UUIDS_PER_TICK 1024
/* Set this to what your compiler uses for 64 bit data type */
#ifdef _WIN32
#define unsigned64_t unsigned __int64
#define I64(C) C
#else
#define unsigned64_t unsigned long long
#define I64(C) C##LL
#endif
typedef unsigned64_t uuid_time_t;
static void format_uuid_v1(xuuid_t * uuid, unsigned16 clockseq, uuid_time_t timestamp, uuid_node_t node);
static void get_current_time(uuid_time_t * timestamp);
static unsigned16 true_random(void);
static void get_pseudo_node_identifier(uuid_node_t *node);
static void get_system_time(uuid_time_t *uuid_time);
static void get_random_info(unsigned char seed[16]);
/* dav_create_opaquelocktoken - generates a UUID version 1 token.
* Clock_sequence and node_address set to pseudo-random
* numbers during init.
*
* Should postpend pid to account for non-seralized creation?
*/
static int create_token(uuid_state *st, xuuid_t *u)
{
uuid_time_t timestamp;
get_current_time(&timestamp);
format_uuid_v1(u, st->cs, timestamp, st->node);
return 1;
}
/*
* dav_create_uuid_state - seed UUID state with pseudorandom data
*/
static void create_uuid_state(uuid_state *st)
{
st->cs = true_random();
get_pseudo_node_identifier(&st->node);
}
/*
* dav_format_opaquelocktoken - generates a text representation
* of an opaquelocktoken
*/
static void format_token(char *target, const xuuid_t *u)
{
sprintf(target, "%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
u->time_low, u->time_mid, u->time_hi_and_version,
u->clock_seq_hi_and_reserved, u->clock_seq_low,
u->node[0], u->node[1], u->node[2],
u->node[3], u->node[4], u->node[5]);
}
/* convert a pair of hex digits to an integer value [0,255] */
static int dav_parse_hexpair(const char *s)
{
int result;
int temp;
result = s[0] - '0';
if (result > 48)
result = (result - 39) << 4;
else if (result > 16)
result = (result - 7) << 4;
else
result = result << 4;
temp = s[1] - '0';
if (temp > 48)
result |= temp - 39;
else if (temp > 16)
result |= temp - 7;
else
result |= temp;
return result;
}
/* dav_parse_locktoken: Parses string produced from
* dav_format_opaquelocktoken back into a xuuid_t
* structure. On failure, return DAV_IF_ERROR_PARSE,
* else DAV_IF_ERROR_NONE.
*/
static int parse_token(const char *char_token, xuuid_t *bin_token)
{
int i;
for (i = 0; i < 36; ++i) {
char c = char_token[i];
if (!isxdigit(c) &&
!(c == '-' && (i == 8 || i == 13 || i == 18 || i == 23)))
return -1;
}
if (char_token[36] != '\0')
return -1;
bin_token->time_low =
(dav_parse_hexpair(&char_token[0]) << 24) |
(dav_parse_hexpair(&char_token[2]) << 16) |
(dav_parse_hexpair(&char_token[4]) << 8) |
dav_parse_hexpair(&char_token[6]);
bin_token->time_mid =
(dav_parse_hexpair(&char_token[9]) << 8) |
dav_parse_hexpair(&char_token[11]);
bin_token->time_hi_and_version =
(dav_parse_hexpair(&char_token[14]) << 8) |
dav_parse_hexpair(&char_token[16]);
bin_token->clock_seq_hi_and_reserved = dav_parse_hexpair(&char_token[19]);
bin_token->clock_seq_low = dav_parse_hexpair(&char_token[21]);
for (i = 6; i--;)
bin_token->node[i] = dav_parse_hexpair(&char_token[i*2+24]);
return 0;
}
/* format_uuid_v1 -- make a UUID from the timestamp, clockseq, and node ID */
static void format_uuid_v1(xuuid_t * uuid, unsigned16 clock_seq,
uuid_time_t timestamp, uuid_node_t node)
{
/* Construct a version 1 uuid with the information we've gathered
* plus a few constants. */
uuid->time_low = (unsigned long)(timestamp & 0xFFFFFFFF);
uuid->time_mid = (unsigned short)((timestamp >> 32) & 0xFFFF);
uuid->time_hi_and_version = (unsigned short)((timestamp >> 48) & 0x0FFF);
uuid->time_hi_and_version |= (1 << 12);
uuid->clock_seq_low = clock_seq & 0xFF;
uuid->clock_seq_hi_and_reserved = (clock_seq & 0x3F00) >> 8;
uuid->clock_seq_hi_and_reserved |= 0x80;
memcpy(&uuid->node, &node, sizeof uuid->node);
}
/* get-current_time -- get time as 60 bit 100ns ticks since whenever.
Compensate for the fact that real clock resolution is less than 100ns. */
static void get_current_time(uuid_time_t * timestamp)
{
uuid_time_t time_now;
static uuid_time_t time_last;
static unsigned16 uuids_this_tick;
static int inited = 0;
if (!inited) {
get_system_time(&time_now);
uuids_this_tick = UUIDS_PER_TICK;
inited = 1;
};
while (1) {
get_system_time(&time_now);
/* if clock reading changed since last UUID generated... */
if (time_last != time_now) {
/* reset count of uuids gen'd with this clock reading */
uuids_this_tick = 0;
break;
};
if (uuids_this_tick < UUIDS_PER_TICK) {
uuids_this_tick++;
break;
}; /* going too fast for our clock; spin */
}; /* add the count of uuids to low order bits of the clock reading */
*timestamp = time_now + uuids_this_tick;
time_last = time_now;
}
/* true_random -- generate a crypto-quality random number.
This sample doesn't do that. */
static unsigned16 true_random(void)
{
uuid_time_t time_now;
get_system_time(&time_now);
time_now = time_now/UUIDS_PER_TICK;
srand((unsigned int)(((time_now >> 32) ^ time_now)&0xffffffff));
return rand();
}
/* This sample implementation generates a random node ID *
* in lieu of a system dependent call to get IEEE node ID. */
static void get_pseudo_node_identifier(uuid_node_t *node)
{
unsigned char seed[16];
get_random_info(seed);
seed[0] |= 0x80;
memcpy(node, seed, sizeof(uuid_node_t));
}
/* system dependent call to get the current system time.
Returned as 100ns ticks since Oct 15, 1582, but resolution may be
less than 100ns. */
#ifdef _WIN32
static void get_system_time(uuid_time_t *uuid_time)
{
ULARGE_INTEGER time;
GetSystemTimeAsFileTime((FILETIME *)&time);
/* NT keeps time in FILETIME format which is 100ns ticks since
Jan 1, 1601. UUIDs use time in 100ns ticks since Oct 15, 1582.
The difference is 17 Days in Oct + 30 (Nov) + 31 (Dec)
+ 18 years and 5 leap days. */
time.QuadPart +=
(unsigned __int64) (1000*1000*10)
* (unsigned __int64) (60 * 60 * 24)
* (unsigned __int64) (17+30+31+365*18+5);
*uuid_time = time.QuadPart;
}
#if defined(_XBOX)
#include "platform/platformAssert.h"
#endif
static void get_random_info(unsigned char seed[16])
{
#if defined(_XBOX)
AssertFatal(false, "get_random_info not implemented on Xbox360");
#else
MD5_CTX c;
struct {
MEMORYSTATUS m;
SYSTEM_INFO s;
FILETIME t;
LARGE_INTEGER pc;
DWORD tc;
DWORD l;
TCHAR hostname[MAX_COMPUTERNAME_LENGTH + 1];
} r;
MD5Init(&c); /* memory usage stats */
GlobalMemoryStatus(&r.m); /* random system stats */
GetSystemInfo(&r.s); /* 100ns resolution (nominally) time of day */
GetSystemTimeAsFileTime(&r.t); /* high resolution performance counter */
QueryPerformanceCounter(&r.pc); /* milliseconds since last boot */
r.tc = GetTickCount();
r.l = MAX_COMPUTERNAME_LENGTH + 1;
GetComputerName(r.hostname, &r.l );
MD5Update(&c, (unsigned char *) &r, sizeof(r));
MD5Final(seed, &c);
#endif
}
#else /* WIN32 */
static void get_system_time(uuid_time_t *uuid_time)
{
struct timeval tp;
gettimeofday(&tp, (struct timezone *)0);
/* Offset between UUID formatted times and Unix formatted times.
UUID UTC base time is October 15, 1582.
Unix base time is January 1, 1970. */
*uuid_time = (tp.tv_sec * 10000000) + (tp.tv_usec * 10) +
I64(0x01B21DD213814000);
}
static void get_random_info(unsigned char seed[16])
{
MD5_CTX c;
/* Leech & Salz use Linux-specific struct sysinfo;
* replace with pid/tid for portability (in the spirit of mod_unique_id) */
struct {
/* Add thread id here, if applicable, when we get to pthread or apr */
pid_t pid;
struct timeval t;
char hostname[257];
} r;
MD5Init(&c);
r.pid = getpid();
gettimeofday(&r.t, (struct timezone *)0);
gethostname(r.hostname, 256);
MD5Update(&c, (unsigned char *)&r, sizeof(r));
MD5Final(seed, &c);
}
#endif /* WIN32 */
#include "core/util/uuid.h"
namespace {
bool gUUIDStateInitialized;
uuid_state gUUIDState;
}
namespace Torque
{
UUID UUID::smNull;
void UUID::generate()
{
if( !gUUIDStateInitialized )
{
create_uuid_state( &gUUIDState );
gUUIDStateInitialized = true;
}
create_token( &gUUIDState, ( xuuid_t* ) this );
}
String UUID::toString() const
{
char buffer[ 1024 ];
format_token( buffer, ( xuuid_t* ) this );
return buffer;
}
bool UUID::fromString( const char* str )
{
if( parse_token( str, ( xuuid_t* ) this ) != 0 )
{
dMemset( this, 0, sizeof( UUID ) );
return false;
}
return true;
}
U32 UUID::getHash() const
{
return ( a + b + c + d + e + f[ 0 ] + f[ 1 ] + f[ 2 ] + f[ 3 ] + f[ 4 ] + f[ 5 ] );
}
}

View file

@ -0,0 +1,94 @@
//-----------------------------------------------------------------------------
// 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_UUID_H_
#define _TORQUE_UUID_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
namespace Torque
{
/// A universally unique identifier.
class UUID
{
public:
typedef void Parent;
protected:
U32 a;
U16 b;
U16 c;
U8 d;
U8 e;
U8 f[ 6 ];
static UUID smNull;
public:
UUID()
{
dMemset( this, 0, sizeof( UUID ) );
}
///
bool isNull() const { return ( *this == smNull ); }
/// Generate a new universally unique identifier (UUID).
void generate();
/// Convert the given universally unique identifier to a printed string
/// representation.
String toString() const;
/// Parse a text string generated by UUIDToString back into a
/// universally unique identifier (UUID).
bool fromString( const char* str );
/// Return a hash value for this UUID.
U32 getHash() const;
bool operator ==( const UUID& uuid ) const
{
return ( dMemcmp( this, &uuid, sizeof( UUID ) ) == 0 );
}
bool operator !=( const UUID& uuid ) const
{
return !( *this == uuid );
}
};
}
namespace DictHash
{
inline U32 hash( const Torque::UUID& uuid )
{
return uuid.getHash();
}
}
#endif // !_TORQUE_UUID_H_

View file

@ -0,0 +1,320 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/stream/stream.h"
#include "core/strings/stringFunctions.h"
#include "core/util/zip/centralDir.h"
#include "core/util/zip/compressor.h"
#include "core/util/safeDelete.h"
namespace Zip
{
//-----------------------------------------------------------------------------
// CentralDir Class
//-----------------------------------------------------------------------------
CentralDir::CentralDir()
{
mHeaderSig = mCentralDirSignature;
mDiskNumStart = 0;
mInternalFileAttr = 0;
mExternalFileAttr = 0;
mLocalHeadOffset = 0;
mVersionMadeBy = 0;
mFileComment = NULL;
mInternalFlags = 0;
}
CentralDir::CentralDir(FileHeader &fh) : FileHeader(fh)
{
mHeaderSig = mCentralDirSignature;
mDiskNumStart = 0;
mInternalFileAttr = 0;
mExternalFileAttr = 0;
mLocalHeadOffset = 0;
mVersionMadeBy = 0;
mFileComment = NULL;
}
CentralDir::~CentralDir()
{
SAFE_DELETE_ARRAY(mFileComment);
}
//-----------------------------------------------------------------------------
bool CentralDir::read(Stream *stream)
{
stream->read(&mHeaderSig);
if(mHeaderSig != mCentralDirSignature)
return false;
stream->read(&mVersionMadeBy);
stream->read(&mExtractVer);
stream->read(&mFlags);
stream->read(&mCompressMethod);
stream->read(&mModTime);
stream->read(&mModDate);
stream->read(&mCRC32);
stream->read(&mCompressedSize);
stream->read(&mUncompressedSize);
U16 fnLen, efLen, fcLen;
stream->read(&fnLen);
stream->read(&efLen);
stream->read(&fcLen);
stream->read(&mDiskNumStart);
stream->read(&mInternalFileAttr);
stream->read(&mExternalFileAttr);
stream->read(&mLocalHeadOffset);
char *fn = new char[fnLen + 1];
stream->read(fnLen, fn);
fn[fnLen] = 0;
mFilename = String(fn);
SAFE_DELETE_ARRAY(fn);
// [tom, 10/28/2006] We currently only need the extra fields when we want to
// open the file, so we won't bother reading them here. This avoids keeping
// them in memory twice.
//readExtraFields(stream, efLen);
stream->setPosition(stream->getPosition() + efLen);
fn = new char[fcLen + 1];
stream->read(fcLen, fn);
fn[fcLen] = 0;
SAFE_DELETE_ARRAY(mFileComment);
mFileComment = fn;
// Sanity checks to make life easier elsewhere
if(mCompressMethod != Stored && mUncompressedSize == 0 && mCompressedSize == 0)
mCompressMethod = Stored;
return true;
}
bool CentralDir::write(Stream *stream)
{
mHeaderSig = mCentralDirSignature;
stream->write(mHeaderSig);
stream->write(mVersionMadeBy);
stream->write(mExtractVer);
stream->write(mFlags);
stream->write(mCompressMethod);
stream->write(mModTime);
stream->write(mModDate);
stream->write(mCRC32);
stream->write(mCompressedSize);
stream->write(mUncompressedSize);
U16 fnLen = mFilename.length(),
efLen = 0,
fcLen = mFileComment ? (U16)dStrlen(mFileComment) : 0;
stream->write(fnLen);
stream->write(efLen);
stream->write(fcLen);
stream->write(mDiskNumStart);
stream->write(mInternalFileAttr);
stream->write(mExternalFileAttr);
stream->write(mLocalHeadOffset);
if(fnLen)
stream->write(fnLen, mFilename);
// FIXME [tom, 10/29/2006] Write extra fields here
if(fcLen)
stream->write(fcLen, mFileComment);
return true;
}
//-----------------------------------------------------------------------------
void CentralDir::setFileComment(const char *comment)
{
SAFE_DELETE_ARRAY(mFileComment);
mFileComment = new char [dStrlen(comment)+1];
dStrcpy(mFileComment, comment);
}
//-----------------------------------------------------------------------------
// EndOfCentralDir Class
//-----------------------------------------------------------------------------
EndOfCentralDir::EndOfCentralDir()
{
mHeaderSig = mEOCDSignature;
mDiskNum = 0;
mStartCDDiskNum = 0;
mNumEntriesInThisCD = 0;
mTotalEntriesInCD = 0;
mCDSize = 0;
mCDOffset = 0;
mCommentSize = 0;
mZipComment = NULL;
}
EndOfCentralDir::~EndOfCentralDir()
{
SAFE_DELETE_ARRAY(mZipComment);
}
//-----------------------------------------------------------------------------
bool EndOfCentralDir::read(Stream *stream)
{
stream->read(&mHeaderSig);
if(mHeaderSig != mEOCDSignature)
return false;
stream->read(&mDiskNum);
stream->read(&mStartCDDiskNum);
stream->read(&mNumEntriesInThisCD);
stream->read(&mTotalEntriesInCD);
stream->read(&mCDSize);
stream->read(&mCDOffset);
stream->read(&mCommentSize);
char *comment = new char[mCommentSize + 1];
stream->read(mCommentSize, comment);
comment[mCommentSize] = 0;
SAFE_DELETE_ARRAY(mZipComment);
mZipComment = comment;
return true;
}
bool EndOfCentralDir::write(Stream *stream)
{
stream->write(mHeaderSig);
stream->write(mDiskNum);
stream->write(mStartCDDiskNum);
stream->write(mNumEntriesInThisCD);
stream->write(mTotalEntriesInCD);
stream->write(mCDSize);
stream->write(mCDOffset);
stream->write(mCommentSize);
if(mZipComment && mCommentSize)
stream->write(mCommentSize, mZipComment);
return true;
}
//-----------------------------------------------------------------------------
// [tom, 10/19/2006] I know, i know ... this'll get rewritten.
// [tom, 1/23/2007] Maybe.
bool EndOfCentralDir::findInStream(Stream *stream)
{
U32 initialPos = stream->getPosition();
U32 size = stream->getStreamSize();
U32 pos;
if(size == 0)
return false;
if(! stream->setPosition(size - mRecordSize))
goto hell;
U32 sig;
stream->read(&sig);
if(sig == mEOCDSignature)
{
stream->setPosition(size - mRecordSize);
return true;
}
// OK, so we couldn't find the EOCD where we expected it. The zip file
// either has comments or isn't a zip file. We need to search the last
// 64Kb of the file for the EOCD.
pos = size > mEOCDSearchSize ? size - mEOCDSearchSize : 0;
if(! stream->setPosition(pos))
goto hell;
while(pos < (size - 4))
{
stream->read(&sig);
if(sig == mEOCDSignature)
{
stream->setPosition(pos);
return true;
}
pos++;
if(! stream->setPosition(pos))
goto hell;
}
hell:
stream->setPosition(initialPos);
return false;
}
//-----------------------------------------------------------------------------
void EndOfCentralDir::setZipComment(U16 commentSize, const char *zipComment)
{
SAFE_DELETE_ARRAY(mZipComment);
mZipComment = new char [commentSize];
dMemcpy((void *)mZipComment, zipComment, commentSize);
mCommentSize = commentSize;
}
void EndOfCentralDir::setZipComment(const char *zipComment)
{
setZipComment(dStrlen(zipComment), zipComment);
}
} // end namespace Zip

View file

@ -0,0 +1,112 @@
//-----------------------------------------------------------------------------
// 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/zip/fileHeader.h"
#ifndef _CENTRALDIR_H_
#define _CENTRALDIR_H_
namespace Zip
{
/// @addtogroup zipint_group
/// @ingroup zip_group
// @{
/// Internal flags used by the zip code when writing zips
enum CDIntFlags
{
CDFileDirty = BIT(0),
CDFileAdded = BIT(1),
CDFileDeleted = BIT(2),
CDFileOpen = BIT(3)
};
class CentralDir : public FileHeader
{
typedef FileHeader Parent;
static const U32 mCentralDirSignature = 0x02014b50;
char *mFileComment;
public:
U16 mDiskNumStart;
U16 mInternalFileAttr;
U32 mExternalFileAttr;
U32 mLocalHeadOffset;
U16 mVersionMadeBy;
U32 mInternalFlags;
CentralDir();
CentralDir(FileHeader &fh);
virtual ~CentralDir();
virtual bool read(Stream *stream);
virtual bool write(Stream *stream);
void setFileComment(const char *comment);
};
class EndOfCentralDir
{
static const U32 mEOCDSignature = 0x06054b50;
/// The size of the EndOfCentralDir record in the zip file, used to locate it
/// at the end of the file.
static const U32 mRecordSize = 20;
/// The number of bytes from the end of the file to start searching for the EOCD
static const U32 mEOCDSearchSize = 64 * 1024;
public:
U32 mHeaderSig;
U16 mDiskNum;
U16 mStartCDDiskNum;
U16 mNumEntriesInThisCD;
U16 mTotalEntriesInCD;
U32 mCDSize;
U32 mCDOffset;
U16 mCommentSize;
const char *mZipComment;
EndOfCentralDir();
virtual ~EndOfCentralDir();
virtual bool findInStream(Stream *stream);
virtual bool read(Stream *stream);
virtual bool write(Stream *stream);
virtual void setZipComment(const char *zipComment);
virtual void setZipComment(U16 commentSize, const char *zipComment);
};
// @}
} // end namespace Zip
#endif // _CENTRALDIR_H_

View file

@ -0,0 +1,70 @@
//-----------------------------------------------------------------------------
// 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/util/zip/compressor.h"
namespace Zip
{
static Compressor *gCompressorInitList = NULL;
//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
Compressor::Compressor(S32 method, const char *name)
{
mName = name;
mMethod = method;
mNext = gCompressorInitList;
gCompressorInitList = this;
}
//-----------------------------------------------------------------------------
// Static Methods
//-----------------------------------------------------------------------------
Compressor *Compressor::findCompressor(S32 method)
{
for(Compressor *walk = gCompressorInitList;walk;walk = walk->mNext)
{
if(walk->getMethod() == method)
return walk;
}
return NULL;
}
Compressor *Compressor::findCompressor(const char *name)
{
for(Compressor *walk = gCompressorInitList;walk;walk = walk->mNext)
{
if(dStricmp(walk->getName(), name) == 0)
return walk;
}
return NULL;
}
} // end namespace Zip

View file

@ -0,0 +1,103 @@
//-----------------------------------------------------------------------------
// 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/zip/centralDir.h"
#ifndef _COMPRESSOR_H_
#define _COMPRESSOR_H_
// Forward refs
class Stream;
namespace Zip
{
/// @addtogroup zipint_group
/// @ingroup zip_group
// @{
// [tom, 10/26/2006] Standard Zip compression methods
enum CompressionMethod
{
Stored = 0,
Shrunk = 1,
ReducedL1 = 2,
ReducedL2 = 3,
ReducedL3 = 4,
ReducedL4 = 5,
Imploded = 6,
ReservedTokenized = 7,
Deflated = 8,
EnhDefalted = 9,
DateCompression = 10,
ReservedPKWARE = 11,
BZip2 = 12,
PPMd = 98,
AESEncrypted = 99, // WinZip's AES Encrypted zips use compression method 99
// to indicate AES encryption. The actual compression
// method is specified in the AES extra field.
};
class Compressor
{
Compressor *mNext;
protected:
const char *mName; //!< The name of the compression method
S32 mMethod; //!< The compression method as in the Zip header
public:
Compressor(S32 method, const char *name);
virtual ~Compressor() {}
inline const char * getName() { return mName; }
inline S32 getMethod() { return mMethod; }
virtual Stream *createReadStream(const CentralDir *cdir, Stream *zipStream) = 0;
virtual Stream *createWriteStream(const CentralDir *cdir, Stream *zipStream) = 0;
// Run time lookup methods
static Compressor *findCompressor(const char *name);
static Compressor *findCompressor(S32 method);
};
#define ImplementCompressor(name, method) \
class Compressor##name : public Compressor \
{ \
public: \
Compressor##name(S32 m, const char *n) : Compressor(m, n) {} \
virtual Stream *createReadStream(const CentralDir *cdir, Stream *zipStream); \
virtual Stream *createWriteStream(const CentralDir *cdir, Stream *zipStream); \
}; \
Compressor##name gCompressor##name##instance(method, #name);
#define CompressorCreateReadStream(name) \
Stream * Compressor##name::createReadStream(const CentralDir *cdir, Stream *zipStream)
#define CompressorCreateWriteStream(name) \
Stream * Compressor##name::createWriteStream(const CentralDir *cdir, Stream *zipStream)
// @}
} // end namespace Zip
#endif // _COMPRESSOR_H_

View file

@ -0,0 +1,50 @@
//-----------------------------------------------------------------------------
// 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/zip/compressor.h"
#include "core/util/zip/zipSubStream.h"
namespace Zip
{
ImplementCompressor(Deflate, Deflated);
CompressorCreateReadStream(Deflate)
{
ZipSubRStream *stream = new ZipSubRStream;
stream->attachStream(zipStream);
stream->setUncompressedSize(cdir->mUncompressedSize);
return stream;
}
CompressorCreateWriteStream(Deflate)
{
ZipSubWStream *stream = new ZipSubWStream;
stream->attachStream(zipStream);
return stream;
}
} // end namespace Zip

View file

@ -0,0 +1,47 @@
//-----------------------------------------------------------------------------
// 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/zip/compressor.h"
#include "core/resizeStream.h"
namespace Zip
{
ImplementCompressor(Stored, Stored);
CompressorCreateReadStream(Stored)
{
ResizeFilterStream *resStream = new ResizeFilterStream;
resStream->attachStream(zipStream);
resStream->setStreamOffset(zipStream->getPosition(), cdir->mCompressedSize);
return resStream;
}
CompressorCreateWriteStream(Stored)
{
return zipStream;
}
} // end namespace Zip

View file

@ -0,0 +1,84 @@
//-----------------------------------------------------------------------------
// 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 _CRCTAB_H_
#define _CRCTAB_H_
static U32 crc_32_tab[256] = {
0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
0x2d02ef8dL
};
#define ZC_CRC32(c, b) (crc_32_tab[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8))
#endif // _CRCTAB_H_

View file

@ -0,0 +1,59 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/util/zip/extraField.h"
namespace Zip
{
static ExtraField *gExtraFieldInitList = NULL;
//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
ExtraField::ExtraField(U16 id, ExtraFieldCreateFn fnCreate)
{
mID = id;
mCreateFn = fnCreate;
mNext = gExtraFieldInitList;
gExtraFieldInitList = this;
}
//-----------------------------------------------------------------------------
// Static Methods
//-----------------------------------------------------------------------------
ExtraField * ExtraField::create(U16 id)
{
for(ExtraField *walk = gExtraFieldInitList;walk;walk = walk->mNext)
{
if(walk->getID() == id)
return walk->mCreateFn();
}
return NULL;
}
} // end namespace Zip

View file

@ -0,0 +1,84 @@
//-----------------------------------------------------------------------------
// 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 _EXTRAFIELD_H_
#define _EXTRAFIELD_H_
class Stream;
namespace Zip
{
/// @addtogroup zipint_group
/// @ingroup zip_group
// @{
// Forward Refs
class ExtraField;
// Creation Helpers
typedef ExtraField *(*ExtraFieldCreateFn)();
template<class T> ExtraField * createExtraField()
{
return new T;
}
// ExtraField base class
class ExtraField
{
ExtraField *mNext;
protected:
U16 mID;
ExtraFieldCreateFn mCreateFn;
public:
ExtraField()
{
mID = 0;
mCreateFn = NULL;
}
ExtraField(U16 id, ExtraFieldCreateFn fnCreate);
virtual ~ExtraField() {}
inline U16 getID() { return mID; }
virtual bool read(Stream *stream) = 0;
// Run time creation methods
static ExtraField *create(U16 id);
};
#define DeclareExtraField(name) \
name(U16 id, ExtraFieldCreateFn fnCreate) : Parent(id, fnCreate) {}
#define ImplementExtraField(name, id) \
name gExtraField##name##instance(id, &createExtraField<name>);
// @}
} // end namespace Zip
#endif // _EXTRAFIELD_H_

View file

@ -0,0 +1,191 @@
//-----------------------------------------------------------------------------
// 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 "core/stream/stream.h"
#include "core/util/zip/fileHeader.h"
#include "core/util/zip/compressor.h"
#include "core/util/safeDelete.h"
#include "core/resizeStream.h"
#include "core/strings/stringFunctions.h"
#include "core/frameAllocator.h"
namespace Zip
{
//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
FileHeader::FileHeader()
{
mHeaderSig = mFileHeaderSignature;
mExtractVer = 20;
mFlags = 0;
mCompressMethod = Stored;
mModTime = 0;
mModDate = 0;
mCRC32 = 0;
mCompressedSize = 0;
mUncompressedSize = 0;
mFilename = "";
}
FileHeader::~FileHeader()
{
for(S32 i = 0;i < mExtraFields.size();i++)
{
SAFE_DELETE(mExtraFields[i]);
}
}
//-----------------------------------------------------------------------------
// Protected Methods
//-----------------------------------------------------------------------------
bool FileHeader::readExtraFields(Stream *stream, U16 efLen)
{
bool ret = true;
U32 pos = stream->getPosition();
U32 end = pos + efLen;
while(stream->getPosition() < end)
{
U16 fieldSig, fieldSize;
ret = false;
ret |= stream->read(&fieldSig);
ret |= stream->read(&fieldSize);
if(! ret)
break;
pos = stream->getPosition();
ExtraField *ef = ExtraField::create(fieldSig);
if(ef)
{
ret |= ef->read(stream);
if(! ret)
delete ef;
else
mExtraFields.push_back(ef);
}
stream->setPosition(pos + fieldSize);
}
return ret;
}
//-----------------------------------------------------------------------------
// Public Methods
//-----------------------------------------------------------------------------
bool FileHeader::read(Stream *stream)
{
stream->read(&mHeaderSig);
if(mHeaderSig != mFileHeaderSignature)
return false;
stream->read(&mExtractVer);
stream->read(&mFlags);
stream->read(&mCompressMethod);
stream->read(&mModTime);
stream->read(&mModDate);
stream->read(&mCRC32);
stream->read(&mCompressedSize);
stream->read(&mUncompressedSize);
U16 fnLen, efLen;
stream->read(&fnLen);
stream->read(&efLen);
char *fn = new char[fnLen + 1];
stream->read(fnLen, fn);
fn[fnLen] = 0;
mFilename = fn;
SAFE_DELETE_ARRAY(fn);
return readExtraFields(stream, efLen);
}
bool FileHeader::write(Stream *stream)
{
mHeaderSig = mFileHeaderSignature;
stream->write(mHeaderSig);
stream->write(mExtractVer);
stream->write(mFlags);
stream->write(mCompressMethod);
stream->write(mModTime);
stream->write(mModDate);
stream->write(mCRC32);
stream->write(mCompressedSize);
stream->write(mUncompressedSize);
U16 fnLen = mFilename.length(),
efLen = 0;
stream->write(fnLen);
stream->write(efLen);
if(fnLen)
stream->write(fnLen, mFilename);
// FIXME [tom, 1/23/2007] Write extra fields here
return true;
}
//-----------------------------------------------------------------------------
ExtraField *FileHeader::findExtraField(U16 id)
{
for(S32 i = 0;i < mExtraFields.size();++i)
{
if(mExtraFields[i]->getID() == id)
return mExtraFields[i];
}
return NULL;
}
//-----------------------------------------------------------------------------
void FileHeader::setFilename(String filename)
{
mFilename = filename;
}
} // end namespace Zip

View file

@ -0,0 +1,112 @@
//-----------------------------------------------------------------------------
// 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/zip/extraField.h"
#include "core/util/tVector.h"
#ifndef _FILEHEADER_H_
#define _FILEHEADER_H_
// Forward Refs
class Stream;
namespace Zip
{
/// @addtogroup zipint_group
/// @ingroup zip_group
// @{
enum FileFlags
{
Encrypted = BIT(0),
// For implode compression
Implode8KDictionary = BIT(1),
Implode3ShannonFanoTrees = BIT(2),
// For deflate compression
DeflateTypeMask = BIT(1) | BIT(2),
FileInfoInDirectory = BIT(3),
// Note that much of the following flag bits are unsupported for various reasons
ReservedEnhDeflate = BIT(4),
PatchData = BIT(5),
StrongEncryption = BIT(6),
UnusedReserved1 = BIT(7),
UnusedReserved2 = BIT(8),
UnusedReserved3 = BIT(9),
UnusedReserved4 = BIT(10),
UnusedReserved5 = BIT(11),
ReservedPKWARE1 = BIT(12),
EncryptedDirectory = BIT(13),
ReservedPKWARE2 = BIT(14),
ReservedPKWARE3 = BIT(15),
};
class FileHeader
{
static const U32 mFileHeaderSignature = 0x04034b50;
protected:
bool readExtraFields(Stream *stream, U16 efLen);
public:
U32 mHeaderSig;
U16 mExtractVer;
U16 mFlags;
U16 mCompressMethod;
U16 mModTime;
U16 mModDate;
U32 mCRC32;
U32 mCompressedSize;
U32 mUncompressedSize;
String mFilename;
Vector<ExtraField *> mExtraFields;
FileHeader();
virtual ~FileHeader();
virtual bool read(Stream *stream);
virtual bool write(Stream *stream);
ExtraField *findExtraField(U16 id);
void setFilename(String filename);
};
// @}
} // end namespace Zip
#endif

View file

@ -0,0 +1,107 @@
//-----------------------------------------------------------------------------
// 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/stringTable.h"
#ifndef _ZIPTEST_H_
#define _ZIPTEST_H_
namespace Zip
{
//-----------------------------------------------------------------------------
//
// Things that need to be tested:
//
// Writing Zips:
// Write to file that doesn't already exist
// Write to file that we've already written to
// Write to file that already exists, but we haven't written to
// Attempt to open a file for write that's already open for write (should fail)
// ** Attempt to open a file that's open for read (should fail)
// Writing >= 5000 files to a zip
// Writing >= 5000 files to a zip in a number of folders
//
// Reading Zips:
// Open for read from file that doesn't exist (should fail)
// Read from file that already existed in the zip
// Read from file that we have written to the zip but not yet rebuilt
// ** Read from file that is already open for read (should fail)
// Read from file that is already open for write (should fail)
//
// Miscellaneous:
// Opening a file in the zip as ReadWrite (should fail)
// Enumerating files
// Deleting files
// ** Adding files
// Opening Zips with zip comments
// Opening Zips/Files with file comments
// Opening large zips that require searching for the EOCD
//
// All tests should be run on zip files that are opened for read, write and readwrite
// All tests need to be run for both forms of openArchive()
//
// All tests that require an existing zip file should be run on a zip created with
// a standard zip application in addition to zip files created by this code.
//
// Tests involving ReadWrite mode need to be done both with and without the zip
// file existing before the test.
//
// Tests marked ** are not possible to test yet as they are not supported by the
// zip code.
//
// [tom, 2/2/2007] I'm using WinZip 11 to create the baseline zips. It is probably
// worthwhile to include zips created in additional applications.
//
//-----------------------------------------------------------------------------
// Directory relative to executable directory to use as a working directory
#define ZIPTEST_WORKING_DIR "unitTests/zip"
// The filename of the zip file that we create for writing to
#define ZIPTEST_WRITE_FILENAME "zipUnitTest.zip"
// The filename of the zip file created in a standard zip application
#define ZIPTEST_BASELINE_FILENAME "zipUnitTestBaseline.zip"
// The filename of our working copy of the above so that we don't destroy the svn copy
#define ZIPTEST_WORKING_FILENAME "zipUnitTestWorking.zip"
//-----------------------------------------------------------------------------
// Helper Functions
//-----------------------------------------------------------------------------
inline StringTableEntry makeTestPath(const char *path)
{
char buffer[1024], dir[1024];
Platform::makeFullPathName(ZIPTEST_WORKING_DIR, dir, sizeof(dir), Platform::getMainDotCsDir());
Platform::makeFullPathName(path, buffer, sizeof(buffer), dir);
return StringTable->insert(buffer, true);
}
//-----------------------------------------------------------------------------
} // end namespace Zip
#endif // _ZIPTEST_H_

View file

@ -0,0 +1,196 @@
//-----------------------------------------------------------------------------
// 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/strings/stringFunctions.h"
#include "core/util/zip/zipArchive.h"
#include "core/util/zip/unitTests/zipTest.h"
#include "unit/test.h"
#include "unit/memoryTester.h"
using namespace UnitTesting;
using namespace Zip;
CreateUnitTest(ZipTestMisc, "Zip/Misc")
{
private:
StringTableEntry mWriteFilename;
StringTableEntry mBaselineFilename;
StringTableEntry mWorkingFilename;
public:
ZipTestMisc()
{
mWriteFilename = makeTestPath(ZIPTEST_WRITE_FILENAME);
mBaselineFilename = makeTestPath(ZIPTEST_BASELINE_FILENAME);
mWorkingFilename = makeTestPath(ZIPTEST_WORKING_FILENAME);
}
void run()
{
MemoryTester m;
m.mark();
// Clean up from a previous run
cleanup();
test(makeTestZip(), "Failed to create test zip");
miscTest(mWorkingFilename);
// [tom, 2/7/2007] extractFile() uses the resource manager so it will
// create a resource object that will be detected as a "leak" since it
// won't be freed until much later.
test(m.check(), "Zip misc test leaked memory! (Unless it says it's from the Resource Manager above, see comments in core/zip/unitTests/zipTestMisc.cc)");
}
private:
//-----------------------------------------------------------------------------
void cleanup()
{
if(Platform::isFile(mWriteFilename))
dFileDelete(mWriteFilename);
if(Platform::isFile(mWorkingFilename))
dFileDelete(mWorkingFilename);
}
bool makeTestZip()
{
FileStream source, dest;
if(! source.open(mBaselineFilename, Torque::FS::File::Read))
{
fail("Failed to open baseline zip for read");
return false;
}
// [tom, 2/7/2007] FileStream's d'tor calls close() so we don't really have to do it here
if(! dest.open(mWorkingFilename, Torque::FS::File::Write))
{
fail("Failed to open working zip for write");
return false;
}
if(! dest.copyFrom(&source))
{
fail("Failed to copy baseline zip");
return false;
}
dest.close();
source.close();
return true;
}
//-----------------------------------------------------------------------------
bool miscTest(const char *zipfile)
{
ZipArchive *zip = new ZipArchive;
bool ret = true;
if(! zip->openArchive(zipfile, ZipArchive::ReadWrite))
{
delete zip;
fail("Unable to open zip file");
return false;
}
// Opening a file in the zip as ReadWrite (should fail)
Stream *stream;
if((stream = zip->openFile("readWriteTest.txt", ZipArchive::ReadWrite)) != NULL)
{
zip->closeFile(stream);
fail("File opened successfully as read/write");
ret = false;
}
// Enumerating, Extracting and Deleting files
U32 curSize = zip->mDiskStream->getStreamSize();
if(zip->numEntries() > 0)
{
// Find the biggest file in the zip
const CentralDir *del = NULL;
for(S32 i = 0;i < zip->numEntries();++i)
{
const CentralDir &cd = (*zip)[i];
if(del == NULL || (del && cd.mUncompressedSize > del->mUncompressedSize))
del = &cd;
}
if(del)
{
// Extract the file
const char *ptr = dStrrchr(del->mFilename, '/');
if(ptr)
++ptr;
else
ptr = del->mFilename;
StringTableEntry fn = makeTestPath(ptr);
ret = test(zip->extractFile(del->mFilename, fn), "Failed to extract file.");
// Then delete it
ret = test(zip->deleteFile(del->mFilename), "ZipArchive::deleteFile() failed?!");
// Close and reopen the zip to force it to rebuild
zip->closeArchive();
if(! zip->openArchive(zipfile, ZipArchive::ReadWrite))
{
delete zip;
fail("Unable to re-open zip file?!");
return false;
}
}
else
{
fail("Couldn't find a file to delete?!");
ret = false;
}
}
else
{
fail("Baseline zip has no files.");
ret = false;
}
U32 newSize = zip->mDiskStream->getStreamSize();
test(newSize != curSize, "Zip file size didn't change when deleting a file ?!");
zip->closeArchive();
delete zip;
return ret;
}
};

View file

@ -0,0 +1,252 @@
//-----------------------------------------------------------------------------
// 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 "unit/test.h"
#include "unit/memoryTester.h"
#include "core/util/zip/zipArchive.h"
#include "core/util/zip/unitTests/zipTest.h"
#include "core/stringTable.h"
#include "core/crc.h"
using namespace UnitTesting;
using namespace Zip;
CreateUnitTest(ZipTestRead, "Zip/Read")
{
private:
StringTableEntry mWriteFilename;
StringTableEntry mBaselineFilename;
StringTableEntry mWorkingFilename;
public:
ZipTestRead()
{
mWriteFilename = makeTestPath(ZIPTEST_WRITE_FILENAME);
mBaselineFilename = makeTestPath(ZIPTEST_BASELINE_FILENAME);
mWorkingFilename = makeTestPath(ZIPTEST_WORKING_FILENAME);
}
void run()
{
MemoryTester m;
m.mark();
// Clean up from a previous run
cleanup();
// Read mode tests with the baseline zip
readTest(mBaselineFilename);
// Read mode tests with a zip created by us
test(makeTestZip(), "Failed to make test zip");
readTest(mWorkingFilename);
// Do read/write mode tests
readWriteTest(mWriteFilename);
test(m.check(), "Zip read test leaked memory!");
}
private:
//-----------------------------------------------------------------------------
void cleanup()
{
if(Platform::isFile(mWriteFilename))
dFileDelete(mWriteFilename);
if(Platform::isFile(mWorkingFilename))
dFileDelete(mWorkingFilename);
}
//-----------------------------------------------------------------------------
bool writeFile(ZipArchive *zip, const char *filename)
{
Stream * stream = zip->openFile(filename, ZipArchive::Write);
if(stream != NULL)
{
stream->writeLine((U8 *)"This is a test file for reading from a zip created with the zip code.\r\nIt is pointless by itself, but needs to be long enough that it gets compressed.\r\n");
zip->closeFile(stream);
return true;
}
return false;
}
bool makeTestZip()
{
ZipArchive *zip = new ZipArchive;
if(!zip->openArchive(mWorkingFilename, ZipArchive::Write))
{
delete zip;
fail("Unable to open zip file");
return false;
}
test(writeFile(zip, "test.txt"), "Failed to write file into test zip");
test(writeFile(zip, "console.log"), "Failed to write file into test zip");
test(writeFile(zip, "folder/OpenAL32.dll"), "Failed to write file into test zip");
zip->closeArchive();
delete zip;
return true;
}
//-----------------------------------------------------------------------------
bool testReadFile(ZipArchive *zip, const char *filename)
{
// [tom, 2/5/2007] Try using openFile() first for fairest real-world test
Stream *stream;
if((stream = zip->openFile(filename, ZipArchive::Read)) == NULL)
return false;
const CentralDir *cd = zip->findFileInfo(filename);
if(cd == NULL)
{
// [tom, 2/5/2007] This shouldn't happen
zip->closeFile(stream);
fail("testReadFile - File opened successfully, but couldn't find central directory. This is bad.");
return false;
}
U32 crc = CRC::INITIAL_CRC_VALUE;
bool ret = true;
U8 buffer[1024];
U32 numBytes = stream->getStreamSize() - stream->getPosition();
while((stream->getStatus() != Stream::EOS) && numBytes > 0)
{
U32 numRead = numBytes > sizeof(buffer) ? sizeof(buffer) : numBytes;
if(! stream->read(numRead, buffer))
{
ret = false;
fail("testReadFile - Couldn't read from stream");
break;
}
crc = CRC::calculateCRC(buffer, numRead, crc);
numBytes -= numRead;
}
if(ret)
{
// CRC Check
crc ^= CRC::CRC_POSTCOND_VALUE;
if(crc != cd->mCRC32)
{
fail("testReadFile - File failed CRC check");
ret = false;
}
}
zip->closeFile(stream);
return ret;
}
//-----------------------------------------------------------------------------
bool readTest(const char *zipfile)
{
ZipArchive *zip = new ZipArchive;
if(!zip->openArchive(zipfile, ZipArchive::Read))
{
delete zip;
fail("Unable to open zip file");
return false;
}
// Try reading files that exist in various formats
testReadFile(zip, "test.txt");
testReadFile(zip, "console.log");
testReadFile(zip, "folder/OpenAL32.dll");
// Try reading a file that doesn't exist
if(testReadFile(zip, "hopefullyDoesntExist.bla.bla.foo"))
fail("Opening a file the didn't exist succeeded");
zip->closeArchive();
delete zip;
return true;
}
bool readWriteTest(const char *zipfile)
{
ZipArchive *zip = new ZipArchive;
bool ret = true;
if(!zip->openArchive(zipfile, ZipArchive::ReadWrite))
{
delete zip;
fail("Unable to open zip file");
return false;
}
// Test writing a file and then reading it back before the zip is rebuilt
test(writeFile(zip, "test.txt"), "Failed to write file to test zip");
test(testReadFile(zip, "test.txt"), "Failed to read file back from test zip");
// Read from file that is already open for write (should fail)
Stream *wrStream = zip->openFile("writeTest.txt", ZipArchive::Write);
if(wrStream != NULL)
{
wrStream->writeLine((U8 *)"Test text to make a valid file");
// This next open should fail
Stream * rdStream = zip->openFile("writeTest.txt", ZipArchive::Read);
if(rdStream != NULL)
{
fail("Succeeded in opening a file for read that's already open for write");
ret = false;
zip->closeFile(rdStream);
}
zip->closeFile(wrStream);
}
else
{
fail("Could not open file for write");
ret = false;
}
zip->closeArchive();
delete zip;
return ret;
}
};

View file

@ -0,0 +1,244 @@
//-----------------------------------------------------------------------------
// 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/util/zip/zipArchive.h"
#include "core/util/zip/unitTests/zipTest.h"
#include "unit/test.h"
#include "unit/memoryTester.h"
using namespace UnitTesting;
using namespace Zip;
CreateUnitTest(ZipTestWrite, "Zip/Write")
{
private:
StringTableEntry mWriteFilename;
StringTableEntry mBaselineFilename;
StringTableEntry mWorkingFilename;
public:
/// Number of files to write for testing large numbers of files in a zip
static const U32 mTons = 5000;
ZipTestWrite()
{
mWriteFilename = makeTestPath(ZIPTEST_WRITE_FILENAME);
mBaselineFilename = makeTestPath(ZIPTEST_BASELINE_FILENAME);
mWorkingFilename = makeTestPath(ZIPTEST_WORKING_FILENAME);
}
void run()
{
MemoryTester m;
m.mark();
// Clean up from a previous run
cleanup();
// Test writing to zip files without the zip file existing
testWriting(mWriteFilename, ZipArchive::Read);
testWriting(mWriteFilename, ZipArchive::ReadWrite);
// Cleanup to try write without existing file
cleanup();
testWriting(mWriteFilename, ZipArchive::Write);
// Now use previous file to test everything again with an existing file
testWriting(mWriteFilename, ZipArchive::ReadWrite);
testWriting(mWriteFilename, ZipArchive::Write);
testWriting(mWriteFilename, ZipArchive::Read);
testWritingTons(makeTestPath("WriteTons.zip"));
test(m.check(), "Zip write test leaked memory!");
}
private:
//-----------------------------------------------------------------------------
void cleanup()
{
if(Platform::isFile(mWriteFilename))
dFileDelete(mWriteFilename);
if(Platform::isFile(mWorkingFilename))
dFileDelete(mWorkingFilename);
}
//-----------------------------------------------------------------------------
bool writeFile(ZipArchive *zip, const char *filename, char *fileBuf, U32 bufSize, bool mustNotExist = false, const char *contents = NULL)
{
if(mustNotExist && fileBuf && bufSize > 0)
{
// Find a unique filename
U32 count = 1;
dStrcpy(fileBuf, filename);
while(zip->findFileInfo(fileBuf))
{
dSprintf(fileBuf, bufSize, "%s.%04x", filename, count++);
if(count == 0)
{
fail("writeFile - got stuck in an infinite loop looking for a unique filename");
return false;
}
}
}
else if(fileBuf && bufSize > 0)
dStrcpy(fileBuf, filename);
// Try and write to the file
Stream * stream = zip->openFile(fileBuf ? fileBuf : filename, ZipArchive::Write);
if(stream != NULL)
{
stream->writeLine(contents ? (U8 *)contents : (U8 *)"This is a test of writing to a file.");
zip->closeFile(stream);
return true;
}
return false;
}
//-----------------------------------------------------------------------------
bool testWriting(const char *zipfile, ZipArchive::AccessMode mode)
{
ZipArchive *zip = new ZipArchive;
if(! zip->openArchive(zipfile, mode))
{
delete zip;
// This is only an error if we're not trying to open as read
if(mode != ZipArchive::Read)
fail("Unable to open zip file");
return mode == ZipArchive::Read;
}
char fileBuf[1024];
// Write to file that doesn't already exist
if(!writeFile(zip, "testWriteNew.txt", fileBuf, sizeof(fileBuf), true))
{
fail("Couldn't write to a file that didn't already exist");
goto bail;
}
// Write to file that we've already written to
if(!writeFile(zip, fileBuf, NULL, 0, false, "This is a test of overwriting the file."))
{
if(mode != ZipArchive::Read)
fail("Couldn't write to a file that we've already written to");
goto bail;
}
// Write to file that already exists, but we haven't written to
// To do this, we need to close and re-open the zipfile
zip->closeArchive();
delete zip;
zip = new ZipArchive;
if(! zip->openArchive(zipfile, mode))
{
delete zip;
fail("Unable to re-open zip file. Strange!");
return false;
}
// Use the file we already overwrote since we are sure of it's filename
if(!writeFile(zip, fileBuf, NULL, 0, false, "This is a test of overwriting the file again."))
{
if(mode != ZipArchive::Read)
fail("Couldn't write to a file that already existed");
goto bail;
}
{
// Move this into its own scope to deal with goto 'crosses initialization' errors
// Attempt to open a file for write that's already open for write (should fail)
Stream * stream1 = zip->openFile("writeLockTest.txt", ZipArchive::Write);
if(stream1 != NULL)
{
stream1->writeLine((U8 *)"Test text to make a valid file");
// This next open should fail
Stream * stream2 = zip->openFile("writeLockTest.txt", ZipArchive::Write);
if(stream2 != NULL)
{
if(mode != ZipArchive::Read)
fail("Opening a file for write multiple times should have failed");
zip->closeFile(stream2);
}
zip->closeFile(stream1);
}
}
bail:
zip->closeArchive();
delete zip;
return true;
}
//-----------------------------------------------------------------------------
bool testWritingTons(const char *zipfile)
{
ZipArchive zip;
if(! zip.openArchive(zipfile, ZipArchive::Write))
{
fail("Unable to open zip file");
return false;
}
bool ret = true;
for(U32 i = 0;i < mTons;++i)
{
char fname[256];
dSprintf(fname, sizeof(fname), "file%04x.txt", i);
if(! writeFile(&zip, fname, NULL, 0))
{
fail("Failed to write file");
fail(fname);
ret = false;
break;
}
}
zip.closeArchive();
return ret;
}
};

View file

@ -0,0 +1,926 @@
//-----------------------------------------------------------------------------
// 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/zip/zipArchive.h"
#include "core/stream/stream.h"
#include "core/stream/fileStream.h"
#include "core/filterStream.h"
#include "core/util/zip/zipCryptStream.h"
#include "core/crc.h"
//#include "core/resManager.h"
#include "console/console.h"
#include "core/util/zip/compressor.h"
#include "core/util/zip/zipTempStream.h"
#include "core/util/zip/zipStatFilter.h"
#ifdef TORQUE_ZIP_AES
#include "core/zipAESCryptStream.h"
#include "core/zip/crypto/aes.h"
#endif
#include "core/util/safeDelete.h"
#include "app/version.h"
namespace Zip
{
//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
ZipArchive::ZipArchive() :
mStream(NULL),
mDiskStream(NULL),
mMode(Read),
mRoot(NULL),
mFilename(NULL)
{
}
ZipArchive::~ZipArchive()
{
closeArchive();
}
//-----------------------------------------------------------------------------
// Protected Methods
//-----------------------------------------------------------------------------
bool ZipArchive::readCentralDirectory()
{
mEntries.clear();
SAFE_DELETE(mRoot);
mRoot = new ZipEntry;
mRoot->mName = "";
mRoot->mIsDirectory = true;
mRoot->mCD.setFilename("");
if(! mEOCD.findInStream(mStream))
return false;
if(! mEOCD.read(mStream))
return false;
if(mEOCD.mDiskNum != mEOCD.mStartCDDiskNum ||
mEOCD.mNumEntriesInThisCD != mEOCD.mTotalEntriesInCD)
{
if(isVerbose())
Con::errorf("ZipArchive::readCentralDirectory - %s: Zips that span multiple disks are not supported.", mFilename ? mFilename : "<no filename>");
return false;
}
if(! mStream->setPosition(mEOCD.mCDOffset))
return false;
for(S32 i = 0;i < mEOCD.mNumEntriesInThisCD;++i)
{
ZipEntry *ze = new ZipEntry;
if(! ze->mCD.read(mStream))
{
delete ze;
if(isVerbose())
Con::errorf("ZipArchive::readCentralDirectory - %s: Error reading central directory.", mFilename ? mFilename : "<no filename>");
return false;
}
insertEntry(ze);
}
return true;
}
void ZipArchive::dumpCentralDirectory(ZipEntry* entry, String* indent)
{
// if entry is null, use root
if (entry == NULL)
entry = mRoot;
if (!entry)
return;
String emptyIndent;
if (indent == NULL)
indent = &emptyIndent;
Con::printf("%s%s%s", indent->c_str(), entry->mIsDirectory ? "/" : "", entry->mName.c_str());
for (Map<String,ZipEntry*>::Iterator iter = entry->mChildren.begin();
iter != entry->mChildren.end();
++iter)
{
String newIdent = *indent + " ";
dumpCentralDirectory((*iter).value, &(newIdent));
}
}
//-----------------------------------------------------------------------------
void ZipArchive::insertEntry(ZipEntry *ze)
{
char path[1024];
dStrncpy(path, ze->mCD.mFilename.c_str(), sizeof(path));
path[sizeof(path) - 1] = 0;
for(S32 i = 0;i < dStrlen(path);++i)
{
if(path[i] == '\\')
path[i] = '/';
}
ZipEntry *root = mRoot;
char *ptr = path, *slash = NULL;
do
{
slash = dStrchr(ptr, '/');
if(slash)
{
// Add the directory
*slash = 0;
// try to get root, create if not found
ZipEntry *newEntry = NULL;
if (!root->mChildren.tryGetValue(ptr, newEntry))
{
newEntry = new ZipEntry;
newEntry->mParent = root;
newEntry->mName = String(ptr);
newEntry->mIsDirectory = true;
newEntry->mCD.setFilename(path);
root->mChildren[ptr] = newEntry;
}
root = newEntry;
*slash = '/';
ptr = slash + 1;
}
else
{
// Add the file.
if(*ptr)
{
ze->mIsDirectory = false;
ze->mName = ptr;
ze->mParent = root;
root->mChildren[ptr] = ze;
mEntries.push_back(ze);
}
else
{
// [tom, 2/6/2007] If ptr is empty, this was a directory entry. Since
// we created a new entry for it above, we need to delete the old
// pointer otherwise it will leak as it won't have got inserted.
delete ze;
}
}
} while(slash);
}
void ZipArchive::removeEntry(ZipEntry *ze)
{
if(ze == mRoot)
{
// [tom, 2/1/2007] We don't want to remove the root as it should always
// be removed through closeArchive()
AssertFatal(0, "ZipArchive::removeEntry - Attempting to remove the root");
return;
}
// Can't iterate the hash table, so we can't do this safely
AssertFatal(!ze->mIsDirectory, "ZipArchive::removeEntry - Cannot remove a directory");
// See if we have a temporary file for this entry
Vector<ZipTempStream *>::iterator i;
for(i = mTempFiles.begin();i != mTempFiles.end();++i)
{
if((*i)->getCentralDir() == &ze->mCD)
{
SAFE_DELETE(*i);
mTempFiles.erase(i);
break;
}
}
// Remove from the tree
Vector<ZipEntry *>::iterator j;
for(j = mEntries.begin();j != mEntries.end();++j)
{
if(*j == ze)
{
mEntries.erase(j);
break;
}
}
// [tom, 2/2/2007] This must be last, as ze is no longer valid once it's
// removed from the parent.
ZipEntry *z = ze->mParent->mChildren[ze->mName];
ze->mParent->mChildren.erase(ze->mName);
delete z;
}
//-----------------------------------------------------------------------------
CentralDir *ZipArchive::findFileInfo(const char *filename)
{
ZipEntry *ze = findZipEntry(filename);
return ze ? &ze->mCD : NULL;
}
ZipArchive::ZipEntry *ZipArchive::findZipEntry(const char *filename)
{
char path[1024];
dStrncpy(path, filename, sizeof(path));
path[sizeof(path) - 1] = 0;
for(S32 i = 0;i < dStrlen(path);++i)
{
if(path[i] == '\\')
path[i] = '/';
}
ZipEntry *root = mRoot;
char *ptr = path, *slash = NULL;
do
{
slash = dStrchr(ptr, '/');
if(slash)
{
// Find the directory
*slash = 0;
// check child dict for new root
ZipEntry *newRoot = NULL;
if (!root->mChildren.tryGetValue(ptr, newRoot))
return NULL;
root = newRoot;
ptr = slash + 1;
}
else
{
// Find the file
ZipEntry* entry = NULL;
if (root->mChildren.tryGetValue(ptr, entry))
return entry;
}
} while(slash);
return NULL;
}
//-----------------------------------------------------------------------------
Stream *ZipArchive::createNewFile(const char *filename, Compressor *method)
{
ZipEntry *ze = new ZipEntry;
ze->mIsDirectory = false;
ze->mCD.setFilename(filename);
insertEntry(ze);
ZipTempStream *stream = new ZipTempStream(&ze->mCD);
if(stream->open())
{
Stream *retStream = method->createWriteStream(&ze->mCD, stream);
if(retStream == NULL)
{
delete stream;
return NULL;
}
ZipStatFilter *filter = new ZipStatFilter(&ze->mCD);
if(! filter->attachStream(retStream))
{
delete filter;
delete retStream;
delete stream;
return NULL;
}
ze->mCD.mCompressMethod = method->getMethod();
ze->mCD.mInternalFlags |= CDFileOpen;
return filter;
}
return NULL;
}
void ZipArchive::updateFile(ZipTempStream *stream)
{
CentralDir *cd = stream->getCentralDir();
// [tom, 1/23/2007] Uncompressed size and CRC32 are updated by ZipStatFilter
cd->mCompressedSize = stream->getStreamSize();
cd->mInternalFlags |= CDFileDirty;
cd->mInternalFlags &= ~CDFileOpen;
// Upper byte should be zero, lower is version as major * 10 + minor
cd->mVersionMadeBy = (getVersionNumber() / 100) & 0xff;
cd->mExtractVer = 20;
U32 dosTime = currentTimeToDOSTime();
cd->mModTime = dosTime & 0x0000ffff;
cd->mModDate = (dosTime & 0xffff0000) >> 16;
mTempFiles.push_back(stream);
}
//-----------------------------------------------------------------------------
U32 ZipArchive::localTimeToDOSTime(const Torque::Time::DateTime &dt)
{
// DOS time format
// http://msdn.microsoft.com/en-us/library/ms724274(VS.85).aspx
return TimeToDOSTime(Torque::Time(dt));
}
U32 ZipArchive::TimeToDOSTime(const Torque::Time& t)
{
S32 year,month,day,hour,minute,second,microsecond;
t.get(&year, &month, &day, &hour, &minute, &second, &microsecond);
if(year > 1980) // De Do Do Do, De Da Da Da
year -= 1980;
return (((day) + (32 * (month)) + (512 * year)) << 16) | ((second/2) + (32* minute) + (2048 * (U32)hour));
}
Torque::Time ZipArchive::DOSTimeToTime(U16 time, U16 date)
{
Torque::Time::DateTime dt;
dt.microsecond = 0;
dt.hour = (time & 0xF800) >> 11;
dt.minute = (time & 0x07E0) >> 5;
dt.second = (time & 0x001F)*2;
dt.year = ((date & 0xFE00) >> 9) + 1980;
dt.month = (date & 0x01E0) >> 5;
dt.day = (date & 0x001F);
return Torque::Time(dt);
}
Torque::Time ZipArchive::DOSTimeToTime(U32 dosTime)
{
U16 time = dosTime & 0x0000ffff;
U16 date = (dosTime & 0xffff0000) >> 16;
return ZipArchive::DOSTimeToTime(time, date);
}
U32 ZipArchive::currentTimeToDOSTime()
{
Torque::Time::DateTime dt;
Torque::Time::getCurrentDateTime(dt);
return localTimeToDOSTime(dt);
}
//-----------------------------------------------------------------------------
// [tom, 1/24/2007] The general idea here is we want to create a new file,
// copy any data from the old zip file and add the new stuff. Once the new
// zip is created, delete the old one and rename the new one.
bool ZipArchive::rebuildZip()
{
String newZipName;
FileStream tempFile;
Stream *zipFile = mStream;
// FIXME [tom, 1/24/2007] Temporary for expediting testing
if(mFilename == NULL)
return false;
if(mMode == ReadWrite)
{
newZipName = String(mFilename) + ".new";
if(! tempFile.open(newZipName, mMode == Write ? Torque::FS::File::Write : Torque::FS::File::ReadWrite))
return false;
zipFile = &tempFile;
}
// Copy any unmodified files
for(S32 i = 0;i < mEntries.size();++i)
{
ZipEntry *entry = mEntries[i];
// Directories are internal only for lookup purposes
if(entry->mIsDirectory || (entry->mCD.mInternalFlags & (CDFileDirty | CDFileDeleted)))
continue;
copyFileToNewZip(&entry->mCD, zipFile);
}
// Copy any dirty files
for(S32 i = 0;i < mTempFiles.size();++i)
{
ZipTempStream *zts = mTempFiles[i];
writeDirtyFileToNewZip(zts, zipFile);
zts->close();
delete zts;
mTempFiles[i] = NULL;
}
mTempFiles.clear();
// Write central directory
mEOCD.mCDOffset = zipFile->getPosition();
mEOCD.mNumEntriesInThisCD = 0;
for(S32 i = 0;i < mEntries.size();++i)
{
ZipEntry *entry = mEntries[i];
// [tom, 1/24/2007] Directories are internal only for lookup purposes
if(entry->mIsDirectory || (entry->mCD.mInternalFlags & CDFileDeleted) != 0)
continue;
++mEOCD.mNumEntriesInThisCD;
if(! entry->mCD.write(zipFile))
break;
}
mEOCD.mCDSize = zipFile->getPosition() - mEOCD.mCDOffset;
mEOCD.mTotalEntriesInCD = mEOCD.mNumEntriesInThisCD;
mEOCD.mDiskNum = 0;
mEOCD.mStartCDDiskNum = 0;
mEOCD.write(zipFile);
if(mMode == ReadWrite)
{
// Close file, replace old zip with it
tempFile.close();
// [tom, 2/1/2007] The disk stream must be closed else we can't rename
// the file. Since rebuildZip() is only called from closeArchive() this
// should be safe.
if(mDiskStream)
{
mDiskStream->close();
delete mDiskStream;
mDiskStream = NULL;
}
String oldRename;
oldRename = String(mFilename) + ".old";
if(! Torque::FS::Rename(mFilename, oldRename))
return false;
if(! Torque::FS::Rename(newZipName, mFilename))
return false;
Torque::FS::Remove(oldRename);
}
return true;
}
bool ZipArchive::writeDirtyFileToNewZip(ZipTempStream *fileStream, Stream *zipStream)
{
CentralDir *cdir = fileStream->getCentralDir();
FileHeader fh(*cdir);
fh.setFilename(cdir->mFilename);
cdir->mLocalHeadOffset = zipStream->getPosition();
// Write header and file
if(! fh.write(zipStream))
return false;
if(! fileStream->rewind())
return false;
return zipStream->copyFrom(fileStream);
}
bool ZipArchive::copyFileToNewZip(CentralDir *cdir, Stream *newZipStream)
{
// [tom, 1/24/2007] Using the stored compressor allows us to copy the raw
// data regardless of compression method without having to re-compress it.
Compressor *comp = Compressor::findCompressor(Stored);
if(comp == NULL)
return false;
if(! mStream->setPosition(cdir->mLocalHeadOffset))
return false;
// Copy file header
// FIXME [tom, 1/24/2007] This will currently not copy the extra fields
FileHeader fh;
if(! fh.read(mStream))
return false;
cdir->mLocalHeadOffset = newZipStream->getPosition();
if(! fh.write(newZipStream))
return false;
// Copy file data
Stream *readS = comp->createReadStream(cdir, mStream);
if(readS == NULL)
return false;
bool ret = newZipStream->copyFrom(readS);
// [tom, 1/24/2007] closeFile() just frees the relevant filters and
// thus it is safe to call from here.
closeFile(readS);
return ret;
}
//-----------------------------------------------------------------------------
// Public Methods
//-----------------------------------------------------------------------------
void ZipArchive::setFilename(const char *filename)
{
SAFE_FREE(mFilename);
if(filename)
mFilename = dStrdup(filename);
}
//-----------------------------------------------------------------------------
bool ZipArchive::openArchive(const char *filename, AccessMode mode /* = Read */)
{
if(mode != Read && mode != Write && mode != ReadWrite)
return false;
closeArchive();
mDiskStream = new FileStream;
if(mDiskStream->open(filename, (Torque::FS::File::AccessMode)mode))
{
setFilename(filename);
if(openArchive(mDiskStream, mode))
return true;
}
// Cleanup just in case openArchive() failed
closeArchive();
return false;
}
bool ZipArchive::openArchive(Stream *stream, AccessMode mode /* = Read */)
{
if(mode != Read && mode != Write && mode != ReadWrite)
return false;
mStream = stream;
mMode = mode;
if(mode == Read || mode == ReadWrite)
{
bool ret = readCentralDirectory();
if(mode == Read)
return ret;
return true;
}
else
{
mEntries.clear();
SAFE_DELETE(mRoot);
mRoot = new ZipEntry;
mRoot->mName = "";
mRoot->mIsDirectory = true;
mRoot->mCD.setFilename("");
}
return true;
}
void ZipArchive::closeArchive()
{
if(mMode == Write || mMode == ReadWrite)
rebuildZip();
// Free any remaining temporary files
for(S32 i = 0;i < mTempFiles.size();++i)
{
SAFE_DELETE(mTempFiles[i]);
}
mTempFiles.clear();
// Close the zip file stream and clean up
if(mDiskStream)
{
mDiskStream->close();
delete mDiskStream;
mDiskStream = NULL;
}
mStream = NULL;
SAFE_FREE(mFilename);
SAFE_DELETE(mRoot);
mEntries.clear();
}
//-----------------------------------------------------------------------------
Stream * ZipArchive::openFile(const char *filename, AccessMode mode /* = Read */)
{
ZipEntry* ze = findZipEntry(filename);
return openFile(filename, ze, mode);
}
Stream * ZipArchive::openFile(const char *filename, ZipEntry* ze, AccessMode mode /* = Read */)
{
if(mode == Read)
{
if(ze == NULL)
return NULL;
return openFileForRead(&ze->mCD);
}
if(mode == Write)
{
if(ze)
{
if(ze->mCD.mInternalFlags & CDFileOpen)
{
if(isVerbose())
Con::errorf("ZipArchive::openFile - File %s is already open", filename);
return NULL;
}
// Remove the old entry so we can create a new one
removeEntry(ze);
ze = NULL;
}
return createNewFile(filename, Compressor::findCompressor(Deflated));
}
if(isVerbose())
Con::errorf("ZipArchive::openFile - Files within zips can only be opened as read or write, but not both at the same time.");
return NULL;
}
void ZipArchive::closeFile(Stream *stream)
{
FilterStream *currentStream, *nextStream;
nextStream = dynamic_cast<FilterStream*>(stream);
while (nextStream)
{
currentStream = nextStream;
stream = currentStream->getStream();
currentStream->detachStream();
nextStream = dynamic_cast<FilterStream*>(stream);
delete currentStream;
}
ZipTempStream *tempStream = dynamic_cast<ZipTempStream *>(stream);
if(tempStream && (tempStream->getCentralDir()->mInternalFlags & CDFileOpen))
{
// [tom, 1/23/2007] This is a temporary file we are writing to
// so we need to update the relevant information in the header.
updateFile(tempStream);
}
}
//-----------------------------------------------------------------------------
Stream *ZipArchive::openFileForRead(const CentralDir *fileCD)
{
if(mMode != Read && mMode != ReadWrite)
return NULL;
if((fileCD->mInternalFlags & (CDFileDeleted | CDFileOpen)) != 0)
return NULL;
Stream *stream = mStream;
if(fileCD->mInternalFlags & CDFileDirty)
{
// File is dirty, we need to read from the temporary file
for(S32 i = 0;i < mTempFiles.size();++i)
{
if(mTempFiles[i]->getCentralDir() == fileCD)
{
// Found the temporary file
if(! mTempFiles[i]->rewind())
{
if(isVerbose())
Con::errorf("ZipArchive::openFile - %s: %s is dirty, but could not rewind temporary file?", mFilename ? mFilename : "<no filename>", fileCD->mFilename.c_str());
return NULL;
}
stream = mTempFiles[i];
break;
}
}
if(stream == mStream)
{
if(isVerbose())
Con::errorf("ZipArchive::openFile - %s: %s is dirty, but no temporary file found?", mFilename ? mFilename : "<no filename>", fileCD->mFilename.c_str());
return NULL;
}
}
else
{
// Read from the zip file directly
if(! mStream->setPosition(fileCD->mLocalHeadOffset))
{
if(isVerbose())
Con::errorf("ZipArchive::openFile - %s: Could not locate local header for file %s", mFilename ? mFilename : "<no filename>", fileCD->mFilename.c_str());
return NULL;
}
FileHeader fh;
if(! fh.read(mStream))
{
if(isVerbose())
Con::errorf("ZipArchive::openFile - %s: Could not read local header for file %s", mFilename ? mFilename : "<no filename>", fileCD->mFilename.c_str());
return NULL;
}
}
Stream *attachTo = stream;
U16 compMethod = fileCD->mCompressMethod;
if(fileCD->mFlags & Encrypted)
{
if(fileCD->mCompressMethod == AESEncrypted)
{
// [tom, 1/19/2007] Whilst AES support does exist, I'm not including it in TGB
// to avoid having to deal with crypto export legal issues.
Con::errorf("ZipArchive::openFile - %s: File %s is AES encrypted, but AES is not supported in this version.", mFilename ? mFilename : "<no filename>", fileCD->mFilename.c_str());
}
else
{
ZipCryptRStream *cryptStream = new ZipCryptRStream;
cryptStream->setPassword(DEFAULT_ZIP_PASSWORD);
cryptStream->setFileEndPos(stream->getPosition() + fileCD->mCompressedSize);
if(! cryptStream->attachStream(stream))
{
delete cryptStream;
return NULL;
}
attachTo = cryptStream;
}
}
Compressor *comp = Compressor::findCompressor(compMethod);
if(comp == NULL)
{
if(isVerbose())
Con::errorf("ZipArchive::openFile - %s: Unsupported compression method (%d) for file %s", mFilename ? mFilename : "<no filename>", fileCD->mCompressMethod, fileCD->mFilename.c_str());
return NULL;
}
return comp->createReadStream(fileCD, attachTo);
}
//-----------------------------------------------------------------------------
bool ZipArchive::addFile(const char *filename, const char *pathInZip, bool replace /* = true */)
{
FileStream f;
if (!f.open(filename, Torque::FS::File::Read))
return false;
const CentralDir *cd = findFileInfo(pathInZip);
if(! replace && cd && (cd->mInternalFlags & CDFileDeleted) == 0)
return false;
Stream *dest = openFile(pathInZip, Write);
if(dest == NULL)
{
f.close();
return false;
}
bool ret = dest->copyFrom(&f);
closeFile(dest);
f.close();
return ret;
}
bool ZipArchive::extractFile(const char *pathInZip, const char *filename, bool *crcFail /* = NULL */)
{
if(crcFail)
*crcFail = false;
const CentralDir *realCD = findFileInfo(pathInZip);
if(realCD == NULL)
return false;
FileStream dest;
if(! dest.open(filename, Torque::FS::File::Write))
return false;
Stream *source = openFile(pathInZip, Read);
if(source == NULL)
{
dest.close();
return false;
}
// [tom, 2/7/2007] CRC checking the lazy man's way
// ZipStatFilter only fails if it doesn't have a central directory, so this is safe
CentralDir fakeCD;
ZipStatFilter zsf(&fakeCD);
zsf.attachStream(source);
bool ret = dest.copyFrom(&zsf);
zsf.detachStream();
if(ret && fakeCD.mCRC32 != realCD->mCRC32)
{
if(crcFail)
*crcFail = true;
if(isVerbose())
Con::errorf("ZipArchive::extractFile - CRC failure extracting file %s", pathInZip);
ret = false;
}
closeFile(source);
dest.close();
return ret;
}
bool ZipArchive::deleteFile(const char *filename)
{
if(mMode != Write && mMode != ReadWrite)
return false;
CentralDir *cd = findFileInfo(filename);
if(cd == NULL)
return false;
cd->mInternalFlags |= CDFileDeleted;
// CodeReview [tom, 2/9/2007] If this is a file we have a temporary file for,
// we should probably delete it here rather then waiting til the archive is closed.
return true;
}
//-----------------------------------------------------------------------------
bool ZipArchive::isVerbose()
{
return Con::getBoolVariable("$pref::Zip::Verbose");
}
void ZipArchive::setVerbose(bool verbose)
{
Con::setBoolVariable("$pref::Zip::Verbose", verbose);
}
} // end namespace Zip

View file

@ -0,0 +1,590 @@
//-----------------------------------------------------------------------------
// 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/zip/fileHeader.h"
#include "core/util/zip/centralDir.h"
#include "core/util/zip/compressor.h"
#include "core/stream/fileStream.h"
#include "core/util/tVector.h"
#include "core/util/tDictionary.h"
#include "core/util/timeClass.h"
#ifndef _ZIPARCHIVE_H_
#define _ZIPARCHIVE_H_
/// @addtogroup zip_group Zip Code
// @{
/// Password to use when opening encrypted zip files. Change this to whatever the password is for your zips.
#define DEFAULT_ZIP_PASSWORD "changeme"
class ZipTestWrite;
class ZipTestRead;
class ZipTestMisc;
namespace Zip
{
/// @addtogroup zip_group Zip Code
// @{
// Forward Refs
class ZipTempStream;
// [tom, 10/18/2006] This will be split up into a separate interface for allowing
// the resource manager to handle any kind of archive relatively easily.
// [tom, 2/9/2007] At least, it was designed so that it could be. It may not
// actually happen for a very long time, if ever ;-)
//-----------------------------------------------------------------------------
/// @brief Class for accessing Zip files
///
/// ZipArchive provides two interfaces for reading or writing zip files.
/// The first is a stream based interface that should be familiar to anyone
/// who's ever written code, and the other is an archiver interface that
/// should be familiar to anyone who's ever used a standard Zip application.
///
/// The two interfaces are not mutually exclusive so you can use both if
/// you wish. For example, you may want to use the stream interface to add
/// a configuration file to a zip without having to create a temporary file
/// and then add a number of existing files using the addFile() method.
///
/// Both interfaces have their advantages and disadvantages, which will
/// be discussed below.
///
/// <h3>Accessing a Zip file</h3>
///
/// Before you can access any files in the zip, you first need to open the
/// archive. This is the same regardless of which interface you use, and there
/// are two ways to accomplish it.
///
/// <b>Opening from a file on the file system</b>
///
/// The simplest method of opening a zip file is to use openArchive(const char *, AccessMode)
/// to open a file that is on the disk.
///
/// When opening a zip file on the file system, the filename is automatically set.
///
/// <b>Opening a file from a stream</b>
///
/// A more advanced way to open the zip file is from an arbitrary stream. The
/// only requirements are that the stream supports seeking and was opened with
/// the correct access mode. Use the openArchive(Stream *, AccessMode) method to
/// do this.
///
/// Opening zip files from arbitrary streams is a very powerful feature and
/// opens many interesting doors. For example, combined with some small changes
/// to the resource manager and startup code, it was possible to implement a
/// VFS that allows the entire game to run from a single executable with no
/// external files.
///
/// Note that the filename is not automatically set when you open the zip file
/// from a stream. The filename is used in error reporting and by the resource
/// manager, so you may wish to set it to something meaningful.
///
/// Regardless of which method you use to open the file, the #AccessMode controls
/// what you can do with it. If you open the archive as #ReadWrite, you can both
/// write to and read from files in the zip. However, it is not possible to open
/// files in the zip as #ReadWrite.
///
/// <b>Closing the zip file</b>
///
/// When you are done with the zip file, call closeArchive() to free any resources
/// and rebuild the zip file if it was open for #Write.
///
/// <b>Example</b>
///
/// @code
/// Zip::ZipArchive za;
/// if(za.openArchive("filename.zip", ZipArchive::Read))
/// {
/// // ... do stuff ...
/// za.closeArchive();
/// }
/// @endcode
///
/// <h3>Archiver Interface</h3>
///
/// The archiver style interface allows you to add, extract and delete files in
/// the zip in a way similar to that of an standard archiver application.
///
/// While the archiver interface is simple to use, it is blocking and thus
/// difficult to use asynchronously. If you require zip file support and
/// responsive UI then you should consider using the stream interface instead.
///
/// See the following method documentation for more information:
///
/// <ul>
/// <li> addFile()
/// <li> extractFile()
/// <li> deleteFile()
/// </ul>
///
/// <b>Example</b>
///
/// @code
/// Zip::ZipArchive za;
/// if(za.openArchive("filename.zip", ZipArchive::ReadWrite))
/// {
/// // Extract a file
/// za.extractFile("test.txt", "test.txt");
/// // Add a file
/// za.addFile("test.txt", "test.txt");
/// // Delete a file
/// za.deleteFile("test.txt");
///
/// za.closeArchive();
/// }
/// @endcode
///
/// <h3>Stream Interface</h3>
///
/// The stream based interface allows you to access files within the zip
/// in a similar way to accessing the file system through the ResourceManager.
///
/// There are a few small caveats to the stream interface:
/// <ul>
/// <li> When writing files, the whole file must be written sequentially. You
/// cannot seek in the stream.
/// <li> It may or may not be possible to seek in streams opened for read.
/// Files that were not compressed in the zip file support seeking with
/// no penalty. In all cases where the file is compressed, if seeking is
/// supported by the decompression and/or decryption filter then it
/// carries with it an extreme performance penalty and should be avoided.
/// All currently available decompression filters (Deflate and BZip2) and
/// decryption filters (Zip 2.0 and AES) support seeking, but have to reset
/// their state and re-decompress/decrypt the entire file up to the point
/// you are seeking to. An extreme example would be that if you had a
/// 20MB file and were currently at the end of the file, seeking back 1 byte
/// of the file would cause the entire file to be decompressed again. This
/// would be a blocking operation that would lock Torque up for an appreciable
/// chunk of time.
/// <li> Files can only be open as #Read or #Write, but not #ReadWrite
/// <li> Only one file can be open for read at a time, but multiple files can
/// be open for write at a time. - [tom, 2/9/2007] Check this
/// </ul>
///
/// See the following method documentation for more information:
///
/// <ul>
/// <li> openFile()
/// <li> closeFile()
/// </ul>
///
/// <b>CRC Checking</b>
///
/// Unlike the archiver interface, there is no automatic CRC checking when
/// reading from files using the stream interface. If you will only be
/// reading files sequentially, see the documentation for ZipStatFilter
/// for a useful trick to get easy CRC checking.
///
/// <b>Example</b>
///
/// @code
/// Zip::ZipArchive za;
/// if(za.openArchive("filename.zip", ZipArchive::Write))
/// {
/// // Write to the file
/// Stream *stream;
/// if(stream = za.openFile("test.txt", ZipArchive::Write))
/// {
/// stream->writeLine((U8 *)"Hello, Zipped World!");
/// za.closeFile(stream);
/// }
///
/// za.closeArchive();
/// }
/// @endcode
///
/// <h3>Compressed Files</h3>
///
/// The zip code included with stock Torque supports "stored" (uncompressed) files
/// and deflate compressed files. The code is easily extensible to support any
/// compression format that the Zip file format supports.
///
/// In addition to the deflate and stored formats, BZip2 is supported but not
/// included with stock Torque. BZip2 support will be released as a resource in
/// the future.
///
/// <h3>Encrypted Files</h3>
///
/// Preliminary support for Encrypted/Passworded files is included in TGB Pro only.
/// Currently, only Zip 2.0 encryption is supported by the stock code. AES support
/// exists and may be released as a resource in the future.
///
/// To set the password used for zips, you need to modify the #DEFAULT_ZIP_PASSWORD
/// define in core/zip/zipArchive.h. This password will be used for all zips that
/// require a password. The default password is changeme. This may be used by
/// TGB Binary users to test encrypted zips with their game. Shipping with the
/// default password is not recommended for obvious reasons.
///
/// The intended use of encrypted zips is for preventing casual copying of your
/// game's assets. Zip 2.0 encryption has known weaknesses that allow an attacker
/// to decrypt the contents of the zip. AES encryption is significantly more secure,
/// but as the password must be stored in the executable it will not stop a
/// determined attacker.
///
/// A script accessible mechanism for setting the password does not currently exist.
/// To use encrypted mod zips, if the password was in script then the password
/// would be clearly visible to anyone that cared to poke around in your scripts.
///
/// Encrypted zip support will be improved in a future version. For now, a more
/// secure method of storing the password is left as an exercise for the reader.
///
/// <h3>Accessing Zip files from script</h3>
///
/// ZipArchive is a C++ class and thus cannot be used from script. However,
/// a wrapper is provided to allow script access to zips. See the documentation
/// on ZipObject for more information.
///
/// <h3>More Examples</h3>
///
/// More in depth example code than that featured here can be found in the
/// unit tests for the zip code (in the core/zip/unitTests directory)
/// and the script code for the packaging utility.
///
//-----------------------------------------------------------------------------
class ZipArchive : public StrongRefBase
{
friend class ::ZipTestWrite;
friend class ::ZipTestRead;
friend class ::ZipTestMisc;
public:
/// Access modes for zip files and files within the zip
enum AccessMode
{
Read = Torque::FS::File::Read, //!< Open a zip or file in a zip for reading
Write = Torque::FS::File::Write, //!< Open a zip or file in a zip for writing
ReadWrite = Torque::FS::File::ReadWrite //!< Open a zip file for reading and writing. <b>Note</b>: Not valid for files in zips.
};
struct ZipEntry
{
ZipEntry *mParent;
String mName;
bool mIsDirectory;
CentralDir mCD;
Map<String,ZipEntry*> mChildren;
ZipEntry()
{
mName = "";
mIsDirectory = false;
mParent = NULL;
}
};
protected:
Stream *mStream;
FileStream *mDiskStream;
AccessMode mMode;
EndOfCentralDir mEOCD;
// mRoot forms a tree of entries for fast queries given a file path
// mEntries allows easy iteration of the entire file list
ZipEntry *mRoot;
Vector<ZipEntry *> mEntries;
const char *mFilename;
Vector<ZipTempStream *> mTempFiles;
bool readCentralDirectory();
void insertEntry(ZipEntry *ze);
void removeEntry(ZipEntry *ze);
Stream *createNewFile(const char *filename, Compressor *method);
Stream *createNewFile(const char *filename, const char *method)
{
return createNewFile(filename, Compressor::findCompressor(method));
}
Stream *createNewFile(const char *filename, S32 method)
{
return createNewFile(filename, Compressor::findCompressor(method));
}
void updateFile(ZipTempStream *stream);
bool rebuildZip();
bool copyFileToNewZip(CentralDir *cdir, Stream *newZipStream);
bool writeDirtyFileToNewZip(ZipTempStream *fileStream, Stream *zipStream);
public:
ZipEntry* getRoot() { return mRoot; }
ZipEntry* findZipEntry(const char *filename);
static U32 localTimeToDOSTime(const Torque::Time::DateTime &dt);
static Torque::Time DOSTimeToTime(U16 time, U16 date);
static Torque::Time DOSTimeToTime(U32 datetime);
static U32 TimeToDOSTime(const Torque::Time& t);
static U32 currentTimeToDOSTime();
void dumpCentralDirectory(ZipEntry* entry = NULL, String* indent = NULL);
public:
ZipArchive();
virtual ~ZipArchive();
/// @name Miscellaneous Methods
// @{
//-----------------------------------------------------------------------------
/// @brief Set the filename of the zip file.
///
/// The zip filename is used by the resource manager and for error reporting.
///
/// <b>Note</b>: The filename is set automatically when you open the file.
///
/// @param filename Filename of the zip file
//-----------------------------------------------------------------------------
void setFilename(const char *filename);
/// Set the disk stream pointer. The ZipArchive is then responsible for
/// deleting the stream when appropriate and the caller should not do the same.
/// This function should only be called after openArchive(Stream*) has been
/// successfully executed.
void setDiskStream(FileStream* stream) { mDiskStream = stream; }
//-----------------------------------------------------------------------------
/// @brief Get the filename of the zip file.
///
/// @returns Filename of the zip file, or NULL if none set
//-----------------------------------------------------------------------------
const char *getFilename() { return mFilename; }
//-----------------------------------------------------------------------------
/// @brief Determine if the Zip code is in verbose mode
///
/// Verbose mode causes the Zip code to provide diagnostic error messages
/// when things go wrong. It can be enabled or disabled through script by
/// setting the $pref::Zip::Verbose variable to true to enable it or false
/// to disable it.
///
/// Verbose mode is mostly useful when tracking down issues with opening
/// a zip file without having to resort to using a debugger.
///
/// @returns The value of $pref::Zip::Verbose
/// @see ZipArchive::setVerbose()
//-----------------------------------------------------------------------------
bool isVerbose();
//-----------------------------------------------------------------------------
/// @brief Turn verbose mode on or off.
///
/// This sets the $pref::Zip::Verbose variable.
///
/// See isVerbose() for a discussion on verbose mode.
///
/// @param verbose True to enable verbose mode, false to disable
/// @see ZipArchive::isVerbose()
//-----------------------------------------------------------------------------
void setVerbose(bool verbose);
// @}
/// @name Archive Access Methods
// @{
//-----------------------------------------------------------------------------
/// @brief Open a zip archive from a file
///
/// The archive must be closed with closeArchive() when you are done with it.
///
/// @param filename Filename of zip file to open
/// @param mode Access mode. May be Read, Write or ReadWrite
/// @return true for success, false for failure
/// @see ZipArchive::openArchive(Stream *, AccessMode), ZipArchive::closeArchive()
//-----------------------------------------------------------------------------
virtual bool openArchive(const char *filename, AccessMode mode = Read);
//-----------------------------------------------------------------------------
/// @brief Open a zip archive from a stream
///
/// The stream must support seeking and must support the specified access
/// mode. For example, if the stream is opened for Read you cannot specify
/// Write to openArchive(). However, if the stream is open for ReadWrite
/// then you can specify any one of Read, Write or ReadWrite as the mode
/// argument to openArchive().
///
/// The archive must be closed with closeArchive() when you are done with it.
///
/// @param stream Pointer to stream to open the zip archive from
/// @param mode Access mode. May be Read, Write or ReadWrite
/// @return true for success, false for failure
/// @see ZipArchive::openArchive(const char *, AccessMode), ZipArchive::closeArchive()
//-----------------------------------------------------------------------------
// CodeReview [tom, 2/9/2007] I just thought, if we open a stream directly
// for write then rebuilding the zip file probably won't work. This needs to
// be checked and either fixed or worked around.
virtual bool openArchive(Stream *stream, AccessMode mode = Read);
//-----------------------------------------------------------------------------
/// @brief Close the zip archive and free any resources
///
/// @see ZipArchive::openArchive(Stream *, AccessMode), ZipArchive::openArchive(const char *, AccessMode)
//-----------------------------------------------------------------------------
virtual void closeArchive();
// @}
/// @name Stream Based File Access Methods
// @{
//-----------------------------------------------------------------------------
/// @brief Open a file within the zip file
///
/// The access mode can only be Read or Write. It is not possible to open
/// a file within the zip as ReadWrite.
///
/// The returned stream must be freed with closeFile(). Do not delete it
/// directly.
///
/// In verbose mode, openFile() will display additional error information
/// in the console when it fails.
///
/// @param filename Filename of the file in the zip
/// @param mode Access mode. May be Read or Write
/// @param ze Output zip entry. May be unspecified.
/// @return Pointer to stream or NULL for failure
/// @see ZipArchive::closeFile(), ZipArchive::isVerbose()
//-----------------------------------------------------------------------------
virtual Stream *openFile(const char *filename, AccessMode mode = Read);
virtual Stream *openFile(const char *filename, ZipEntry* ze, AccessMode = Read);
//-----------------------------------------------------------------------------
/// @brief Close a file opened through openFile()
///
/// @param stream Stream to close
/// @see ZipArchive::openFile(const char *, AccessMode)
//-----------------------------------------------------------------------------
virtual void closeFile(Stream *stream);
//-----------------------------------------------------------------------------
/// @brief Open a file within the zip file for read
///
/// This method exists mainly for the integration with the resource manager.
/// Unless there is good reason to use this method, it is better to use the
/// openFile() method instead.
///
/// @param fileCD Pointer to central directory of the file to open
/// @return Pointer to stream or NULL for failure
/// @see ZipArchive::openFile(const char *, AccessMode), ZipArchive::closeFile()
//-----------------------------------------------------------------------------
Stream *openFileForRead(const CentralDir *fileCD);
// @}
/// @name Archiver Style File Access Methods
// @{
//-----------------------------------------------------------------------------
/// @brief Add a file to the zip
///
/// If replace is false and the file already exists in the zip, this function
/// will fail and return false. If replace is true, the existing file will be
/// overwritten.
///
/// @param filename Filename on the local file system to add
/// @param pathInZip The path and filename in the zip file to give this file
/// @param replace true to replace existing files, false otherwise
/// @return true for success, false for failure
/// @see ZipArchive::extractFile(), ZipArchive::deleteFile(), ZipArchive::isVerbose()
//-----------------------------------------------------------------------------
bool addFile(const char *filename, const char *pathInZip, bool replace = true);
//-----------------------------------------------------------------------------
/// @brief Extract a file from the zip
///
/// The file will be created through the resource manager and so must be
/// in a location that is writable.
///
/// The file will be CRC checked during extraction and extractFile() will
/// return false if the CRC check failed. The CRC check is just an advisory,
/// the output file will still exist if the CRC check failed. It is up to
/// the caller to decide what to do in the event of a CRC failure.
///
/// You can optionally pass a pointer to a bool to determine if a CRC check
/// failed. If extractFile() returns false and crcFail is false then the failure
/// was not CRC related. If crcFail is true and extractFile() returns false,
/// then the CRC check failed but the file otherwise extracted OK. You can
/// take your chances as to whether the data is valid or not, if you wish.
///
/// In verbose mode, extractFile() will display an error in the console when
/// a file fails the CRC check.
///
/// @param pathInZip The path and filename in the zip file to extract
/// @param filename Filename on the local file system to extract to
/// @param crcFail Pointer to a boolean that receives the result of the CRC check
/// @return true for success, false for failure
/// @see ZipArchive::addFile(), ZipArchive::deleteFile(), ZipArchive::isVerbose()
//-----------------------------------------------------------------------------
bool extractFile(const char *pathInZip, const char *filename, bool *crcFail = NULL);
//-----------------------------------------------------------------------------
/// @brief Delete a file from the zip
///
/// Flags a file as deleted so it is removed when the zip file is rebuilt.
///
/// @param filename Filename in the zip to delete
/// @return true for success, false for failure
/// @see ZipArchive::addFile(), ZipArchive::extractFile(), ZipArchive::isVerbose()
//-----------------------------------------------------------------------------
bool deleteFile(const char *filename);
// @}
/// @name Enumeration Methods
// @{
//-----------------------------------------------------------------------------
/// @brief Get number of entries in the central directory
///
/// @see ZipArchive::findFileInfo(const char *)
//-----------------------------------------------------------------------------
U32 numEntries() const { return mEntries.size(); }
//-----------------------------------------------------------------------------
/// Get a central directory entry
//-----------------------------------------------------------------------------
const CentralDir & operator[](const U32 idx) const { return mEntries[idx]->mCD; }
//-----------------------------------------------------------------------------
/// @brief Find a file in the zip
///
/// @param filename Path and filename to find
/// @return Pointer to the central directory entry
//-----------------------------------------------------------------------------
CentralDir *findFileInfo(const char *filename);
// @}
};
// @}
} // end namespace Zip
// @}
#endif // _ZIPARCHIVE_H_

View file

@ -0,0 +1,204 @@
//-----------------------------------------------------------------------------
// 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/zip/zipCryptStream.h"
#include "core/util/zip/crctab.h"
#include "console/console.h"
//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
ZipCryptRStream::ZipCryptRStream() : mStream(NULL), mFileEndPos(0), mPassword(NULL)
{
}
ZipCryptRStream::~ZipCryptRStream()
{
}
//-----------------------------------------------------------------------------
// Private Methods
//-----------------------------------------------------------------------------
U32 ZipCryptRStream::fillBuffer(const U32 in_attemptSize, void *pBuffer)
{
AssertFatal(mStream != NULL, "No stream to fill from?");
AssertFatal(mStream->getStatus() != Stream::Closed,
"Fill from a closed stream?");
U32 currPos = mStream->getPosition();
U32 actualReadSize;
if (in_attemptSize + currPos > mFileEndPos) {
actualReadSize = mFileEndPos - currPos;
} else {
actualReadSize = in_attemptSize;
}
if (mStream->read(actualReadSize, pBuffer) == true) {
return actualReadSize;
} else {
AssertWarn(false, "Read failed while trying to fill buffer");
return 0;
}
}
//-----------------------------------------------------------------------------
// Public Methods
//-----------------------------------------------------------------------------
void ZipCryptRStream::setPassword(const char *password)
{
mKeys[0] = 305419896;
mKeys[1] = 591751049;
mKeys[2] = 878082192;
mPassword = password;
const char *pPtr = password;
while(*pPtr)
{
updateKeys(*pPtr);
pPtr++;
}
}
bool ZipCryptRStream::attachStream(Stream* io_pSlaveStream)
{
mStream = io_pSlaveStream;
mStreamStartPos = mStream->getPosition();
// [tom, 12/20/2005] Encrypted zip files have an extra 12 bytes
// of entropy before the file data.
U8 buffer[12];
if(mStream->read(sizeof(buffer), &buffer))
{
// Initialize keys
for(S32 i = 0;i < sizeof(buffer);i++)
{
updateKeys(buffer[i] ^= decryptByte());
}
// if(buffer[11] !)
mFileStartPos = mStream->getPosition();
setStatus(Ok);
return true;
}
return false;
}
void ZipCryptRStream::detachStream()
{
mStream = NULL;
// Clear keys, just in case
dMemset(&mKeys, 0, sizeof(mKeys));
setStatus(Closed);
}
U32 ZipCryptRStream::getPosition() const
{
return mStream->getPosition();
}
bool ZipCryptRStream::setPosition(const U32 in_newPosition)
{
if(in_newPosition > mFileEndPos)
return false;
U32 curPos = getPosition();
U32 readSize = in_newPosition - mFileStartPos;
bool ret = true;
if(in_newPosition < curPos)
{
// Reposition to start of stream
Stream *stream = getStream();
U32 startPos = mStreamStartPos;
const char *password = mPassword;
detachStream();
setPassword(password);
stream->setPosition(startPos);
ret = attachStream(stream);
if(in_newPosition == mFileStartPos)
return ret;
}
// Read until we reach the new position
U8 *buffer = new U8 [1024];
while(readSize >= 1024)
{
readSize -= 1024;
ret = _read(1024, buffer);
if(! ret)
break;
}
if(readSize > 0 && ret)
{
ret = _read(readSize, buffer);
}
delete [] buffer;
return ret;
}
//-----------------------------------------------------------------------------
// Protected Methods
//-----------------------------------------------------------------------------
void ZipCryptRStream::updateKeys(const U8 c)
{
mKeys[0] = ZC_CRC32(mKeys[0], c);
mKeys[1] += mKeys[0] & 0x000000ff;
mKeys[1] = mKeys[1] * 134775813 + 1;
U32 k = mKeys[1] >> 24;
mKeys[2] = ZC_CRC32(mKeys[2], k);
}
U8 ZipCryptRStream::decryptByte()
{
U16 temp;
temp = (mKeys[2] & 0xffff) | 2;
return (temp * (temp ^ 1)) >> 8;
}
bool ZipCryptRStream::_read(const U32 in_numBytes, void* out_pBuffer)
{
U32 numRead = fillBuffer(in_numBytes, out_pBuffer);
if(numRead > 0)
{
// Decrypt
U8 *pBytes = (U8 *)out_pBuffer;
for(S32 i = 0;i < numRead;i++)
{
updateKeys(pBytes[i] ^= decryptByte());
}
return true;
}
return false;
}

View file

@ -0,0 +1,68 @@
//-----------------------------------------------------------------------------
// 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 _ZIPCRYPTSTREAM_H_
#define _ZIPCRYPTSTREAM_H_
#ifndef _FILTERSTREAM_H_
#include "core/filterStream.h"
#endif
class ZipCryptRStream : public FilterStream
{
typedef FilterStream Parent;
Stream *mStream;
S32 mStreamStartPos;
S32 mFileStartPos;
S32 mFileEndPos;
U32 mKeys[3]; // mKeys and it's usage is very unclear and has a ton of magic numbers -patw
const char *mPassword;
U32 fillBuffer(const U32 in_attemptSize, void *pBuffer);
public:
ZipCryptRStream();
virtual ~ZipCryptRStream();
void setPassword(const char *password);
inline void setFileEndPos(S32 pos) { mFileEndPos = pos; }
// Overrides of FilterStream
bool attachStream(Stream* io_pSlaveStream);
void detachStream();
Stream *getStream() { return mStream; }
U32 getPosition() const;
bool setPosition(const U32 in_newPosition);
protected:
bool _read(const U32 in_numBytes, void* out_pBuffer);
void updateKeys(const U8 c);
U8 decryptByte();
};
#endif // _ZIPCRYPTSTREAM_H_

View file

@ -0,0 +1,423 @@
//-----------------------------------------------------------------------------
// 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/zip/zipObject.h"
#include "core/util/safeDelete.h"
#include "console/engineAPI.h"
//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
ZipObject::ZipObject()
{
mZipArchive = NULL;
}
ZipObject::~ZipObject()
{
closeArchive();
}
IMPLEMENT_CONOBJECT(ZipObject);
ConsoleDocClass( ZipObject,
"@brief Provides access to a zip file.\n\n"
"A ZipObject add, delete and extract files that are within a zip archive. You may also "
"read and write directly to the files within the archive by obtaining a StreamObject "
"for the file."
"@tsexample\n"
"// Open a zip archive, creating it if it doesn't exist\n"
"%archive = new ZipObject();\n"
"%archive.openArchive(\"testArchive.zip\", Write);\n\n"
"// Add a file to the archive with the given name\n"
"%archive.addFile(\"./water.png\", \"water.png\");\n\n"
"// Close the archive to save the changes\n"
"%archive.closeArchive();\n"
"@endtsexample\n\n"
"@note Behind the scenes all of the work is being done with the ZipArchive and StreamObject classes.\n"
"@see StreamObject when using methods such as openFileForRead() and openFileForWrite()\n\n"
"@ingroup FileSystem\n"
);
//-----------------------------------------------------------------------------
// Protected Methods
//-----------------------------------------------------------------------------
StreamObject *ZipObject::createStreamObject(Stream *stream)
{
for(S32 i = 0;i < mStreamPool.size();++i)
{
StreamObject *so = mStreamPool[i];
if(so == NULL)
{
// Reuse any free locations in the pool
so = new StreamObject(stream);
so->registerObject();
mStreamPool[i] = so;
return so;
}
if(so->getStream() == NULL)
{
// Existing unused stream, update it and return it
so->setStream(stream);
return so;
}
}
// No free object found, create a new one
StreamObject *so = new StreamObject(stream);
so->registerObject();
mStreamPool.push_back(so);
return so;
}
//-----------------------------------------------------------------------------
// Public Methods
//-----------------------------------------------------------------------------
bool ZipObject::openArchive(const char *filename, Zip::ZipArchive::AccessMode mode /* = Read */)
{
closeArchive();
mZipArchive = new Zip::ZipArchive;
if(mZipArchive->openArchive(filename, mode))
return true;
SAFE_DELETE(mZipArchive);
return false;
}
void ZipObject::closeArchive()
{
if(mZipArchive == NULL)
return;
for(S32 i = 0;i < mStreamPool.size();++i)
{
StreamObject *so = mStreamPool[i];
if(so && so->getStream() != NULL)
closeFile(so);
SAFE_DELETE_OBJECT(mStreamPool[i]);
}
mStreamPool.clear();
mZipArchive->closeArchive();
SAFE_DELETE(mZipArchive);
}
//-----------------------------------------------------------------------------
StreamObject * ZipObject::openFileForRead(const char *filename)
{
if(mZipArchive == NULL)
return NULL;
Stream * stream = mZipArchive->openFile(filename, Zip::ZipArchive::Read);
if(stream != NULL)
return createStreamObject(stream);
return NULL;
}
StreamObject * ZipObject::openFileForWrite(const char *filename)
{
if(mZipArchive == NULL)
return NULL;
Stream * stream = mZipArchive->openFile(filename, Zip::ZipArchive::Write);
if(stream != NULL)
return createStreamObject(stream);
return NULL;
}
void ZipObject::closeFile(StreamObject *stream)
{
if(mZipArchive == NULL)
return;
#ifdef TORQUE_DEBUG
bool found = false;
for(S32 i = 0;i < mStreamPool.size();++i)
{
StreamObject *so = mStreamPool[i];
if(so && so == stream)
{
found = true;
break;
}
}
AssertFatal(found, "ZipObject::closeFile() - Attempting to close stream not opened by this ZipObject");
#endif
mZipArchive->closeFile(stream->getStream());
stream->setStream(NULL);
}
//-----------------------------------------------------------------------------
bool ZipObject::addFile(const char *filename, const char *pathInZip, bool replace /* = true */)
{
return mZipArchive->addFile(filename, pathInZip, replace);
}
bool ZipObject::extractFile(const char *pathInZip, const char *filename)
{
return mZipArchive->extractFile(pathInZip, filename);
}
bool ZipObject::deleteFile(const char *filename)
{
return mZipArchive->deleteFile(filename);
}
//-----------------------------------------------------------------------------
S32 ZipObject::getFileEntryCount()
{
return mZipArchive ? mZipArchive->numEntries() : 0;
}
String ZipObject::getFileEntry(S32 idx)
{
if(mZipArchive == NULL)
return "";
const Zip::CentralDir &dir = (*mZipArchive)[idx];
char buffer[1024];
int chars = dSprintf(buffer, sizeof(buffer), "%s\t%d\t%d\t%d\t%08x",
dir.mFilename.c_str(), dir.mUncompressedSize, dir.mCompressedSize,
dir.mCompressMethod, dir.mCRC32);
if (chars < sizeof(buffer))
buffer[chars] = 0;
return String(buffer);
}
//-----------------------------------------------------------------------------
// Console Methods
//-----------------------------------------------------------------------------
static const struct
{
const char *strMode;
Zip::ZipArchive::AccessMode mode;
} gModeMap[]=
{
{ "read", Zip::ZipArchive::Read },
{ "write", Zip::ZipArchive::Write },
{ "readwrite", Zip::ZipArchive::ReadWrite },
{ NULL, (Zip::ZipArchive::AccessMode)0 }
};
//-----------------------------------------------------------------------------
DefineEngineMethod(ZipObject, openArchive, bool, ( const char* filename, const char* accessMode ), ( "read" ),
"@brief Open a zip archive for manipulation.\n\n"
"Once a zip archive is opened use the various ZipObject methods for "
"working with the files within the archive. Be sure to close the archive when "
"you are done with it.\n\n"
"@param filename The path and file name of the zip archive to open.\n"
"@param accessMode One of read, write or readwrite\n"
"@return True is the archive was successfully opened.\n"
"@note If you wish to make any changes to the archive, be sure to open it "
"with a write or readwrite access mode.\n"
"@see closeArchive()")
{
Zip::ZipArchive::AccessMode mode = Zip::ZipArchive::Read;
for(S32 i = 0;gModeMap[i].strMode;++i)
{
if(dStricmp(gModeMap[i].strMode, accessMode) == 0)
{
mode = gModeMap[i].mode;
break;
}
}
char buf[512];
Con::expandScriptFilename(buf, sizeof(buf), filename);
return object->openArchive(buf, mode);
}
DefineEngineMethod(ZipObject, closeArchive, void, (),,
"@brief Close an already opened zip archive.\n\n"
"@see openArchive()")
{
object->closeArchive();
}
//-----------------------------------------------------------------------------
DefineEngineMethod(ZipObject, openFileForRead, SimObject*, ( const char* filename ),,
"@brief Open a file within the zip archive for reading.\n\n"
"Be sure to close the file when you are done with it.\n"
"@param filename The path and name of the file to open within the zip archive.\n"
"@return A standard StreamObject is returned for working with the file.\n"
"@note You must first open the zip archive before working with files within it.\n"
"@see closeFile()\n"
"@see openArchive()")
{
StreamObject *stream = object->openFileForRead(filename);
return stream;
}
DefineEngineMethod(ZipObject, openFileForWrite, SimObject*, ( const char* filename ),,
"@brief Open a file within the zip archive for writing to.\n\n"
"Be sure to close the file when you are done with it.\n"
"@param filename The path and name of the file to open within the zip archive.\n"
"@return A standard StreamObject is returned for working with the file.\n"
"@note You must first open the zip archive before working with files within it.\n"
"@see closeFile()\n"
"@see openArchive()")
{
StreamObject *stream = object->openFileForWrite(filename);
return stream;
}
DefineEngineMethod(ZipObject, closeFile, void, ( SimObject* stream ),,
"@brief Close a previously opened file within the zip archive.\n\n"
"@param stream The StreamObject of a previously opened file within the zip archive.\n"
"@see openFileForRead()\n"
"@see openFileForWrite()")
{
StreamObject *so = dynamic_cast<StreamObject *>(stream);
if(so == NULL)
{
Con::errorf("ZipObject::closeFile - Invalid stream specified");
return;
}
object->closeFile(so);
}
//-----------------------------------------------------------------------------
DefineEngineMethod(ZipObject, addFile, bool, ( const char* filename, const char* pathInZip, bool replace ), ( true ),
"@brief Add a file to the zip archive\n\n"
"@param filename The path and name of the file to add to the zip archive.\n"
"@param pathInZip The path and name to be given to the file within the zip archive.\n"
"@param replace If a file already exists within the zip archive at the same location as this "
"new file, this parameter indicates if it should be replaced. By default, it will be replaced.\n"
"@return True if the file was successfully added to the zip archive.")
{
// Left this line commented out as it was useful when i had a problem
// with the zip's i was creating. [2/21/2007 justind]
// [tom, 2/21/2007] To is now a warnf() for better visual separation in the console
// Con::errorf("zipAdd: %s", argv[2]);
//Con::warnf(" To: %s", argv[3]);
return object->addFile(filename, pathInZip, replace);
}
DefineEngineMethod(ZipObject, extractFile, bool, ( const char* pathInZip, const char* filename ),,
"@brief Extact a file from the zip archive and save it to the requested location.\n\n"
"@param pathInZip The path and name of the file to be extracted within the zip archive.\n"
"@param filename The path and name to give the extracted file.\n\n"
"@return True if the file was successfully extracted.")
{
return object->extractFile(pathInZip, filename);
}
DefineEngineMethod(ZipObject, deleteFile, bool, ( const char* pathInZip ),,
"@brief Deleted the given file from the zip archive\n\n"
"@param pathInZip The path and name of the file to be deleted from the zip archive.\n"
"@return True of the file was successfully deleted.\n"
"@note Files that have been deleted from the archive will still show up with a "
"getFileEntryCount() until you close the archive. If you need to have the file "
"count up to date with only valid files within the archive, you could close and then "
"open the archive again.\n"
"@see getFileEntryCount()\n"
"@see closeArchive()\n"
"@see openArchive()")
{
return object->deleteFile(pathInZip);
}
//-----------------------------------------------------------------------------
DefineEngineMethod(ZipObject, getFileEntryCount, S32, (),,
"@brief Get the number of files within the zip archive.\n\n"
"Use getFileEntry() to retrive information on each file within the archive.\n\n"
"@return The number of files within the zip archive.\n"
"@note The returned count will include any files that have been deleted from "
"the archive using deleteFile(). To clear out all deleted files, you could "
"close and then open the archive again.\n"
"@see getFileEntry()\n"
"@see closeArchive()\n"
"@see openArchive()")
{
return object->getFileEntryCount();
}
DefineEngineMethod(ZipObject, getFileEntry, String, ( S32 index ),,
"@brief Get information on the requested file within the zip archive.\n\n"
"This methods provides five different pieces of information for the requested file:\n"
"<ul><li>filename - The path and name of the file within the zip archive</li>"
"<li>uncompressed size</li>"
"<li>compressed size</li>"
"<li>compression method</li>"
"<li>CRC32</li></ul>\n"
"Use getFileEntryCount() to obtain the total number of files within the archive.\n"
"@param index The index of the file within the zip archive. Use getFileEntryCount() to determine the number of files.\n"
"@return A tab delimited list of information on the requested file, or an empty string if the file could not be found.\n"
"@see getFileEntryCount()")
{
return object->getFileEntry(index);
}

View file

@ -0,0 +1,85 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "console/simBase.h"
#include "core/util/zip/zipArchive.h"
#include "core/util/tVector.h"
#include "core/stream/streamObject.h"
#include "core/util/str.h"
#ifndef _ZIPOBJECT_H_
#define _ZIPOBJECT_H_
/// @addtogroup zip_group
// @{
//-----------------------------------------------------------------------------
/// @brief Script wrapper for Zip::ZipArchive.
//-----------------------------------------------------------------------------
class ZipObject : public SimObject
{
typedef SimObject Parent;
protected:
Zip::ZipArchive *mZipArchive;
// StreamObjects are pooled and reused to avoid creating tons of SimObjects
Vector<StreamObject *> mStreamPool;
StreamObject *createStreamObject(Stream *stream);
public:
ZipObject();
virtual ~ZipObject();
DECLARE_CONOBJECT(ZipObject);
// Methods for accessing the archive
/// @see Zip::ZipArchive::openArchive()
bool openArchive(const char *filename, Zip::ZipArchive::AccessMode mode = Zip::ZipArchive::Read);
/// @see Zip::ZipArchive::closeArchive()
void closeArchive();
// Stream based file system style interface
/// @see Zip::ZipArchive::openFile()
StreamObject *openFileForRead(const char *filename);
/// @see Zip::ZipArchive::openFile()
StreamObject *openFileForWrite(const char *filename);
/// @see Zip::ZipArchive::closeFile()
void closeFile(StreamObject *stream);
// Alternative archiver style interface
/// @see Zip::ZipArchive::addFile()
bool addFile(const char *filename, const char *pathInZip, bool replace = true);
/// @see Zip::ZipArchive::extractFile()
bool extractFile(const char *pathInZip, const char *filename);
/// @see Zip::ZipArchive::deleteFile()
bool deleteFile(const char *filename);
// Methods for access the list of files
S32 getFileEntryCount();
String getFileEntry(S32 idx);
};
// @}
#endif // _ZIPOBJECT_H_

View file

@ -0,0 +1,173 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/filterStream.h"
#include "core/crc.h"
#include "core/util/zip/centralDir.h"
// [tom, 1/29/2007] ZipStatFilter allows us to track CRC and uncompressed size
// on the fly. This is necessary when dealing with compressed files as the
// CRC must be of the uncompressed data.
//
// The alternative would be to compress the files when updating the zip file,
// but this could potentially cause ZipArchive::closeArchive() to take a long
// time to complete. With compression done on the fly the time consuming code
// is pushed out to when writing to files, which is significantly easier to
// do asynchronously.
#ifndef _ZIPSTATFILTER_H_
#define _ZIPSTATFILTER_H_
//-----------------------------------------------------------------------------
/// \brief Namespace for the zip code.
///
/// @see Zip::ZipArchive, \ref zip_group "Zip Code Module"
//-----------------------------------------------------------------------------
namespace Zip
{
/// @addtogroup zipint_group Zip Code Internals
///
/// The zip code internals are mostly undocumented, but should be fairly
/// obvious to anyone who is familiar with the zip file format.
/// @ingroup zip_group
// @{
//-----------------------------------------------------------------------------
/// \brief Helper class for tracking CRC and uncompressed size
///
/// ZipStatFilter allows us to track CRC and uncompressed size
/// on the fly. This is necessary when dealing with compressed files as the
/// CRC must be of the uncompressed data.
///
/// ZipStatFilter is mostly intended for internal use by the zip code.
/// However, it can be useful when reading zips sequentially using the
/// stream interface to provide CRC checking.
///
/// <b>Example</b>
///
/// @code
/// // It's assumed that you would use proper error checking and that
/// // zip is a valid pointer to a ZipArchive and otherStream is a pointer
/// // to a valid stream.
/// Zip::ZipArchive *zip;
/// Stream *otherStream;
///
/// // We need the real central directory to compare the CRC32
/// Zip::CentralDir *realCD = zip->findFileInfo("file.txt");
/// Stream *stream = zip->openFile("file.txt", ZipArchive::Read);
///
/// Zip::CentralDir fakeCD;
/// Zip::ZipStatFilter zsf(&fakeCD);
///
/// zsf.attachStream(stream);
///
/// // ... read <i>entire</i> file sequentially using zsf instead of stream
/// otherStream->copyFrom(&zsf);
///
/// zsf.detachStream();
///
/// // fakeCD.mCRC32 now contains the CRC32 of the stream
/// if(fakeCD.mCRC32 != realCD->mCRC32)
/// {
/// // ... handle CRC failure ...
/// }
///
/// zip->closeFile(stream);
/// @endcode
///
/// A more complete example of this may be found in the code for the
/// ZipArchive::extractFile() method in zipArchive.cc
///
//-----------------------------------------------------------------------------
class ZipStatFilter : public FilterStream
{
typedef FilterStream Parent;
protected:
Stream *mStream;
CentralDir *mCD;
virtual bool _write(const U32 numBytes, const void *buffer)
{
if(! mStream->write(numBytes, buffer))
return false;
mCD->mUncompressedSize += numBytes;
mCD->mCRC32 = CRC::calculateCRC(buffer, numBytes, mCD->mCRC32);
return true;
}
virtual bool _read(const U32 numBytes, void *buffer)
{
if(! mStream->read(numBytes, buffer))
return false;
mCD->mUncompressedSize += numBytes;
mCD->mCRC32 = CRC::calculateCRC(buffer, numBytes, mCD->mCRC32);
return true;
}
public:
ZipStatFilter() : mStream(NULL), mCD(NULL) {}
ZipStatFilter(CentralDir *cd) : mStream(NULL), mCD(cd) {}
virtual ~ZipStatFilter()
{
detachStream();
}
virtual bool attachStream(Stream *stream)
{
if(mCD == NULL)
return false;
mStream = stream;
mCD->mUncompressedSize = 0;
mCD->mCRC32 = CRC::INITIAL_CRC_VALUE;
return true;
}
virtual void detachStream()
{
if(mStream == NULL)
return;
// Post condition the CRC
mCD->mCRC32 ^= CRC::CRC_POSTCOND_VALUE;
mStream = NULL;
}
virtual Stream *getStream() { return mStream; }
void setCentralDir(CentralDir *cd) { mCD = cd; }
CentralDir *getCentralDir() { return mCD; }
};
// @}
} // end namespace Zip
#endif // _ZIPSTATFILTER_H_

View file

@ -0,0 +1,479 @@
//-----------------------------------------------------------------------------
// 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 "zlib.h"
#include "core/util/zip/zipSubStream.h"
const U32 ZipSubRStream::csm_streamCaps = U32(Stream::StreamRead) | U32(Stream::StreamPosition);
const U32 ZipSubRStream::csm_inputBufferSize = 4096;
const U32 ZipSubWStream::csm_streamCaps = U32(Stream::StreamWrite);
const U32 ZipSubWStream::csm_bufferSize = (2048 * 1024);
//--------------------------------------------------------------------------
//--------------------------------------
//
ZipSubRStream::ZipSubRStream()
: m_pStream(NULL),
m_uncompressedSize(0),
m_currentPosition(0),
m_EOS(false),
m_pZipStream(NULL),
m_pInputBuffer(NULL),
m_originalSlavePosition(0),
m_lastBytesRead(0)
{
//
}
//--------------------------------------
ZipSubRStream::~ZipSubRStream()
{
detachStream();
}
//--------------------------------------
bool ZipSubRStream::attachStream(Stream* io_pSlaveStream)
{
AssertFatal(io_pSlaveStream != NULL, "NULL Slave stream?");
AssertFatal(m_pStream == NULL, "Already attached!");
m_pStream = io_pSlaveStream;
m_originalSlavePosition = io_pSlaveStream->getPosition();
m_uncompressedSize = 0;
m_currentPosition = 0;
m_EOS = false;
// Initialize zipStream state...
m_pZipStream = new z_stream_s;
m_pInputBuffer = new U8[csm_inputBufferSize];
m_pZipStream->zalloc = Z_NULL;
m_pZipStream->zfree = Z_NULL;
m_pZipStream->opaque = Z_NULL;
U32 buffSize = fillBuffer(csm_inputBufferSize);
m_pZipStream->next_in = m_pInputBuffer;
m_pZipStream->avail_in = buffSize;
m_pZipStream->total_in = 0;
inflateInit2(m_pZipStream, -MAX_WBITS);
setStatus(Ok);
return true;
}
//--------------------------------------
void ZipSubRStream::detachStream()
{
if (m_pZipStream != NULL)
{
// close out zip stream...
inflateEnd(m_pZipStream);
delete [] m_pInputBuffer;
m_pInputBuffer = NULL;
delete m_pZipStream;
m_pZipStream = NULL;
}
m_pStream = NULL;
m_originalSlavePosition = 0;
m_uncompressedSize = 0;
m_currentPosition = 0;
m_EOS = false;
setStatus(Closed);
}
//--------------------------------------
Stream* ZipSubRStream::getStream()
{
return m_pStream;
}
//--------------------------------------
void ZipSubRStream::setUncompressedSize(const U32 in_uncSize)
{
AssertFatal(m_pStream != NULL, "error, no stream to set unc size for");
m_uncompressedSize = in_uncSize;
}
//--------------------------------------
bool ZipSubRStream::_read(const U32 in_numBytes, void *out_pBuffer)
{
m_lastBytesRead = 0;
if (in_numBytes == 0)
return true;
AssertFatal(out_pBuffer != NULL, "NULL output buffer");
if (getStatus() == Closed) {
AssertFatal(false, "Attempted read from closed stream");
return false;
}
if (Ok != getStatus())
return false;
if (m_EOS)
{
setStatus(EOS);
return true;
};
// Ok, we need to call inflate() until the output buffer is full.
// first, set up the output portion of the z_stream
//
m_pZipStream->next_out = (Bytef*)out_pBuffer;
m_pZipStream->avail_out = in_numBytes;
m_pZipStream->total_out = 0;
while (m_pZipStream->avail_out != 0)
{
S32 retVal = Z_OK;
if(m_pZipStream->avail_in == 0)
{
// check if there is more output pending
inflate(m_pZipStream, Z_SYNC_FLUSH);
if(m_pZipStream->total_out != in_numBytes)
{
// Need to provide more input bytes for the stream to read...
U32 buffSize = fillBuffer(csm_inputBufferSize);
//AssertFatal(buffSize != 0, "Must find a more graceful way to handle this");
m_pZipStream->next_in = m_pInputBuffer;
m_pZipStream->avail_in = buffSize;
m_pZipStream->total_in = 0;
}
}
// need to get more?
if(m_pZipStream->total_out != in_numBytes)
retVal = inflate(m_pZipStream, Z_SYNC_FLUSH);
AssertFatal(retVal != Z_BUF_ERROR, "Should never run into a buffer error");
AssertFatal(retVal == Z_OK || retVal == Z_STREAM_END, "error in the stream");
m_lastBytesRead = m_pZipStream->total_out;
if (retVal == Z_STREAM_END)
{
if (m_pZipStream->avail_out != 0)
m_EOS = true;
setStatus(EOS);
m_currentPosition += m_pZipStream->total_out;
return true;
}
}
AssertFatal(m_pZipStream->total_out == in_numBytes,
"Error, didn't finish the decompression!");
// If we're here, everything went peachy...
setStatus(Ok);
m_currentPosition += m_pZipStream->total_out;
return true;
}
//--------------------------------------
bool ZipSubRStream::hasCapability(const Capability in_cap) const
{
return (csm_streamCaps & U32(in_cap)) != 0;
}
//--------------------------------------
U32 ZipSubRStream::getPosition() const
{
AssertFatal(m_pStream != NULL, "Error, not attached");
return m_currentPosition;
}
//--------------------------------------
bool ZipSubRStream::setPosition(const U32 in_newPosition)
{
AssertFatal(m_pStream != NULL, "Error, not attached");
if (in_newPosition == 0)
{
Stream* pStream = getStream();
U32 resetPosition = m_originalSlavePosition;
U32 uncompressedSize = m_uncompressedSize;
detachStream();
pStream->setPosition(resetPosition);
attachStream(pStream);
setUncompressedSize(uncompressedSize);
return true;
}
else
{
if (in_newPosition > m_uncompressedSize)
return false;
U32 newPosition = in_newPosition;
if (newPosition < m_currentPosition)
{
Stream* pStream = getStream();
U32 resetPosition = m_originalSlavePosition;
U32 uncompressedSize = m_uncompressedSize;
detachStream();
pStream->setPosition(resetPosition);
attachStream(pStream);
setUncompressedSize(uncompressedSize);
}
else
{
newPosition -= m_currentPosition;
}
bool bRet = true;
char *buffer = new char[2048];
while (newPosition >= 2048)
{
newPosition -= 2048;
if (!_read(2048,buffer))
{
bRet = false;
break;
}
};
if (bRet && newPosition > 0)
{
if (!_read(newPosition,buffer))
{
bRet = false;
};
};
delete [] buffer;
return bRet;
}
}
//--------------------------------------
U32 ZipSubRStream::getStreamSize()
{
AssertFatal(m_pStream != NULL, "No stream to size()");
AssertFatal(m_uncompressedSize != 0, "No data? Properties probably not set...");
return m_uncompressedSize;
}
//--------------------------------------
U32 ZipSubRStream::fillBuffer(const U32 in_attemptSize)
{
AssertFatal(m_pStream != NULL, "No stream to fill from?");
AssertFatal(m_pStream->getStatus() != Stream::Closed,
"Fill from a closed stream?");
U32 streamSize = m_pStream->getStreamSize();
U32 currPos = m_pStream->getPosition();
U32 actualReadSize;
if (in_attemptSize + currPos > streamSize) {
actualReadSize = streamSize - currPos;
} else {
actualReadSize = in_attemptSize;
}
if (m_pStream->read(actualReadSize, m_pInputBuffer) == true) {
return actualReadSize;
} else {
AssertWarn(false, "Read failed while trying to fill buffer");
return 0;
}
}
//--------------------------------------------------------------------------
ZipSubWStream::ZipSubWStream()
: m_pStream(NULL),
m_pZipStream(NULL),
m_currPosition(0),
m_pOutputBuffer(NULL),
m_pInputBuffer(NULL),
m_lastBytesRead(0),
m_lastBytesWritten(0)
{
//
}
//--------------------------------------
ZipSubWStream::~ZipSubWStream()
{
detachStream();
}
//--------------------------------------
bool ZipSubWStream::attachStream(Stream* io_pSlaveStream)
{
AssertFatal(io_pSlaveStream != NULL, "NULL Slave stream?");
AssertFatal(m_pStream == NULL, "Already attached!");
m_pStream = io_pSlaveStream;
m_currPosition = 0;
m_pOutputBuffer = new U8[csm_bufferSize];
m_pInputBuffer = new U8[csm_bufferSize];
// Initialize zipStream state...
m_pZipStream = new z_stream_s;
m_pZipStream->zalloc = Z_NULL;
m_pZipStream->zfree = Z_NULL;
m_pZipStream->opaque = Z_NULL;
m_pZipStream->next_in = m_pInputBuffer;
m_pZipStream->avail_in = csm_bufferSize;
m_pZipStream->total_in = 0;
m_pZipStream->next_out = m_pOutputBuffer;
m_pZipStream->avail_out = csm_bufferSize;
m_pZipStream->total_out = 0;
deflateInit2(m_pZipStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
setStatus(Ok);
return true;
}
//--------------------------------------
void ZipSubWStream::detachStream()
{
// Must finish...
if (m_pZipStream != NULL)
{
m_pZipStream->avail_in = 0;
deflate(m_pZipStream, Z_FINISH);
// write the remainder
m_pStream->write(csm_bufferSize - m_pZipStream->avail_out, m_pOutputBuffer);
// close out zip stream...
deflateEnd(m_pZipStream);
delete m_pZipStream;
m_pZipStream = NULL;
delete [] m_pInputBuffer;
delete [] m_pOutputBuffer;
m_pInputBuffer = NULL;
m_pOutputBuffer = NULL;
}
m_pStream = NULL;
m_currPosition = 0;
setStatus(Closed);
}
//--------------------------------------
Stream* ZipSubWStream::getStream()
{
return m_pStream;
}
//--------------------------------------
bool ZipSubWStream::_read(const U32, void*)
{
AssertFatal(false, "Cannot read from a ZipSubWStream");
setStatus(IllegalCall);
return false;
}
//--------------------------------------
bool ZipSubWStream::_write(const U32 numBytes, const void *pBuffer)
{
m_lastBytesWritten = 0;
if (numBytes == 0)
return true;
AssertFatal(pBuffer != NULL, "NULL input buffer");
if (getStatus() == Closed)
{
AssertFatal(false, "Attempted write to a closed stream");
return false;
}
m_pZipStream->next_in = (U8*)pBuffer;
m_pZipStream->avail_in = numBytes;
// write as many bufferSize chunks as possible
while(m_pZipStream->avail_in != 0)
{
if(m_pZipStream->avail_out == 0)
{
if(!m_pStream->write(csm_bufferSize, m_pOutputBuffer))
return(false);
m_pZipStream->next_out = m_pOutputBuffer;
m_pZipStream->avail_out = csm_bufferSize;
}
S32 retVal = deflate(m_pZipStream, Z_NO_FLUSH);
AssertFatal(retVal != Z_BUF_ERROR, "ZipSubWStream::_write: invalid buffer");
}
setStatus(Ok);
m_currPosition += m_pZipStream->total_out;
m_lastBytesWritten = m_pZipStream->total_out;
return true;
}
//--------------------------------------
bool ZipSubWStream::hasCapability(const Capability in_cap) const
{
return (csm_streamCaps & U32(in_cap)) != 0;
}
//--------------------------------------
U32 ZipSubWStream::getPosition() const
{
AssertFatal(m_pStream != NULL, "Error, not attached");
return m_currPosition;
}
//--------------------------------------
bool ZipSubWStream::setPosition(const U32 /*in_newPosition*/)
{
AssertFatal(m_pStream != NULL, "Error, not attached");
AssertFatal(false, "Not implemented!");
// Erk. How do we do this.
return false;
}
U32 ZipSubWStream::getStreamSize()
{
AssertFatal(false, "Undecided how to implement this!");
return 0;
}

View file

@ -0,0 +1,121 @@
//-----------------------------------------------------------------------------
// 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 _ZIPSUBSTREAM_H_
#define _ZIPSUBSTREAM_H_
//Includes
#ifndef _FILTERSTREAM_H_
#include "core/filterStream.h"
#endif
struct z_stream_s;
class ZipSubRStream : public FilterStream, public IStreamByteCount
{
typedef FilterStream Parent;
static const U32 csm_streamCaps;
static const U32 csm_inputBufferSize;
Stream* m_pStream;
U32 m_uncompressedSize;
U32 m_currentPosition;
bool m_EOS;
z_stream_s* m_pZipStream;
U8* m_pInputBuffer;
U32 m_originalSlavePosition;
U32 m_lastBytesRead;
U32 fillBuffer(const U32 in_attemptSize);
public:
virtual U32 getLastBytesRead() { return m_lastBytesRead; }
virtual U32 getLastBytesWritten() { return 0; }
public:
ZipSubRStream();
virtual ~ZipSubRStream();
// Overrides of NFilterStream
public:
bool attachStream(Stream* io_pSlaveStream);
void detachStream();
Stream* getStream();
void setUncompressedSize(const U32);
// Mandatory overrides. By default, these are simply passed to
// whatever is returned from getStream();
protected:
bool _read(const U32 in_numBytes, void* out_pBuffer);
public:
bool hasCapability(const Capability) const;
U32 getPosition() const;
bool setPosition(const U32 in_newPosition);
U32 getStreamSize();
};
class ZipSubWStream : public FilterStream, public IStreamByteCount
{
typedef FilterStream Parent;
static const U32 csm_streamCaps;
static const U32 csm_bufferSize;
Stream* m_pStream;
z_stream_s* m_pZipStream;
U32 m_currPosition; // Indicates number of _uncompressed_ bytes written
U8* m_pOutputBuffer;
U8* m_pInputBuffer;
U32 m_lastBytesRead;
U32 m_lastBytesWritten;
public:
virtual U32 getLastBytesRead() { return m_lastBytesRead; }
virtual U32 getLastBytesWritten() { return m_lastBytesWritten; }
public:
ZipSubWStream();
virtual ~ZipSubWStream();
// Overrides of NFilterStream
public:
bool attachStream(Stream* io_pSlaveStream);
void detachStream();
Stream* getStream();
// 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 //_ZIPSUBSTREAM_H_

View file

@ -0,0 +1,55 @@
//-----------------------------------------------------------------------------
// 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 "console/console.h"
#include "core/util/zip/zipTempStream.h"
#include "core/crc.h"
namespace Zip
{
//-----------------------------------------------------------------------------
// Public Methods
//-----------------------------------------------------------------------------
static S32 tempNum = 0;
bool ZipTempStream::open(String filename, Torque::FS::File::AccessMode mode)
{
if(filename.isEmpty())
{
filename = String("_ZipTempStream_tempfile") + String::ToString(tempNum++);
//filename = Platform::getTemporaryFileName();
mDeleteOnClose = true;
}
mFilename = filename;
if(! Parent::open(filename, mode))
return false;
mStreamCaps &= ~U32(StreamPosition);
return true;
}
} // end namespace Zip

View file

@ -0,0 +1,92 @@
//-----------------------------------------------------------------------------
// 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/zip/zipArchive.h"
#include "core/util/str.h"
#ifndef _ZIPTEMPSTREAM_H_
#define _ZIPTEMPSTREAM_H_
namespace Zip
{
/// @addtogroup zipint_group
/// @ingroup zip_group
// @{
class ZipTempStream : public FileStream
{
typedef FileStream Parent;
protected:
CentralDir *mCD;
bool mDeleteOnClose;
String mFilename;
public:
ZipTempStream() : mCD(NULL), mDeleteOnClose(false) {}
ZipTempStream(CentralDir *cd) : mCD(cd), mDeleteOnClose(false) {}
virtual ~ZipTempStream() { close(); }
void setCentralDir(CentralDir *cd) { mCD = cd; }
CentralDir *getCentralDir() { return mCD; }
void setDeleteOnClose(bool del) { mDeleteOnClose = del; }
virtual bool open(String filename, Torque::FS::File::AccessMode mode);
/// Open a temporary file in ReadWrite mode. The file will be deleted when the stream is closed.
virtual bool open()
{
return open(String(), Torque::FS::File::ReadWrite);
}
virtual void close()
{
Parent::close();
if(mDeleteOnClose)
Torque::FS::Remove(mFilename);
}
/// Disallow setPosition()
virtual bool setPosition(const U32 i_newPosition) { return false; }
/// Seek back to the start of the file.
/// This is used internally by the zip code and should never be called whilst
/// filters are attached (e.g. when reading or writing in a zip file)
bool rewind()
{
mStreamCaps |= U32(StreamPosition);
bool ret = Parent::setPosition(0);
mStreamCaps &= ~U32(StreamPosition);
return ret;
}
};
// @}
} // end namespace Zip
#endif // _ZIPTEMPSTREAM_H_

View file

@ -0,0 +1,475 @@
//-----------------------------------------------------------------------------
// 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/zip/zipVolume.h"
#include "core/util/zip/zipSubStream.h"
#include "core/util/noncopyable.h"
#include "console/console.h"
namespace Torque
{
using namespace FS;
using namespace Zip;
class ZipFileNode : public Torque::FS::File, public Noncopyable
{
typedef FileNode Parent;
//--------------------------------------------------------------------------
// ZipFileNode class (Internal)
//--------------------------------------------------------------------------
public:
ZipFileNode(StrongRefPtr<ZipArchive>& archive, String zipFilename, Stream* zipStream, ZipArchive::ZipEntry* ze)
{
mZipStream = zipStream;
mArchive = archive;
mZipFilename = zipFilename;
mByteCount = dynamic_cast<IStreamByteCount*>(mZipStream);
AssertFatal(mByteCount, "error, zip stream interface does not implement IStreamByteCount");
mZipEntry = ze;
}
virtual ~ZipFileNode()
{
close();
}
virtual Path getName() const { return mZipFilename; }
virtual Status getStatus() const
{
if (mZipStream)
{
// great, Stream Status is different from FileNode Status...
switch (mZipStream->getStatus())
{
case Stream::Ok:
return FileNode::Open;
case Stream::EOS:
return FileNode::EndOfFile;
case Stream::IOError:
return FileNode::UnknownError;
default:
return FileNode::UnknownError;
}
}
else
return FileNode::Closed;
}
virtual bool getAttributes(Attributes* attr)
{
if (!attr)
return false;
attr->flags = FileNode::File | FileNode::Compressed | FileNode::ReadOnly;
attr->name = mZipFilename;
// use the mod time for both mod and access time, since we only have mod time in the CD
attr->mtime = ZipArchive::DOSTimeToTime(mZipEntry->mCD.mModTime, mZipEntry->mCD.mModDate);
attr->atime = ZipArchive::DOSTimeToTime(mZipEntry->mCD.mModTime, mZipEntry->mCD.mModDate);
attr->size = mZipEntry->mCD.mUncompressedSize;
return true;
}
virtual U32 getPosition()
{
if (mZipStream)
return mZipStream->getPosition();
else
return 0;
}
virtual U32 setPosition(U32 pos, SeekMode mode)
{
if (!mZipStream || mode != Begin)
return 0;
else
return mZipStream->setPosition(pos);
}
virtual bool open(AccessMode mode)
{
// stream is already open so just check to make sure that they are using a valid mode
if (mode == Read)
return mZipStream != NULL;
else
{
Con::errorf("ZipFileSystem: Write access denied for file %s", mZipFilename.c_str());
return false;
}
}
virtual bool close()
{
if (mZipStream != NULL && mArchive != NULL)
{
mArchive->closeFile(mZipStream);
mZipStream = NULL;
mByteCount = NULL;
}
return true;
}
virtual U32 read(void* dst, U32 size)
{
if (mZipStream && mZipStream->read(size, dst) && mByteCount)
return mByteCount->getLastBytesRead();
else
return 0;
}
virtual U32 write(const void* src, U32 size)
{
if (mZipStream && mZipStream->write(size, src) && mByteCount)
return mByteCount->getLastBytesWritten();
else
return 0;
}
protected:
virtual U32 calculateChecksum()
{
// JMQ: implement
return 0;
};
Stream* mZipStream;
StrongRefPtr<ZipArchive> mArchive;
ZipArchive::ZipEntry* mZipEntry;
String mZipFilename;
IStreamByteCount* mByteCount;
};
//--------------------------------------------------------------------------
// ZipDirectoryNode class (Internal)
//--------------------------------------------------------------------------
class ZipDirectoryNode : public Torque::FS::Directory, public Noncopyable
{
public:
ZipDirectoryNode(StrongRefPtr<ZipArchive>& archive, const Torque::Path& path, ZipArchive::ZipEntry* ze)
{
mPath = path;
mArchive = archive;
mZipEntry = ze;
if (mZipEntry)
mChildIter = mZipEntry->mChildren.end();
}
~ZipDirectoryNode()
{
}
Torque::Path getName() const { return mPath; }
// getStatus() doesn't appear to be used for directories
Status getStatus() const
{
return FileNode::Open;
}
bool getAttributes(Attributes* attr)
{
if (!attr)
return false;
attr->flags = FileNode::Directory | FileNode::Compressed | FileNode::ReadOnly;
attr->name = mPath.getFullPath();
// use the mod time for both mod and access time, since we only have mod time in the CD
attr->mtime = ZipArchive::DOSTimeToTime(mZipEntry->mCD.mModTime, mZipEntry->mCD.mModDate);
attr->atime = ZipArchive::DOSTimeToTime(mZipEntry->mCD.mModTime, mZipEntry->mCD.mModDate);
attr->size = mZipEntry->mCD.mUncompressedSize;
return true;
}
bool open()
{
// reset iterator
if (mZipEntry)
mChildIter = mZipEntry->mChildren.begin();
return (mZipEntry != NULL && mArchive.getPointer() != NULL);
}
bool close()
{
if (mZipEntry)
mChildIter = mZipEntry->mChildren.end();
return true;
}
bool read(Attributes* attr)
{
if (!attr)
return false;
if (mChildIter == mZipEntry->mChildren.end())
return false;
ZipArchive::ZipEntry* ze = (*mChildIter).value;
attr->flags = FileNode::Compressed;
if (ze->mIsDirectory)
attr->flags |= FileNode::Directory;
else
attr->flags |= FileNode::File;
attr->name = ze->mName;
attr->mtime = ZipArchive::DOSTimeToTime(ze->mCD.mModTime, ze->mCD.mModDate);
attr->atime = ZipArchive::DOSTimeToTime(ze->mCD.mModTime, ze->mCD.mModDate);
attr->size = 0; // we don't know the size until we open a stream, so we'll have to use size 0
mChildIter++;
return true;
}
private:
U32 calculateChecksum()
{
return 0;
}
Torque::Path mPath;
Map<String,ZipArchive::ZipEntry*>::Iterator mChildIter;
StrongRefPtr<ZipArchive> mArchive;
ZipArchive::ZipEntry* mZipEntry;
};
//--------------------------------------------------------------------------
// ZipFakeRootNode class (Internal)
//--------------------------------------------------------------------------
class ZipFakeRootNode : public Torque::FS::Directory, public Noncopyable
{
public:
ZipFakeRootNode(StrongRefPtr<ZipArchive>& archive, const Torque::Path& path, const String &fakeRoot)
{
mPath = path;
mArchive = archive;
mRead = false;
mFakeRoot = fakeRoot;
}
~ZipFakeRootNode()
{
}
Torque::Path getName() const { return mPath; }
// getStatus() doesn't appear to be used for directories
Status getStatus() const
{
return FileNode::Open;
}
bool getAttributes(Attributes* attr)
{
if (!attr)
return false;
attr->flags = FileNode::Directory | FileNode::Compressed | FileNode::ReadOnly;
attr->name = mPath.getFullPath();
// use the mod time for both mod and access time, since we only have mod time in the CD
ZipArchive::ZipEntry* zipEntry = mArchive->getRoot();
attr->mtime = ZipArchive::DOSTimeToTime(zipEntry->mCD.mModTime, zipEntry->mCD.mModDate);
attr->atime = ZipArchive::DOSTimeToTime(zipEntry->mCD.mModTime, zipEntry->mCD.mModDate);
attr->size = zipEntry->mCD.mUncompressedSize;
return true;
}
bool open()
{
mRead = false;
return (mArchive.getPointer() != NULL);
}
bool close()
{
mRead = false;
return true;
}
bool read(Attributes* attr)
{
if (!attr)
return false;
if (mRead)
return false;
ZipArchive::ZipEntry* ze = mArchive->getRoot();
attr->flags = FileNode::Compressed;
if (ze->mIsDirectory)
attr->flags |= FileNode::Directory;
else
attr->flags |= FileNode::File;
attr->name = mFakeRoot;
attr->mtime = ZipArchive::DOSTimeToTime(ze->mCD.mModTime, ze->mCD.mModDate);
attr->atime = ZipArchive::DOSTimeToTime(ze->mCD.mModTime, ze->mCD.mModDate);
attr->size = 0; // we don't know the size until we open a stream, so we'll have to use size 0
mRead = true;
return true;
}
private:
U32 calculateChecksum()
{
return 0;
}
Torque::Path mPath;
Map<String,ZipArchive::ZipEntry*>::Iterator mChildIter;
StrongRefPtr<ZipArchive> mArchive;
bool mRead;
String mFakeRoot;
};
//--------------------------------------------------------------------------
// ZipFileSystem
//--------------------------------------------------------------------------
ZipFileSystem::ZipFileSystem(String& zipFilename, bool zipNameIsDir /* = false */)
{
mZipFilename = zipFilename;
mInitted = false;
mZipNameIsDir = zipNameIsDir;
if(mZipNameIsDir)
{
Path path(zipFilename);
mFakeRoot = Path::Join(path.getPath(), '/', path.getFileName());
}
// open the file now but don't read it yet, since we want construction to be lightweight
// we open the file now so that whatever filesystems are mounted right now (which may be temporary)
// can be umounted without affecting this file system.
mZipArchiveStream = new FileStream();
mZipArchiveStream->open(mZipFilename, Torque::FS::File::Read);
// As far as the mount system is concerned, ZFSes are read only write now (even though
// ZipArchive technically support read-write, we don't expose this to the mount system because we
// don't want to support that as a standard run case right now.)
mReadOnly = true;
}
ZipFileSystem::~ZipFileSystem()
{
if (mZipArchiveStream)
{
mZipArchiveStream->close();
delete mZipArchiveStream;
}
mZipArchive = NULL;
}
FileNodeRef ZipFileSystem::resolve(const Path& path)
{
if (!mInitted)
_init();
if (mZipArchive.isNull())
return NULL;
// eat leading "/"
String name = path.getFullPathWithoutRoot();
if (name.find("/") == 0)
name = name.substr(1, name.length() - 1);
if(name.isEmpty() && mZipNameIsDir)
return new ZipFakeRootNode(mZipArchive, path, mFakeRoot);
if(mZipNameIsDir)
{
// Remove the fake root from the name so things can be found
if(name.find(mFakeRoot) == 0)
name = name.substr(mFakeRoot.length());
#ifdef TORQUE_DISABLE_FIND_ROOT_WITHIN_ZIP
else
// If a zip file's name isn't the root of the path we're looking for
// then do not continue. Otherwise, we'll continue to look for the
// path's root within the zip file itself. i.e. we're looking for the
// path "scripts/test.cs". If the zip file itself isn't called scripts.zip
// then we won't look within the archive for a "scripts" directory.
return NULL;
#endif
if (name.find("/") == 0)
name = name.substr(1, name.length() - 1);
}
// first check to see if input path is a directory
// check for request of root directory
if (name.isEmpty())
{
ZipDirectoryNode* zdn = new ZipDirectoryNode(mZipArchive, path, mZipArchive->getRoot());
return zdn;
}
ZipArchive::ZipEntry* ze = mZipArchive->findZipEntry(name);
if (ze == NULL)
return NULL;
if (ze->mIsDirectory)
{
ZipDirectoryNode* zdn = new ZipDirectoryNode(mZipArchive, path, ze);
return zdn;
}
// pass in the zip entry so that openFile() doesn't need to look it up again.
Stream* stream = mZipArchive->openFile(name, ze, ZipArchive::Read);
if (stream == NULL)
return NULL;
ZipFileNode* zfn = new ZipFileNode(mZipArchive, name, stream, ze);
return zfn;
}
void ZipFileSystem::_init()
{
if (mInitted)
return;
mInitted = true;
if (!mZipArchive.isNull())
return;
if (mZipArchiveStream->getStatus() != Stream::Ok)
return;
mZipArchive = new ZipArchive();
if (!mZipArchive->openArchive(mZipArchiveStream, ZipArchive::Read))
{
Con::errorf("ZipFileSystem: failed to open zip archive %s", mZipFilename.c_str());
return;
}
// tell the archive that it owns the zipStream now
mZipArchive->setDiskStream(mZipArchiveStream);
// and null it out because we don't own it anymore
mZipArchiveStream = NULL;
// for debugging
//mZipArchive->dumpCentralDirectory();
}
};

View file

@ -0,0 +1,71 @@
//-----------------------------------------------------------------------------
// 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_ZIP_VOLUME_H_
#define _CORE_ZIP_VOLUME_H_
#include "core/volume.h"
#include "core/util/str.h"
#include "core/util/zip/zipArchive.h"
#include "core/util/autoPtr.h"
namespace Torque
{
using namespace FS;
using namespace Zip;
class ZipFileSystem: public FileSystem
{
public:
ZipFileSystem(String& zipFilename, bool zipNameIsDir = false);
virtual ~ZipFileSystem();
String getTypeStr() const { return "Zip"; }
FileNodeRef resolve(const Path& path);
// these are unsupported, ZipFileSystem is currently read only access
FileNodeRef create(const Path& path,FileNode::Mode) { return 0; }
bool remove(const Path& path) { return 0; }
bool rename(const Path& a,const Path& b) { return 0; }
// these are unsupported
Path mapTo(const Path& path) { return path; }
Path mapFrom(const Path& path) { return path; }
public:
/// Private interface for use by unit test only.
StrongRefPtr<ZipArchive> getArchive() { return mZipArchive; }
private:
void _init();
bool mInitted;
bool mZipNameIsDir;
String mZipFilename;
String mFakeRoot;
FileStream* mZipArchiveStream;
StrongRefPtr<ZipArchive> mZipArchive;
};
}
#endif