mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-04-21 12:25:30 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
2107
Engine/source/core/util/FastDelegate.h
Normal file
2107
Engine/source/core/util/FastDelegate.h
Normal file
File diff suppressed because it is too large
Load diff
131
Engine/source/core/util/autoPtr.h
Normal file
131
Engine/source/core/util/autoPtr.h
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AUTOPTR_H_
|
||||
#define _AUTOPTR_H_
|
||||
|
||||
#ifndef _TYPETRAITS_H_
|
||||
# include "platform/typetraits.h"
|
||||
#endif
|
||||
|
||||
|
||||
template<class T>
|
||||
struct AutoPtrRef
|
||||
{
|
||||
T* _ptr;
|
||||
AutoPtrRef(T *ptr)
|
||||
: _ptr(ptr)
|
||||
{}
|
||||
};
|
||||
|
||||
/// A simple smart pointer.
|
||||
/// An extended version of std::auto_ptr which supports a deletion policy.
|
||||
/// The delete policy indicates how the ptr is to be deleted. DeleteSingle,
|
||||
/// the default, is used to delete individual objects. DeleteArray can
|
||||
/// can be used to delete arrays.
|
||||
/// <code>
|
||||
/// AutoPtr<Object> ptr(new Object);
|
||||
/// AutoPtr<Object,DeleteSingle> ptr(new Object);
|
||||
/// AutoPtr<Object,DeleteArray> ptr(new Object[10]);
|
||||
/// </code>
|
||||
/// AutoPtrs do not perform reference counting and assume total ownership
|
||||
/// of any object assigned to them. Assigning an AutoPtr to another transfers
|
||||
/// that ownership and resets the source AutoPtr to 0.
|
||||
template<class T, class P = DeleteSingle>
|
||||
class AutoPtr
|
||||
{
|
||||
public:
|
||||
typedef T ValueType;
|
||||
|
||||
explicit AutoPtr(T *ptr = 0): _ptr(ptr) {}
|
||||
~AutoPtr()
|
||||
{
|
||||
P::destroy(_ptr);
|
||||
}
|
||||
|
||||
// Copy constructors
|
||||
AutoPtr(AutoPtr &rhs): _ptr(rhs.release()) {}
|
||||
|
||||
template<class U>
|
||||
AutoPtr(AutoPtr<U,P> &rhs): _ptr(rhs.release()) { }
|
||||
|
||||
/// Transfer ownership, any object currently be referenced is deleted and
|
||||
/// rhs is set to 0.
|
||||
AutoPtr& operator= (AutoPtr &rhs)
|
||||
{
|
||||
reset(rhs.release());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class U>
|
||||
AutoPtr& operator= (AutoPtr<U,P> &rhs)
|
||||
{
|
||||
reset(rhs.release());
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Access
|
||||
T* ptr() const { return _ptr; }
|
||||
T& operator*() const { return *_ptr; }
|
||||
T* operator->() const { return _ptr; }
|
||||
T& operator[](size_t index) { return (_ptr)[index]; }
|
||||
|
||||
/// Release ownership of the object without deleting it.
|
||||
T* release()
|
||||
{
|
||||
T* tmp(_ptr);
|
||||
_ptr = 0;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/// Equivalent to *this = (T*)ptr, except that operator=(T*) isn't provided for.
|
||||
void reset(T* ptr = 0)
|
||||
{
|
||||
if (_ptr != ptr)
|
||||
{
|
||||
P::destroy(_ptr);
|
||||
_ptr = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Conversion to/from ref type
|
||||
AutoPtr(AutoPtrRef<T> ref): _ptr(ref._ptr) {}
|
||||
AutoPtr& operator= (AutoPtrRef<T> ref)
|
||||
{
|
||||
reset(ref._ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isNull() const { return _ptr == NULL; }
|
||||
bool isValid() const { return !isNull(); }
|
||||
|
||||
template<class U>
|
||||
operator AutoPtrRef<U>() { return AutoPtrRef<U>(release()); }
|
||||
|
||||
template<class U>
|
||||
operator AutoPtr<U,P>() { return AutoPtr<U,P>(release()); }
|
||||
|
||||
private:
|
||||
T *_ptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
158
Engine/source/core/util/byteBuffer.cpp
Normal file
158
Engine/source/core/util/byteBuffer.cpp
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/util/byteBuffer.h"
|
||||
|
||||
|
||||
namespace Torque
|
||||
{
|
||||
|
||||
class PrivateBBData
|
||||
{
|
||||
public:
|
||||
PrivateBBData()
|
||||
: refCount( 1 ),
|
||||
dataSize( 0 ),
|
||||
data( NULL )
|
||||
{
|
||||
}
|
||||
|
||||
U32 refCount; ///< Reference count
|
||||
U32 dataSize; ///< Length of buffer
|
||||
U8 *data; ///< Our data buffer
|
||||
};
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
ByteBuffer::ByteBuffer()
|
||||
{
|
||||
_data = new PrivateBBData;
|
||||
_data->dataSize = 0;
|
||||
_data->data = NULL;
|
||||
}
|
||||
|
||||
ByteBuffer::ByteBuffer(U8 *dataPtr, U32 bufferSize)
|
||||
{
|
||||
_data = new PrivateBBData;
|
||||
_data->dataSize = bufferSize;
|
||||
_data->data = new U8[bufferSize];
|
||||
|
||||
dMemcpy( _data->data, dataPtr, bufferSize );
|
||||
}
|
||||
|
||||
ByteBuffer::ByteBuffer(U32 bufferSize)
|
||||
{
|
||||
_data = new PrivateBBData;
|
||||
_data->dataSize = bufferSize;
|
||||
_data->data = new U8[bufferSize];
|
||||
}
|
||||
|
||||
ByteBuffer::ByteBuffer(const ByteBuffer &theBuffer)
|
||||
{
|
||||
_data = theBuffer._data;
|
||||
_data->refCount++;
|
||||
}
|
||||
|
||||
ByteBuffer &ByteBuffer::operator=(const ByteBuffer &theBuffer)
|
||||
{
|
||||
_data = theBuffer._data;
|
||||
_data->refCount++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer::~ByteBuffer()
|
||||
{
|
||||
if (!--_data->refCount)
|
||||
{
|
||||
delete [] _data->data;
|
||||
delete _data;
|
||||
|
||||
_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void ByteBuffer::setBuffer(U8 *dataPtr, U32 bufferSize, bool copyData)
|
||||
{
|
||||
U8 *newData = dataPtr;
|
||||
|
||||
if ( copyData )
|
||||
{
|
||||
newData = new U8[bufferSize];
|
||||
|
||||
dMemcpy( newData, dataPtr, bufferSize );
|
||||
}
|
||||
|
||||
delete [] _data->data;
|
||||
|
||||
_data->data = newData;
|
||||
_data->dataSize = bufferSize;
|
||||
}
|
||||
|
||||
void ByteBuffer::resize(U32 newBufferSize)
|
||||
{
|
||||
U8 *newData = new U8[newBufferSize];
|
||||
|
||||
U32 copyLen = getMin( newBufferSize, _data->dataSize );
|
||||
|
||||
dMemcpy( newData, _data->data, copyLen );
|
||||
|
||||
delete [] _data->data;
|
||||
|
||||
_data->data = newData;
|
||||
_data->dataSize = newBufferSize;
|
||||
}
|
||||
|
||||
void ByteBuffer::appendBuffer(const U8 *dataBuffer, U32 bufferSize)
|
||||
{
|
||||
U32 start = _data->dataSize;
|
||||
resize(start + bufferSize);
|
||||
dMemcpy(_data->data + start, dataBuffer, bufferSize);
|
||||
}
|
||||
|
||||
U32 ByteBuffer::getBufferSize() const
|
||||
{
|
||||
return _data->dataSize;
|
||||
}
|
||||
|
||||
U8 *ByteBuffer::getBuffer()
|
||||
{
|
||||
return _data->data;
|
||||
}
|
||||
|
||||
const U8 *ByteBuffer::getBuffer() const
|
||||
{
|
||||
return _data->data;
|
||||
}
|
||||
|
||||
ByteBuffer ByteBuffer::getCopy() const
|
||||
{
|
||||
return ByteBuffer( _data->data, _data->dataSize );
|
||||
}
|
||||
|
||||
void ByteBuffer::clear()
|
||||
{
|
||||
dMemset(_data->data, 0, _data->dataSize);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
85
Engine/source/core/util/byteBuffer.h
Normal file
85
Engine/source/core/util/byteBuffer.h
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _BYTEBUFFER_H_
|
||||
#define _BYTEBUFFER_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
|
||||
namespace Torque
|
||||
{
|
||||
|
||||
class PrivateBBData;
|
||||
|
||||
class ByteBuffer
|
||||
{
|
||||
public:
|
||||
ByteBuffer();
|
||||
|
||||
/// Create a ByteBuffer from a chunk of memory.
|
||||
ByteBuffer(U8 *dataPtr, U32 bufferSize);
|
||||
|
||||
/// Create a ByteBuffer of the specified size.
|
||||
ByteBuffer(U32 bufferSize);
|
||||
|
||||
/// Copy constructor
|
||||
ByteBuffer(const ByteBuffer &theBuffer);
|
||||
|
||||
ByteBuffer &operator=(const ByteBuffer &theBuffer);
|
||||
|
||||
~ByteBuffer();
|
||||
|
||||
/// Set the ByteBuffer to point to a new chunk of memory.
|
||||
void setBuffer(U8 *dataPtr, U32 bufferSize, bool copyData);
|
||||
|
||||
/// Resize the buffer.
|
||||
void resize(U32 newBufferSize);
|
||||
|
||||
/// Appends the specified buffer to the end of the byte buffer.
|
||||
void appendBuffer(const U8 *dataBuffer, U32 bufferSize);
|
||||
|
||||
/// Appends the specified ByteBuffer to the end of this byte buffer.
|
||||
void appendBuffer(const ByteBuffer &theBuffer)
|
||||
{
|
||||
appendBuffer(theBuffer.getBuffer(), theBuffer.getBufferSize());
|
||||
}
|
||||
|
||||
U32 getBufferSize() const;
|
||||
|
||||
U8 *getBuffer();
|
||||
const U8 *getBuffer() const;
|
||||
|
||||
/// Copy the data in the buffer.
|
||||
ByteBuffer getCopy() const;
|
||||
|
||||
/// Clear the buffer.
|
||||
void clear();
|
||||
|
||||
private:
|
||||
PrivateBBData *_data;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
25
Engine/source/core/util/byteswap.h
Normal file
25
Engine/source/core/util/byteswap.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef BYTESWAP
|
||||
#define BYTESWAP(x, y) x = x ^ y; y = x ^ y; x = x ^y;
|
||||
#endif //defined(BYTESWAP)
|
||||
42
Engine/source/core/util/commonSwizzles.cpp
Normal file
42
Engine/source/core/util/commonSwizzles.cpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/util/swizzle.h"
|
||||
|
||||
namespace Swizzles
|
||||
{
|
||||
dsize_t _bgra[] = { 2, 1, 0, 3 };
|
||||
dsize_t _bgr[] = { 2, 1, 0 };
|
||||
dsize_t _rgb[] = { 0, 1, 2 };
|
||||
dsize_t _argb[] = { 3, 0, 1, 2 };
|
||||
dsize_t _rgba[] = { 0, 1, 2, 3 };
|
||||
dsize_t _abgr[] = { 3, 2, 1, 0 };
|
||||
|
||||
Swizzle<U8, 4> bgra( _bgra );
|
||||
Swizzle<U8, 3> bgr( _bgr );
|
||||
Swizzle<U8, 3> rgb( _rgb );
|
||||
Swizzle<U8, 4> argb( _argb );
|
||||
Swizzle<U8, 4> rgba( _rgba );
|
||||
Swizzle<U8, 4> abgr( _abgr );
|
||||
|
||||
NullSwizzle<U8, 4> null;
|
||||
}
|
||||
61
Engine/source/core/util/delegate.h
Normal file
61
Engine/source/core/util/delegate.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _UTIL_DELEGATE_H_
|
||||
#define _UTIL_DELEGATE_H_
|
||||
|
||||
#include "core/util/FastDelegate.h"
|
||||
|
||||
/// @def Delegate
|
||||
/// The macro which abstracts the details of the delegate implementation.
|
||||
#define Delegate fastdelegate::FastDelegate
|
||||
|
||||
/// @typedef DelegateMemento
|
||||
/// An opaque structure which can hold an arbitary delegate.
|
||||
/// @see Delegate
|
||||
typedef fastdelegate::DelegateMemento DelegateMemento;
|
||||
|
||||
|
||||
template<class T>
|
||||
class DelegateRemapper : public DelegateMemento
|
||||
{
|
||||
public:
|
||||
DelegateRemapper() : mOffset(0) {}
|
||||
|
||||
void set(T * t, const DelegateMemento & memento)
|
||||
{
|
||||
SetMementoFrom(memento);
|
||||
if (m_pthis)
|
||||
mOffset = ((int)m_pthis) - ((int)t);
|
||||
}
|
||||
|
||||
void rethis(T * t)
|
||||
{
|
||||
if (m_pthis)
|
||||
m_pthis = (fastdelegate::detail::GenericClass *)(mOffset + (int)t);
|
||||
}
|
||||
|
||||
protected:
|
||||
int mOffset;
|
||||
};
|
||||
|
||||
#endif // _UTIL_DELEGATE_H_
|
||||
108
Engine/source/core/util/dxt5nmSwizzle.h
Normal file
108
Engine/source/core/util/dxt5nmSwizzle.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _DXT5nm_SWIZZLE_H_
|
||||
#define _DXT5nm_SWIZZLE_H_
|
||||
|
||||
#include "core/util/swizzle.h"
|
||||
#include "core/util/byteswap.h"
|
||||
|
||||
class DXT5nmSwizzle : public Swizzle<U8, 4>
|
||||
{
|
||||
public:
|
||||
DXT5nmSwizzle() : Swizzle<U8, 4>( NULL ) {};
|
||||
|
||||
virtual void InPlace( void *memory, const dsize_t size ) const
|
||||
{
|
||||
AssertFatal( size % 4 == 0, "Bad buffer size for DXT5nm Swizzle" );
|
||||
|
||||
volatile U8 *u8Mem = reinterpret_cast<U8 *>( memory );
|
||||
|
||||
for( int i = 0; i < size >> 2; i++ )
|
||||
{
|
||||
// g = garbage byte
|
||||
// Input: [X|Y|Z|g] (rgba)
|
||||
// Output: [g|Y|0xFF|X] (bgra)
|
||||
BYTESWAP( u8Mem[0], u8Mem[3] ); // Store X in Alpha
|
||||
*u8Mem ^= *u8Mem; // 0 the garbage bit
|
||||
u8Mem[2] |= 0xFF; // Set Red to 1.0
|
||||
|
||||
u8Mem += 4;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void ToBuffer( void *destination, const void *source, const dsize_t size ) const
|
||||
{
|
||||
AssertFatal( size % 4 == 0, "Bad buffer size for DXT5nm Swizzle" );
|
||||
|
||||
volatile const U8 *srcU8 = reinterpret_cast<const U8 *>( source );
|
||||
volatile U8 *dstU8 = reinterpret_cast<U8 *>( destination );
|
||||
|
||||
for( int i = 0; i < size >> 2; i++ )
|
||||
{
|
||||
// g = garbage byte
|
||||
// Input: [X|Y|Z|g] (rgba)
|
||||
// Output: [g|Y|0xFF|X] (bgra)
|
||||
*dstU8++ = 0; // 0 garbage bit
|
||||
*dstU8++ = srcU8[1]; // Copy Y into G
|
||||
*dstU8++ |= 0xFF; // Set Red to 1.0
|
||||
*dstU8++ = srcU8[0]; // Copy X into Alpha
|
||||
|
||||
srcU8 += 4;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class DXT5nmSwizzleUp24t32 : public Swizzle<U8, 3>
|
||||
{
|
||||
public:
|
||||
DXT5nmSwizzleUp24t32() : Swizzle<U8, 3>( NULL ) {};
|
||||
|
||||
virtual void InPlace( void *memory, const dsize_t size ) const
|
||||
{
|
||||
AssertISV( false, "Cannot swizzle in place a 24->32 bit swizzle." );
|
||||
}
|
||||
|
||||
virtual void ToBuffer( void *destination, const void *source, const dsize_t size ) const
|
||||
{
|
||||
AssertFatal( size % 3 == 0, "Bad buffer size for DXT5nm Swizzle" );
|
||||
const int pixels = size / 3;
|
||||
|
||||
volatile const U8 *srcU8 = reinterpret_cast<const U8 *>( source );
|
||||
volatile U8 *dstU8 = reinterpret_cast<U8 *>( destination );
|
||||
|
||||
// destination better damn well be the right size
|
||||
for( int i = 0; i < pixels; i++ )
|
||||
{
|
||||
// g = garbage byte
|
||||
// Input: [X|Y|Z|g] (rgba)
|
||||
// Output: [g|Y|0xFF|X] (bgra)
|
||||
*dstU8++ = 0; // 0 garbage bit
|
||||
*dstU8++ = srcU8[1]; // Copy Y into G
|
||||
*dstU8++ |= 0xFF; // Set Red to 1.0
|
||||
*dstU8++ = srcU8[0]; // Copy X into Alpha
|
||||
|
||||
srcU8 += 3;
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
141
Engine/source/core/util/endian.h
Normal file
141
Engine/source/core/util/endian.h
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _ENDIAN_H_
|
||||
#define _ENDIAN_H_
|
||||
|
||||
#ifndef _TORQUE_TYPES_H_
|
||||
#include "platform/types.h"
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Endian conversions
|
||||
|
||||
inline U8 endianSwap(const U8 in_swap)
|
||||
{
|
||||
return in_swap;
|
||||
}
|
||||
|
||||
inline S8 endianSwap(const S8 in_swap)
|
||||
{
|
||||
return in_swap;
|
||||
}
|
||||
|
||||
/**
|
||||
Convert the byte ordering on the U16 to and from big/little endian format.
|
||||
@param in_swap Any U16
|
||||
@returns swapped U16.
|
||||
*/
|
||||
|
||||
inline U16 endianSwap(const U16 in_swap)
|
||||
{
|
||||
return U16(((in_swap >> 8) & 0x00ff) |
|
||||
((in_swap << 8) & 0xff00));
|
||||
}
|
||||
|
||||
inline S16 endianSwap(const S16 in_swap)
|
||||
{
|
||||
return S16(endianSwap(U16(in_swap)));
|
||||
}
|
||||
|
||||
/**
|
||||
Convert the byte ordering on the U32 to and from big/little endian format.
|
||||
@param in_swap Any U32
|
||||
@returns swapped U32.
|
||||
*/
|
||||
inline U32 endianSwap(const U32 in_swap)
|
||||
{
|
||||
return U32(((in_swap >> 24) & 0x000000ff) |
|
||||
((in_swap >> 8) & 0x0000ff00) |
|
||||
((in_swap << 8) & 0x00ff0000) |
|
||||
((in_swap << 24) & 0xff000000));
|
||||
}
|
||||
|
||||
inline S32 endianSwap(const S32 in_swap)
|
||||
{
|
||||
return S32(endianSwap(U32(in_swap)));
|
||||
}
|
||||
|
||||
inline U64 endianSwap(const U64 in_swap)
|
||||
{
|
||||
U32 *inp = (U32 *) &in_swap;
|
||||
U64 ret;
|
||||
U32 *outp = (U32 *) &ret;
|
||||
outp[0] = endianSwap(inp[1]);
|
||||
outp[1] = endianSwap(inp[0]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline S64 endianSwap(const S64 in_swap)
|
||||
{
|
||||
return S64(endianSwap(U64(in_swap)));
|
||||
}
|
||||
|
||||
inline F32 endianSwap(const F32 in_swap)
|
||||
{
|
||||
U32 result = endianSwap(* ((U32 *) &in_swap) );
|
||||
return * ((F32 *) &result);
|
||||
}
|
||||
|
||||
inline F64 endianSwap(const F64 in_swap)
|
||||
{
|
||||
U64 result = endianSwap(* ((U64 *) &in_swap) );
|
||||
return * ((F64 *) &result);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Endian conversions
|
||||
|
||||
#ifdef TORQUE_LITTLE_ENDIAN
|
||||
|
||||
#define TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(type) \
|
||||
inline type convertHostToLEndian(type i) { return i; } \
|
||||
inline type convertLEndianToHost(type i) { return i; } \
|
||||
inline type convertHostToBEndian(type i) { return endianSwap(i); } \
|
||||
inline type convertBEndianToHost(type i) { return endianSwap(i); }
|
||||
|
||||
#elif defined(TORQUE_BIG_ENDIAN)
|
||||
|
||||
#define TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(type) \
|
||||
inline type convertHostToLEndian(type i) { return endianSwap(i); } \
|
||||
inline type convertLEndianToHost(type i) { return endianSwap(i); } \
|
||||
inline type convertHostToBEndian(type i) { return i; } \
|
||||
inline type convertBEndianToHost(type i) { return i; }
|
||||
|
||||
#else
|
||||
#error "Endian define not set!"
|
||||
#endif
|
||||
|
||||
|
||||
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(U8)
|
||||
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(S8)
|
||||
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(U16)
|
||||
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(S16)
|
||||
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(U32)
|
||||
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(S32)
|
||||
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(U64)
|
||||
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(S64)
|
||||
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(F32)
|
||||
TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(F64)
|
||||
|
||||
#endif
|
||||
|
||||
27
Engine/source/core/util/fourcc.h
Normal file
27
Engine/source/core/util/fourcc.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef MakeFourCC
|
||||
#define MakeFourCC(ch0, ch1, ch2, ch3) \
|
||||
((U32)(U8)(ch0) | ((U32)(U8)(ch1) << 8) | \
|
||||
((U32)(U8)(ch2) << 16) | ((U32)(U8)(ch3) << 24 ))
|
||||
#endif //defined(MakeFourCC)
|
||||
271
Engine/source/core/util/hashFunction.cpp
Normal file
271
Engine/source/core/util/hashFunction.cpp
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Borrowed from: http://burtleburtle.net/bob/hash/doobs.html
|
||||
//
|
||||
// Original code by:
|
||||
//
|
||||
// By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this
|
||||
// code any way you wish, private, educational, or commercial. It's free.
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#include "core/util/hashFunction.h"
|
||||
|
||||
namespace Torque
|
||||
{
|
||||
|
||||
#define hashsize(n) ((U32)1<<(n))
|
||||
#define hashmask(n) (hashsize(n)-1)
|
||||
|
||||
/*
|
||||
--------------------------------------------------------------------
|
||||
mix -- mix 3 32-bit values reversibly.
|
||||
For every delta with one or two bits set, and the deltas of all three
|
||||
high bits or all three low bits, whether the original value of a,b,c
|
||||
is almost all zero or is uniformly distributed,
|
||||
* If mix() is run forward or backward, at least 32 bits in a,b,c
|
||||
have at least 1/4 probability of changing.
|
||||
* If mix() is run forward, every bit of c will change between 1/3 and
|
||||
2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.)
|
||||
mix() was built out of 36 single-cycle latency instructions in a
|
||||
structure that could supported 2x parallelism, like so:
|
||||
a -= b;
|
||||
a -= c; x = (c>>13);
|
||||
b -= c; a ^= x;
|
||||
b -= a; x = (a<<8);
|
||||
c -= a; b ^= x;
|
||||
c -= b; x = (b>>13);
|
||||
...
|
||||
Unfortunately, superscalar Pentiums and Sparcs can't take advantage
|
||||
of that parallelism. They've also turned some of those single-cycle
|
||||
latency instructions into multi-cycle latency instructions. Still,
|
||||
this is the fastest good hash I could find. There were about 2^^68
|
||||
to choose from. I only looked at a billion or so.
|
||||
--------------------------------------------------------------------
|
||||
*/
|
||||
#define mix(a,b,c) \
|
||||
{ \
|
||||
a -= b; a -= c; a ^= (c>>13); \
|
||||
b -= c; b -= a; b ^= (a<<8); \
|
||||
c -= a; c -= b; c ^= (b>>13); \
|
||||
a -= b; a -= c; a ^= (c>>12); \
|
||||
b -= c; b -= a; b ^= (a<<16); \
|
||||
c -= a; c -= b; c ^= (b>>5); \
|
||||
a -= b; a -= c; a ^= (c>>3); \
|
||||
b -= c; b -= a; b ^= (a<<10); \
|
||||
c -= a; c -= b; c ^= (b>>15); \
|
||||
}
|
||||
|
||||
/*
|
||||
--------------------------------------------------------------------
|
||||
hash() -- hash a variable-length key into a 32-bit value
|
||||
k : the key (the unaligned variable-length array of bytes)
|
||||
len : the length of the key, counting by bytes
|
||||
initval : can be any 4-byte value
|
||||
Returns a 32-bit value. Every bit of the key affects every bit of
|
||||
the return value. Every 1-bit and 2-bit delta achieves avalanche.
|
||||
About 6*len+35 instructions.
|
||||
|
||||
The best hash table sizes are powers of 2. There is no need to do
|
||||
mod a prime (mod is sooo slow!). If you need less than 32 bits,
|
||||
use a bitmask. For example, if you need only 10 bits, do
|
||||
h = (h & hashmask(10));
|
||||
In which case, the hash table should have hashsize(10) elements.
|
||||
|
||||
If you are hashing n strings (U8 **)k, do it like this:
|
||||
for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);
|
||||
|
||||
By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this
|
||||
code any way you wish, private, educational, or commercial. It's free.
|
||||
|
||||
See http://burtleburtle.net/bob/hash/evahash.html
|
||||
Use for hash table lookup, or anything where one collision in 2^^32 is
|
||||
acceptable. Do NOT use for cryptographic purposes.
|
||||
--------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
U32 hash(register const U8 *k, register U32 length, register U32 initval)
|
||||
{
|
||||
register U32 a,b,c,len;
|
||||
|
||||
/* Set up the internal state */
|
||||
len = length;
|
||||
a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
|
||||
c = initval; /* the previous hash value */
|
||||
|
||||
/*---------------------------------------- handle most of the key */
|
||||
while (len >= 12)
|
||||
{
|
||||
a += (k[0] +((U32)k[1]<<8) +((U32)k[2]<<16) +((U32)k[3]<<24));
|
||||
b += (k[4] +((U32)k[5]<<8) +((U32)k[6]<<16) +((U32)k[7]<<24));
|
||||
c += (k[8] +((U32)k[9]<<8) +((U32)k[10]<<16)+((U32)k[11]<<24));
|
||||
mix(a,b,c);
|
||||
k += 12; len -= 12;
|
||||
}
|
||||
|
||||
/*------------------------------------- handle the last 11 bytes */
|
||||
c += length;
|
||||
switch(len) /* all the case statements fall through */
|
||||
{
|
||||
case 11: c+=((U32)k[10]<<24);
|
||||
case 10: c+=((U32)k[9]<<16);
|
||||
case 9 : c+=((U32)k[8]<<8);
|
||||
/* the first byte of c is reserved for the length */
|
||||
case 8 : b+=((U32)k[7]<<24);
|
||||
case 7 : b+=((U32)k[6]<<16);
|
||||
case 6 : b+=((U32)k[5]<<8);
|
||||
case 5 : b+=k[4];
|
||||
case 4 : a+=((U32)k[3]<<24);
|
||||
case 3 : a+=((U32)k[2]<<16);
|
||||
case 2 : a+=((U32)k[1]<<8);
|
||||
case 1 : a+=k[0];
|
||||
/* case 0: nothing left to add */
|
||||
}
|
||||
mix(a,b,c);
|
||||
/*-------------------------------------------- report the result */
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
--------------------------------------------------------------------
|
||||
mix -- mix 3 64-bit values reversibly.
|
||||
mix() takes 48 machine instructions, but only 24 cycles on a superscalar
|
||||
machine (like Intel's new MMX architecture). It requires 4 64-bit
|
||||
registers for 4::2 parallelism.
|
||||
All 1-bit deltas, all 2-bit deltas, all deltas composed of top bits of
|
||||
(a,b,c), and all deltas of bottom bits were tested. All deltas were
|
||||
tested both on random keys and on keys that were nearly all zero.
|
||||
These deltas all cause every bit of c to change between 1/3 and 2/3
|
||||
of the time (well, only 113/400 to 287/400 of the time for some
|
||||
2-bit delta). These deltas all cause at least 80 bits to change
|
||||
among (a,b,c) when the mix is run either forward or backward (yes it
|
||||
is reversible).
|
||||
This implies that a hash using mix64 has no funnels. There may be
|
||||
characteristics with 3-bit deltas or bigger, I didn't test for
|
||||
those.
|
||||
--------------------------------------------------------------------
|
||||
*/
|
||||
#define mix64(a,b,c) \
|
||||
{ \
|
||||
a -= b; a -= c; a ^= (c>>43); \
|
||||
b -= c; b -= a; b ^= (a<<9); \
|
||||
c -= a; c -= b; c ^= (b>>8); \
|
||||
a -= b; a -= c; a ^= (c>>38); \
|
||||
b -= c; b -= a; b ^= (a<<23); \
|
||||
c -= a; c -= b; c ^= (b>>5); \
|
||||
a -= b; a -= c; a ^= (c>>35); \
|
||||
b -= c; b -= a; b ^= (a<<49); \
|
||||
c -= a; c -= b; c ^= (b>>11); \
|
||||
a -= b; a -= c; a ^= (c>>12); \
|
||||
b -= c; b -= a; b ^= (a<<18); \
|
||||
c -= a; c -= b; c ^= (b>>22); \
|
||||
}
|
||||
|
||||
/*
|
||||
--------------------------------------------------------------------
|
||||
hash64() -- hash a variable-length key into a 64-bit value
|
||||
k : the key (the unaligned variable-length array of bytes)
|
||||
len : the length of the key, counting by bytes
|
||||
level : can be any 8-byte value
|
||||
Returns a 64-bit value. Every bit of the key affects every bit of
|
||||
the return value. No funnels. Every 1-bit and 2-bit delta achieves
|
||||
avalanche. About 41+5len instructions.
|
||||
|
||||
The best hash table sizes are powers of 2. There is no need to do
|
||||
mod a prime (mod is sooo slow!). If you need less than 64 bits,
|
||||
use a bitmask. For example, if you need only 10 bits, do
|
||||
h = (h & hashmask(10));
|
||||
In which case, the hash table should have hashsize(10) elements.
|
||||
|
||||
If you are hashing n strings (ub1 **)k, do it like this:
|
||||
for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);
|
||||
|
||||
By Bob Jenkins, Jan 4 1997. bob_jenkins@burtleburtle.net. You may
|
||||
use this code any way you wish, private, educational, or commercial,
|
||||
but I would appreciate if you give me credit.
|
||||
|
||||
See http://burtleburtle.net/bob/hash/evahash.html
|
||||
Use for hash table lookup, or anything where one collision in 2^^64
|
||||
is acceptable. Do NOT use for cryptographic purposes.
|
||||
--------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
U64 hash64( register const U8 *k, register U32 length, register U64 initval )
|
||||
{
|
||||
register U64 a,b,c,len;
|
||||
|
||||
/* Set up the internal state */
|
||||
len = length;
|
||||
a = b = initval; /* the previous hash value */
|
||||
c = 0x9e3779b97f4a7c13LL; /* the golden ratio; an arbitrary value */
|
||||
|
||||
/*---------------------------------------- handle most of the key */
|
||||
while (len >= 24)
|
||||
{
|
||||
a += (k[0] +((U64)k[ 1]<< 8)+((U64)k[ 2]<<16)+((U64)k[ 3]<<24)
|
||||
+((U64)k[4 ]<<32)+((U64)k[ 5]<<40)+((U64)k[ 6]<<48)+((U64)k[ 7]<<56));
|
||||
b += (k[8] +((U64)k[ 9]<< 8)+((U64)k[10]<<16)+((U64)k[11]<<24)
|
||||
+((U64)k[12]<<32)+((U64)k[13]<<40)+((U64)k[14]<<48)+((U64)k[15]<<56));
|
||||
c += (k[16] +((U64)k[17]<< 8)+((U64)k[18]<<16)+((U64)k[19]<<24)
|
||||
+((U64)k[20]<<32)+((U64)k[21]<<40)+((U64)k[22]<<48)+((U64)k[23]<<56));
|
||||
mix64(a,b,c);
|
||||
k += 24; len -= 24;
|
||||
}
|
||||
|
||||
/*------------------------------------- handle the last 23 bytes */
|
||||
c += length;
|
||||
switch(len) /* all the case statements fall through */
|
||||
{
|
||||
case 23: c+=((U64)k[22]<<56);
|
||||
case 22: c+=((U64)k[21]<<48);
|
||||
case 21: c+=((U64)k[20]<<40);
|
||||
case 20: c+=((U64)k[19]<<32);
|
||||
case 19: c+=((U64)k[18]<<24);
|
||||
case 18: c+=((U64)k[17]<<16);
|
||||
case 17: c+=((U64)k[16]<<8);
|
||||
/* the first byte of c is reserved for the length */
|
||||
case 16: b+=((U64)k[15]<<56);
|
||||
case 15: b+=((U64)k[14]<<48);
|
||||
case 14: b+=((U64)k[13]<<40);
|
||||
case 13: b+=((U64)k[12]<<32);
|
||||
case 12: b+=((U64)k[11]<<24);
|
||||
case 11: b+=((U64)k[10]<<16);
|
||||
case 10: b+=((U64)k[ 9]<<8);
|
||||
case 9: b+=((U64)k[ 8]);
|
||||
case 8: a+=((U64)k[ 7]<<56);
|
||||
case 7: a+=((U64)k[ 6]<<48);
|
||||
case 6: a+=((U64)k[ 5]<<40);
|
||||
case 5: a+=((U64)k[ 4]<<32);
|
||||
case 4: a+=((U64)k[ 3]<<24);
|
||||
case 3: a+=((U64)k[ 2]<<16);
|
||||
case 2: a+=((U64)k[ 1]<<8);
|
||||
case 1: a+=((U64)k[ 0]);
|
||||
/* case 0: nothing left to add */
|
||||
}
|
||||
mix64(a,b,c);
|
||||
/*-------------------------------------------- report the result */
|
||||
return c;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
39
Engine/source/core/util/hashFunction.h
Normal file
39
Engine/source/core/util/hashFunction.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _HASHFUNCTION_H_
|
||||
#define _HASHFUNCTION_H_
|
||||
|
||||
#ifndef _TORQUE_TYPES_H_
|
||||
#include "platform/types.h"
|
||||
#endif
|
||||
|
||||
namespace Torque
|
||||
{
|
||||
|
||||
extern U32 hash(register const U8 *k, register U32 length, register U32 initval);
|
||||
|
||||
extern U64 hash64(register const U8 *k, register U32 length, register U64 initval);
|
||||
|
||||
}
|
||||
|
||||
#endif // _HASHFUNCTION_H_
|
||||
202
Engine/source/core/util/journal/journal.cpp
Normal file
202
Engine/source/core/util/journal/journal.cpp
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/util/journal/journal.h"
|
||||
|
||||
#include "core/stream/fileStream.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
#include "console/console.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Journal::FuncDecl* Journal::_FunctionList;
|
||||
Stream *Journal::mFile;
|
||||
Journal::Mode Journal::_State = Journal::StopState;
|
||||
U32 Journal::_Count;
|
||||
bool Journal::_Dispatching = false;
|
||||
|
||||
Journal Journal::smInstance;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Journal::~Journal()
|
||||
{
|
||||
if( mFile )
|
||||
Stop();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Journal::Functor* Journal::_create(Id id)
|
||||
{
|
||||
for (FuncDecl* ptr = _FunctionList; ptr; ptr = ptr->next)
|
||||
if (ptr->id == id)
|
||||
return ptr->create();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Journal::Id Journal::_getFunctionId(VoidPtr ptr,VoidMethod method)
|
||||
{
|
||||
for (FuncDecl* itr = _FunctionList; itr; itr = itr->next)
|
||||
if (itr->match(ptr,method))
|
||||
return itr->id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Journal::_removeFunctionId(VoidPtr ptr,VoidMethod method)
|
||||
{
|
||||
FuncDecl ** itr = &_FunctionList;
|
||||
|
||||
do
|
||||
{
|
||||
if((*itr)->match(ptr, method))
|
||||
{
|
||||
// Unlink and break.
|
||||
FuncDecl* decl = *itr;
|
||||
idPool().free( decl->id );
|
||||
*itr = (*itr)->next;
|
||||
delete decl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Advance to next...
|
||||
itr = &((*itr)->next);
|
||||
}
|
||||
while(*itr);
|
||||
}
|
||||
|
||||
void Journal::_start()
|
||||
{
|
||||
}
|
||||
|
||||
void Journal::_finish()
|
||||
{
|
||||
if (_State == PlayState)
|
||||
--_Count;
|
||||
else {
|
||||
U32 pos = mFile->getPosition();
|
||||
mFile->setPosition(0);
|
||||
mFile->write(++_Count);
|
||||
mFile->setPosition(pos);
|
||||
}
|
||||
}
|
||||
|
||||
void Journal::Record(const char * file)
|
||||
{
|
||||
if (_State == DisabledState)
|
||||
{
|
||||
Con::errorf("//---------------------------------------------//");
|
||||
Con::errorf("Journal::Record() - Cannot record a journal after GuiCanvas or NetConnection creation!");
|
||||
Con::errorf("To record before canvas/netConnection creation, run %s with the following arguments: -jSave %s",
|
||||
Platform::getExecutableName(), file);
|
||||
Con::errorf("//---------------------------------------------//");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_State == StopState)
|
||||
{
|
||||
_Count = 0;
|
||||
mFile = new FileStream();
|
||||
|
||||
if( ((FileStream*)mFile)->open(file, Torque::FS::File::Write) )
|
||||
{
|
||||
mFile->write(_Count);
|
||||
_State = RecordState;
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertWarn(false,"Journal: Could not create journal file");
|
||||
Con::errorf("Journal: Could not create journal file '%s'", file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Journal::Play(const char * file)
|
||||
{
|
||||
if (_State == DisabledState)
|
||||
{
|
||||
Con::errorf("//---------------------------------------------//");
|
||||
Con::errorf("Journal::Play() - Cannot playback a journal after GuiCanvas or NetConnection creation!");
|
||||
Con::errorf("To playback before canvas/netConnection creation, run %s with the following arguments: -jPlay %s",
|
||||
Platform::getExecutableName(), file);
|
||||
Con::errorf("//---------------------------------------------//");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_State == StopState)
|
||||
{
|
||||
SAFE_DELETE(mFile);
|
||||
mFile = new FileStream();
|
||||
if( ((FileStream*)mFile)->open(file, Torque::FS::File::Read) )
|
||||
{
|
||||
mFile->read(&_Count);
|
||||
_State = PlayState;
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertWarn(false,"Journal: Could not open journal file");
|
||||
Con::errorf("Journal: Could not open journal file '%s'", file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Journal::Stop()
|
||||
{
|
||||
AssertFatal(mFile, "Journal::Stop - no file stream open!");
|
||||
|
||||
SAFE_DELETE( mFile );
|
||||
_State = StopState;
|
||||
}
|
||||
|
||||
bool Journal::PlayNext()
|
||||
{
|
||||
if (_State == PlayState) {
|
||||
_start();
|
||||
Id id;
|
||||
|
||||
mFile->read(&id);
|
||||
|
||||
Functor* jrn = _create(id);
|
||||
AssertFatal(jrn,"Journal: Undefined function found in journal");
|
||||
jrn->read(mFile);
|
||||
_finish();
|
||||
|
||||
_Dispatching = true;
|
||||
jrn->dispatch();
|
||||
_Dispatching = false;
|
||||
|
||||
delete jrn;
|
||||
if (_Count)
|
||||
return true;
|
||||
Stop();
|
||||
|
||||
//debugBreak();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Journal::Disable()
|
||||
{
|
||||
if (_State == StopState)
|
||||
_State = DisabledState;
|
||||
}
|
||||
635
Engine/source/core/util/journal/journal.h
Normal file
635
Engine/source/core/util/journal/journal.h
Normal file
|
|
@ -0,0 +1,635 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _UTIL_JOURNAL_JOURNAL_H_
|
||||
#define _UTIL_JOURNAL_JOURNAL_H_
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/stream/stream.h"
|
||||
#include "util/returnType.h"
|
||||
#include "core/stream/ioHelper.h"
|
||||
#include "core/idGenerator.h"
|
||||
|
||||
/// Journaling System.
|
||||
///
|
||||
/// The journaling system is used to record external events and
|
||||
/// non-deterministic values from an execution run in order that the
|
||||
/// run may be re-played for debugging purposes. The journaling
|
||||
/// system is integrated into the platform library, though not all
|
||||
/// platform calls are journaled.
|
||||
///
|
||||
/// File system calls are not journaled, so any modified files must
|
||||
/// be reset to their original state before playback. Only a single
|
||||
/// journal can be recored or played back at a time.
|
||||
///
|
||||
/// For the journals to play back correctly, journal events cannot
|
||||
/// be triggered during the processing of another event.
|
||||
class Journal
|
||||
{
|
||||
Journal() {}
|
||||
~Journal();
|
||||
|
||||
static Journal smInstance;
|
||||
|
||||
typedef U32 Id;
|
||||
typedef void* VoidPtr;
|
||||
typedef void (Journal::*VoidMethod)();
|
||||
|
||||
/// Functor base classes
|
||||
struct Functor
|
||||
{
|
||||
Functor() {}
|
||||
virtual ~Functor() {}
|
||||
virtual void read(Stream *s) = 0;
|
||||
virtual void dispatch() = 0;
|
||||
};
|
||||
|
||||
/// Multiple argument function functor specialization
|
||||
template <class T>
|
||||
struct FunctorDecl: public Functor {
|
||||
typedef void(*FuncPtr)();
|
||||
FuncPtr ptr;
|
||||
FunctorDecl(FuncPtr p): ptr(p) {}
|
||||
void read(Stream *file) {}
|
||||
void dispatch() { (*ptr)(); }
|
||||
};
|
||||
|
||||
template <class A,class B,class C,class D,class E,class F,class G,class H,class I,class J>
|
||||
struct FunctorDecl< void(*)(A,B,C,D,E,F,G,H,I,J) >: public Functor {
|
||||
typedef void(*FuncPtr)(A,B,C,D,E,F,G,H,I,J);
|
||||
FuncPtr ptr; A a; B b; C c; D d; E e; F f; G g; H h; I i; J j;
|
||||
FunctorDecl(FuncPtr p): ptr(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h,i,j); }
|
||||
void dispatch() { (*ptr)(a,b,c,d,e,f,g,h,i,j); }
|
||||
};
|
||||
|
||||
template <class A,class B,class C,class D,class E,class F,class G,class H,class I>
|
||||
struct FunctorDecl< void(*)(A,B,C,D,E,F,G,H,I) >: public Functor {
|
||||
typedef void(*FuncPtr)(A,B,C,D,E,F,G,H,I);
|
||||
FuncPtr ptr; A a; B b; C c; D d; E e; F f; G g; H h; I i;
|
||||
FunctorDecl(FuncPtr p): ptr(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h,i); }
|
||||
void dispatch() { (*ptr)(a,b,c,d,e,f,g,h,i); }
|
||||
};
|
||||
|
||||
template <class A,class B,class C,class D,class E,class F,class G,class H>
|
||||
struct FunctorDecl< void(*)(A,B,C,D,E,F,G,H) >: public Functor {
|
||||
typedef void(*FuncPtr)(A,B,C,D,E,F,G,H);
|
||||
FuncPtr ptr; A a; B b; C c; D d; E e; F f; G g; H h;
|
||||
FunctorDecl(FuncPtr p): ptr(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h); }
|
||||
void dispatch() { (*ptr)(a,b,c,d,e,f,g,h); }
|
||||
};
|
||||
|
||||
template <class A,class B,class C,class D,class E,class F,class G>
|
||||
struct FunctorDecl< void(*)(A,B,C,D,E,F,G) >: public Functor {
|
||||
typedef void(*FuncPtr)(A,B,C,D,E,F,G);
|
||||
FuncPtr ptr; A a; B b; C c; D d; E e; F f; G g;
|
||||
FunctorDecl(FuncPtr p): ptr(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g); }
|
||||
void dispatch() { (*ptr)(a,b,c,d,e,f,g); }
|
||||
};
|
||||
|
||||
template <class A,class B,class C,class D,class E,class F>
|
||||
struct FunctorDecl< void(*)(A,B,C,D,E,F) >: public Functor {
|
||||
typedef void(*FuncPtr)(A,B,C,D,E,F);
|
||||
FuncPtr ptr; A a; B b; C c; D d; E e; F f;
|
||||
FunctorDecl(FuncPtr p): ptr(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f); }
|
||||
void dispatch() { (*ptr)(a,b,c,d,e,f); }
|
||||
};
|
||||
|
||||
template <class A,class B,class C,class D,class E>
|
||||
struct FunctorDecl< void(*)(A,B,C,D,E) >: public Functor {
|
||||
typedef void(*FuncPtr)(A,B,C,D,E);
|
||||
FuncPtr ptr; A a; B b; C c; D d; E e;
|
||||
FunctorDecl(FuncPtr p): ptr(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e); }
|
||||
void dispatch() { (*ptr)(a,b,c,d,e); }
|
||||
};
|
||||
|
||||
template <class A,class B,class C,class D>
|
||||
struct FunctorDecl< void(*)(A,B,C,D) >: public Functor {
|
||||
typedef void(*FuncPtr)(A,B,C,D);
|
||||
FuncPtr ptr; A a; B b; C c; D d;
|
||||
FunctorDecl(FuncPtr p): ptr(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b,c,d); }
|
||||
void dispatch() { (*ptr)(a,b,c,d); }
|
||||
};
|
||||
|
||||
template <class A,class B,class C>
|
||||
struct FunctorDecl< void(*)(A,B,C) >: public Functor {
|
||||
typedef void(*FuncPtr)(A,B,C);
|
||||
FuncPtr ptr; A a; B b; C c;
|
||||
FunctorDecl(FuncPtr p): ptr(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b,c); }
|
||||
void dispatch() { (*ptr)(a,b,c); }
|
||||
};
|
||||
|
||||
template <class A,class B>
|
||||
struct FunctorDecl< void(*)(A,B) >: public Functor {
|
||||
typedef void(*FuncPtr)(A,B);
|
||||
FuncPtr ptr; A a; B b;
|
||||
FunctorDecl(FuncPtr p): ptr(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b); }
|
||||
void dispatch() { (*ptr)(a,b); }
|
||||
};
|
||||
|
||||
template <class A>
|
||||
struct FunctorDecl< void(*)(A) >: public Functor {
|
||||
typedef void(*FuncPtr)(A);
|
||||
FuncPtr ptr; A a;
|
||||
FunctorDecl(FuncPtr p): ptr(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a); }
|
||||
void dispatch() { (*ptr)(a); }
|
||||
};
|
||||
|
||||
// Multiple argument object member function functor specialization
|
||||
template <class T,class U>
|
||||
struct MethodDecl: public Functor {
|
||||
typedef T ObjPtr;
|
||||
typedef U MethodPtr;
|
||||
ObjPtr obj; MethodPtr method;
|
||||
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
|
||||
void read(Stream *file) {}
|
||||
void dispatch() { (obj->*method)(); }
|
||||
};
|
||||
|
||||
template <class T,class A,class B,class C,class D,class E,class F,class G,class H,class I,class J>
|
||||
struct MethodDecl<T*, void(T::*)(A,B,C,D,E,F,G,H,I,J) >: public Functor {
|
||||
typedef T* ObjPtr;
|
||||
typedef void(T::*MethodPtr)(A,B,C,D,E,F,G,H,I,J);
|
||||
ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e; F f; G g; H h; I i; J j;
|
||||
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h,i,j); }
|
||||
void dispatch() { (obj->*method)(a,b,c,d,e,f,g,h,i,j); }
|
||||
};
|
||||
|
||||
template <class T,class A,class B,class C,class D,class E,class F,class G,class H,class I>
|
||||
struct MethodDecl<T*, void(T::*)(A,B,C,D,E,F,G,H,I) >: public Functor {
|
||||
typedef T* ObjPtr;
|
||||
typedef void(T::*MethodPtr)(A,B,C,D,E,F,G,H,I);
|
||||
ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e; F f; G g; H h; I i;
|
||||
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h,i); }
|
||||
void dispatch() { (obj->*method)(a,b,c,d,e,f,g,h,i); }
|
||||
};
|
||||
|
||||
template <class T,class A,class B,class C,class D,class E,class F,class G,class H>
|
||||
struct MethodDecl<T*, void(T::*)(A,B,C,D,E,F,G,H) >: public Functor {
|
||||
typedef T* ObjPtr;
|
||||
typedef void(T::*MethodPtr)(A,B,C,D,E,F,G,H);
|
||||
ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e; F f; G g; H h;
|
||||
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h); }
|
||||
void dispatch() { (obj->*method)(a,b,c,d,e,f,g,h); }
|
||||
};
|
||||
|
||||
template <class T,class A,class B,class C,class D,class E,class F,class G>
|
||||
struct MethodDecl<T*, void(T::*)(A,B,C,D,E,F,G) >: public Functor {
|
||||
typedef T* ObjPtr;
|
||||
typedef void(T::*MethodPtr)(A,B,C,D,E,F,G);
|
||||
ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e; F f; G g;
|
||||
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g); }
|
||||
void dispatch() { (obj->*method)(a,b,c,d,e,f,g); }
|
||||
};
|
||||
|
||||
template <class T,class A,class B,class C,class D,class E,class F>
|
||||
struct MethodDecl<T*, void(T::*)(A,B,C,D,E,F) >: public Functor {
|
||||
typedef T* ObjPtr;
|
||||
typedef void(T::*MethodPtr)(A,B,C,D,E,F);
|
||||
ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e; F f;
|
||||
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f); }
|
||||
void dispatch() { (obj->*method)(a,b,c,d,e,f); }
|
||||
};
|
||||
|
||||
template <class T,class A,class B,class C,class D,class E>
|
||||
struct MethodDecl<T*, void(T::*)(A,B,C,D,E) >: public Functor {
|
||||
typedef T* ObjPtr;
|
||||
typedef void(T::*MethodPtr)(A,B,C,D,E);
|
||||
ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e;
|
||||
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e); }
|
||||
void dispatch() { (obj->*method)(a,b,c,d,e); }
|
||||
};
|
||||
|
||||
template <class T,class A,class B,class C,class D>
|
||||
struct MethodDecl<T*, void(T::*)(A,B,C,D) >: public Functor {
|
||||
typedef T* ObjPtr;
|
||||
typedef void(T::*MethodPtr)(A,B,C,D);
|
||||
ObjPtr obj; MethodPtr method; A a; B b; C c; D d;
|
||||
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b,c,d); }
|
||||
void dispatch() { (obj->*method)(a,b,c,d); }
|
||||
};
|
||||
|
||||
template <class T,class A,class B,class C>
|
||||
struct MethodDecl<T*, void(T::*)(A,B,C) >: public Functor {
|
||||
typedef T* ObjPtr;
|
||||
typedef void(T::*MethodPtr)(A,B,C);
|
||||
ObjPtr obj; MethodPtr method; A a; B b; C c;
|
||||
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b,c); }
|
||||
void dispatch() { (obj->*method)(a,b,c); }
|
||||
};
|
||||
|
||||
template <class T,class A,class B>
|
||||
struct MethodDecl<T*, void(T::*)(A,B) >: public Functor {
|
||||
typedef T* ObjPtr;
|
||||
typedef void(T::*MethodPtr)(A,B);
|
||||
ObjPtr obj; MethodPtr method; A a; B b;
|
||||
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a,b); }
|
||||
void dispatch() { (obj->*method)(a,b); }
|
||||
};
|
||||
|
||||
template <class T,class A>
|
||||
struct MethodDecl<T*, void(T::*)(A) >: public Functor {
|
||||
typedef T* ObjPtr;
|
||||
typedef void(T::*MethodPtr)(A);
|
||||
ObjPtr obj; MethodPtr method; A a;
|
||||
MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {}
|
||||
void read(Stream *file) { IOHelper::reads(file,a); }
|
||||
void dispatch() { (obj->*method)(a); }
|
||||
};
|
||||
|
||||
// Function declarations
|
||||
struct FuncDecl {
|
||||
FuncDecl* next;
|
||||
Id id;
|
||||
virtual ~FuncDecl() {}
|
||||
virtual bool match(VoidPtr,VoidMethod) const = 0;
|
||||
virtual Functor* create() const = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct FuncRep: public FuncDecl {
|
||||
typename T::FuncPtr function;
|
||||
virtual bool match(VoidPtr ptr,VoidMethod) const {
|
||||
return function == (typename T::FuncPtr)ptr;
|
||||
}
|
||||
T* create() const { return new T(function); };
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct MethodRep: public FuncDecl {
|
||||
typename T::ObjPtr obj;
|
||||
typename T::MethodPtr method;
|
||||
virtual bool match(VoidPtr ptr,VoidMethod func) const {
|
||||
return obj == (typename T::ObjPtr)ptr && method == (typename T::MethodPtr)func;
|
||||
}
|
||||
T* create() const { return new T(obj,method); };
|
||||
};
|
||||
|
||||
static FuncDecl* _FunctionList;
|
||||
|
||||
static inline IdGenerator &idPool()
|
||||
{
|
||||
static IdGenerator _IdPool(1, 65535);
|
||||
return _IdPool;
|
||||
}
|
||||
|
||||
static U32 _Count;
|
||||
static Stream *mFile;
|
||||
static enum Mode {
|
||||
StopState, PlayState, RecordState, DisabledState
|
||||
} _State;
|
||||
static bool _Dispatching;
|
||||
|
||||
static Functor* _create(Id id);
|
||||
static void _start();
|
||||
static void _finish();
|
||||
static Id _getFunctionId(VoidPtr ptr,VoidMethod method);
|
||||
static void _removeFunctionId(VoidPtr ptr,VoidMethod method);
|
||||
|
||||
public:
|
||||
static void Record(const char * file);
|
||||
static void Play(const char * file);
|
||||
static bool PlayNext();
|
||||
static void Stop();
|
||||
static void Disable();
|
||||
|
||||
/// Returns true if in recording mode.
|
||||
static inline bool IsRecording() {
|
||||
return _State == RecordState;
|
||||
}
|
||||
|
||||
/// Returns true if in play mode.
|
||||
static inline bool IsPlaying() {
|
||||
return _State == PlayState;
|
||||
}
|
||||
|
||||
/// Returns true if a function is being dispatched
|
||||
static inline bool IsDispatching() {
|
||||
return _Dispatching;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void Read(T *v)
|
||||
{
|
||||
AssertFatal(IsPlaying(), "Journal::Read - not playing right now.");
|
||||
bool r = mFile->read(v);
|
||||
AssertFatal(r, "Journal::Read - failed to read!");
|
||||
}
|
||||
|
||||
static bool Read(U32 size, void *buffer)
|
||||
{
|
||||
AssertFatal(IsPlaying(), "Journal::Read - not playing right now.");
|
||||
bool r = mFile->read(size, buffer);
|
||||
AssertFatal(r, "Journal::Read - failed to read!");
|
||||
return r;
|
||||
}
|
||||
|
||||
static void ReadString(char str[256])
|
||||
{
|
||||
AssertFatal(IsPlaying(), "Journal::ReadString - not playing right now.");
|
||||
mFile->readString(str);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void Write(const T &v)
|
||||
{
|
||||
AssertFatal(IsRecording(), "Journal::Write - not recording right now.");
|
||||
bool r = mFile->write(v);
|
||||
AssertFatal(r, "Journal::Write - failed to write!");
|
||||
}
|
||||
|
||||
static bool Write(U32 size, void *buffer)
|
||||
{
|
||||
AssertFatal(IsRecording(), "Journal::Write - not recording right now.");
|
||||
bool r = mFile->write(size, buffer);
|
||||
AssertFatal(r, "Journal::Write - failed to write!");
|
||||
return r;
|
||||
}
|
||||
|
||||
static void WriteString(const char str[256])
|
||||
{
|
||||
AssertFatal(IsRecording(), "Journal::WriteString - not recording right now.");
|
||||
mFile->writeString(str);
|
||||
}
|
||||
|
||||
/// Register a function with the journalling system.
|
||||
template<typename T>
|
||||
static void DeclareFunction(T func) {
|
||||
if (!_getFunctionId((VoidPtr)func,0)) {
|
||||
FuncRep<FunctorDecl<T> >* decl = new FuncRep<FunctorDecl<T> >;
|
||||
decl->function = func;
|
||||
decl->id = idPool().alloc();
|
||||
decl->next = _FunctionList;
|
||||
_FunctionList = decl;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
static void DeclareFunction(T obj, U method)
|
||||
{
|
||||
if (!_getFunctionId((VoidPtr)obj,(VoidMethod)method)) {
|
||||
MethodRep<MethodDecl<T,U> >* decl = new MethodRep<MethodDecl<T,U> >;
|
||||
decl->obj = obj;
|
||||
decl->method = method;
|
||||
decl->id = idPool().alloc();
|
||||
decl->next = _FunctionList;
|
||||
_FunctionList = decl;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
static void RemoveFunction(T obj, U method)
|
||||
{
|
||||
_removeFunctionId((VoidPtr)obj,(VoidMethod)method);
|
||||
}
|
||||
|
||||
/// Journal a function's return value. The return value of the
|
||||
/// function is stored into the journal and retrieved during
|
||||
/// playback. During playback the function is not executed.
|
||||
#define Method(Func,Arg1,Arg2) \
|
||||
static typename ReturnType<Func>::ValueType Result Arg1 { \
|
||||
typename ReturnType<Func>::ValueType value; \
|
||||
if (_Dispatching) \
|
||||
return; \
|
||||
if (_State == PlayState) { \
|
||||
_start(); \
|
||||
IOHelper::reads(mFile,value); \
|
||||
_finish(); \
|
||||
return value; \
|
||||
} \
|
||||
_Dispatching = true; \
|
||||
value = (*func) Arg2; \
|
||||
_Dispatching = false; \
|
||||
if (_State == RecordState) { \
|
||||
_start(); \
|
||||
IOHelper::writes(mFile,value); \
|
||||
_finish(); \
|
||||
} \
|
||||
return value; \
|
||||
}
|
||||
|
||||
template<class Func,class A,class B,class C,class D,class E,class F, class G, class H, class I, class J>
|
||||
Method(Func,(Func func,A a,B b,C c,D d,E e,F f, G g, H h, I i, J j),(a,b,c,d,e,f,g,h,i,j))
|
||||
template<class Func,class A,class B,class C,class D,class E,class F, class G, class H, class I>
|
||||
Method(Func,(Func func,A a,B b,C c,D d,E e,F f, G g, H h, I i),(a,b,c,d,e,f,g,h,i))
|
||||
template<class Func,class A,class B,class C,class D,class E,class F, class G, class H>
|
||||
Method(Func,(Func func,A a,B b,C c,D d,E e,F f, G g, H h),(a,b,c,d,e,f,g,h))
|
||||
template<class Func,class A,class B,class C,class D,class E,class F, class G>
|
||||
Method(Func,(Func func,A a,B b,C c,D d,E e,F f, G g),(a,b,c,d,e,f,g))
|
||||
template<class Func,class A,class B,class C,class D,class E,class F>
|
||||
Method(Func,(Func func,A a,B b,C c,D d,E e,F f),(a,b,c,d,e,f))
|
||||
template<class Func,class A,class B,class C,class D,class E>
|
||||
Method(Func,(Func func,A a,B b,C c,D d,E e),(a,b,c,d,e))
|
||||
template<class Func,class A,class B,class C,class D>
|
||||
Method(Func,(Func func,A a,B b,C c,D d),(a,b,c,d))
|
||||
template<class Func,class A,class B,class C>
|
||||
Method(Func,(Func func,A a,B b,C c),(a,b,c))
|
||||
template<class Func,class A,class B>
|
||||
Method(Func,(Func func,A a,B b),(a,b))
|
||||
template<class Func,class A>
|
||||
Method(Func,(Func func,A a),(a))
|
||||
template<class Func>
|
||||
Method(Func,(Func func),())
|
||||
#undef Method
|
||||
|
||||
/// Journal a function call. Store the function id and all the
|
||||
/// function's arguments into the journal. On journal playback the
|
||||
/// function is executed with the retrieved arguments. The function
|
||||
/// must have been previously declared using the declareFunction()
|
||||
/// method.
|
||||
#define Method(Arg1,Arg2,Arg3) \
|
||||
static void Call Arg1 { \
|
||||
if (_Dispatching) \
|
||||
return; \
|
||||
if (_State == PlayState) \
|
||||
return; \
|
||||
if (_State == RecordState) { \
|
||||
Id id = _getFunctionId((VoidPtr)func,0); \
|
||||
AssertFatal(id,"Journal: Function must be be declared before being called"); \
|
||||
_start(); \
|
||||
IOHelper::writes Arg2; \
|
||||
_finish(); \
|
||||
} \
|
||||
_Dispatching = true; \
|
||||
(*func) Arg3; \
|
||||
_Dispatching = false; \
|
||||
return; \
|
||||
}
|
||||
|
||||
template<class Func,class A,class B,class C,class D,class E, class F, class G, class H, class I, class J>
|
||||
Method((Func func,A a,B b,C c,D d,E e,F f,G g,H h,I i,J j),(mFile,id,a,b,c,d,e,f,g,h,i,j),(a,b,c,d,e,f,g,h,i,j))
|
||||
template<class Func,class A,class B,class C,class D,class E, class F, class G, class H, class I>
|
||||
Method((Func func,A a,B b,C c,D d,E e,F f,G g,H h,I i),(mFile,id,a,b,c,d,e,f,g,h,i),(a,b,c,d,e,f,g,h,i))
|
||||
template<class Func,class A,class B,class C,class D,class E, class F, class G, class H>
|
||||
Method((Func func,A a,B b,C c,D d,E e,F f,G g,H h),(mFile,id,a,b,c,d,e,f,g,h),(a,b,c,d,e,f,g,h))
|
||||
template<class Func,class A,class B,class C,class D,class E, class F, class G>
|
||||
Method((Func func,A a,B b,C c,D d,E e,F f,G g),(mFile,id,a,b,c,d,e,f,g),(a,b,c,d,e,f,g))
|
||||
template<class Func,class A,class B,class C,class D,class E, class F>
|
||||
Method((Func func,A a,B b,C c,D d,E e,F f),(mFile,id,a,b,c,d,e,f),(a,b,c,d,e,f))
|
||||
template<class Func,class A,class B,class C,class D,class E>
|
||||
Method((Func func,A a,B b,C c,D d,E e),(mFile,id,a,b,c,d,e),(a,b,c,d,e))
|
||||
template<class Func,class A,class B,class C,class D>
|
||||
Method((Func func,A a,B b,C c,D d),(mFile,id,a,b,c,d),(a,b,c,d))
|
||||
template<class Func,class A,class B,class C>
|
||||
Method((Func func,A a,B b,C c),(mFile,id,a,b,c),(a,b,c))
|
||||
template<class Func,class A,class B>
|
||||
Method((Func func,A a,B b),(mFile,id,a,b),(a,b))
|
||||
template<class Func,class A>
|
||||
Method((Func func,A a),(mFile,id,a),(a))
|
||||
template<class Func>
|
||||
Method((Func func),(mFile,id),())
|
||||
#undef Method
|
||||
|
||||
#define Method(Arg1,Arg2,Arg3) \
|
||||
static void Call Arg1 { \
|
||||
if (_Dispatching) \
|
||||
return; \
|
||||
if (_State == PlayState) \
|
||||
return; \
|
||||
if (_State == RecordState) { \
|
||||
Id id = _getFunctionId((VoidPtr)obj,(VoidMethod)method); \
|
||||
AssertFatal(id != 0,"Journal: Function must be be declared before being called"); \
|
||||
_start(); \
|
||||
IOHelper::writes Arg2; \
|
||||
_finish(); \
|
||||
} \
|
||||
_Dispatching = true; \
|
||||
(obj->*method) Arg3; \
|
||||
_Dispatching = false; \
|
||||
return; \
|
||||
}
|
||||
|
||||
|
||||
template<class Obj,class A,class B,class C,class D,class E,class F,class G,class H,class I,class J>
|
||||
Method((Obj* obj,void (Obj::*method)(A,B,C,D,E,F,G,H,I,J),A a,B b,C c,D d,E e,F f,G g,H h,I i,J j),(mFile,id,a,b,c,d,e,f,g,h,i,j),(a,b,c,d,e,f,g,h,i,j))
|
||||
template<class Obj,class A,class B,class C,class D,class E,class F,class G,class H,class I>
|
||||
Method((Obj* obj,void (Obj::*method)(A,B,C,D,E,F,G,H,I),A a,B b,C c,D d,E e,F f,G g,H h,I i),(mFile,id,a,b,c,d,e,f,g,h,i),(a,b,c,d,e,f,g,h,i))
|
||||
template<class Obj,class A,class B,class C,class D,class E,class F,class G,class H>
|
||||
Method((Obj* obj,void (Obj::*method)(A,B,C,D,E,F,G,H),A a,B b,C c,D d,E e,F f,G g,H h),(mFile,id,a,b,c,d,e,f,g,h),(a,b,c,d,e,f,g,h))
|
||||
template<class Obj,class A,class B,class C,class D,class E,class F,class G>
|
||||
Method((Obj* obj,void (Obj::*method)(A,B,C,D,E,F,G),A a,B b,C c,D d,E e,F f,G g),(mFile,id,a,b,c,d,e,f,g),(a,b,c,d,e,f,g))
|
||||
template<class Obj,class A,class B,class C,class D,class E,class F>
|
||||
Method((Obj* obj,void (Obj::*method)(A,B,C,D,E,F),A a,B b,C c,D d,E e,F f),(mFile,id,a,b,c,d,e,f),(a,b,c,d,e,f))
|
||||
template<class Obj,class A,class B,class C,class D,class E>
|
||||
Method((Obj* obj,void (Obj::*method)(A,B,C,D,E),A a,B b,C c,D d,E e),(mFile,id,a,b,c,d,e),(a,b,c,d,e))
|
||||
template<class Obj,class A,class B,class C,class D>
|
||||
Method((Obj* obj,void (Obj::*method)(A,B,C,D),A a,B b,C c,D d),(mFile,id,a,b,c,d),(a,b,c,d))
|
||||
template<class Obj,class A,class B,class C>
|
||||
Method((Obj* obj,void (Obj::*method)(A,B,C),A a,B b,C c),(mFile,id,a,b,c),(a,b,c))
|
||||
template<class Obj,class A,class B>
|
||||
Method((Obj* obj,void (Obj::*method)(A,B),A a,B b),(mFile,id,a,b),(a,b))
|
||||
template<class Obj,class A>
|
||||
Method((Obj* obj,void (Obj::*method)(A),A a),(mFile,id,a),(a))
|
||||
template<class Obj>
|
||||
Method((Obj* obj,void (Obj::*method)()),(mFile,id),())
|
||||
|
||||
#undef Method
|
||||
|
||||
/// Write data into the journal. Non-deterministic data can be stored
|
||||
/// into the journal for reading during playback. The function
|
||||
/// returns true if the journal is record mode.
|
||||
#define Method(Arg1,Arg2) \
|
||||
static inline bool Writes Arg1 { \
|
||||
if (_State == RecordState) { \
|
||||
_start(); IOHelper::writes Arg2; _finish(); \
|
||||
return true; \
|
||||
} \
|
||||
return false; \
|
||||
}
|
||||
|
||||
template<class A,class B,class C,class D,class E, class F, class G, class H,class I,class J>
|
||||
Method((A& a,B& b,C& c,D& d,E& e, F& f, G& g, H& h, I& i,J& j),(mFile,a,b,c,d,e,f,g,h,i,j));
|
||||
template<class A,class B,class C,class D,class E, class F, class G, class H,class I>
|
||||
Method((A& a,B& b,C& c,D& d,E& e, F& f, G& g, H& h, I& i),(mFile,a,b,c,d,e,f,g,h,i));
|
||||
template<class A,class B,class C,class D,class E, class F, class G, class H>
|
||||
Method((A& a,B& b,C& c,D& d,E& e, F& f, G& g, H& h),(mFile,a,b,c,d,e,f,g,h));
|
||||
template<class A,class B,class C,class D,class E, class F, class G>
|
||||
Method((A& a,B& b,C& c,D& d,E& e, F& f, G& g),(mFile,a,b,c,d,e,f,g));
|
||||
template<class A,class B,class C,class D,class E, class F>
|
||||
Method((A& a,B& b,C& c,D& d,E& e, F& f),(mFile,a,b,c,d,e,f));
|
||||
template<class A,class B,class C,class D,class E>
|
||||
Method((A& a,B& b,C& c,D& d,E& e),(mFile,a,b,c,d,e));
|
||||
template<class A,class B,class C,class D>
|
||||
Method((A& a,B& b,C& c,D& d),(mFile,a,b,c,d));
|
||||
template<class A,class B,class C>
|
||||
Method((A& a,B& b,C& c),(mFile,a,b,c));
|
||||
template<class A,class B>
|
||||
Method((A& a,B& b),(mFile,a,b));
|
||||
template<class A>
|
||||
Method((A& a),(mFile,a));
|
||||
#undef Method
|
||||
|
||||
/// Read data from the journal. Read non-deterministic data stored
|
||||
/// during the recording phase. The function returns true if the
|
||||
/// journal is play mode.
|
||||
#define Method(Arg1,Arg2) \
|
||||
static inline bool Reads Arg1 { \
|
||||
if (_State == PlayState) { \
|
||||
_start(); IOHelper::reads Arg2; _finish(); \
|
||||
return true; \
|
||||
} \
|
||||
return false; \
|
||||
}
|
||||
|
||||
template<class A,class B,class C,class D,class E, class F, class G, class H, class I, class J>
|
||||
Method((A& a,B& b,C& c,D& d,E& e,F& f,G& g, H& h, I& i, J& j),(mFile,a,b,c,d,e,f,g,h,i,j));
|
||||
template<class A,class B,class C,class D,class E, class F, class G, class H, class I>
|
||||
Method((A& a,B& b,C& c,D& d,E& e,F& f,G& g, H& h, I& i),(mFile,a,b,c,d,e,f,g,h,i));
|
||||
template<class A,class B,class C,class D,class E, class F, class G, class H>
|
||||
Method((A& a,B& b,C& c,D& d,E& e,F& f,G& g, H& h),(mFile,a,b,c,d,e,f,g,h));
|
||||
template<class A,class B,class C,class D,class E, class F, class G>
|
||||
Method((A& a,B& b,C& c,D& d,E& e,F& f,G& g),(mFile,a,b,c,d,e,f,g));
|
||||
template<class A,class B,class C,class D,class E, class F>
|
||||
Method((A& a,B& b,C& c,D& d,E& e,F& f),(mFile,a,b,c,d,e,f));
|
||||
template<class A,class B,class C,class D,class E>
|
||||
Method((A& a,B& b,C& c,D& d,E& e),(mFile,a,b,c,d,e));
|
||||
template<class A,class B,class C,class D>
|
||||
Method((A& a,B& b,C& c,D& d),(mFile,a,b,c,d));
|
||||
template<class A,class B,class C>
|
||||
Method((A& a,B& b,C& c),(mFile,a,b,c));
|
||||
template<class A,class B>
|
||||
Method((A& a,B& b),(mFile,a,b));
|
||||
template<class A>
|
||||
Method((A& a),(mFile,a));
|
||||
|
||||
#undef Method
|
||||
};
|
||||
|
||||
#endif
|
||||
334
Engine/source/core/util/journal/journaledSignal.h
Normal file
334
Engine/source/core/util/journal/journaledSignal.h
Normal file
|
|
@ -0,0 +1,334 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _JOURNALEDSIGNAL_H_
|
||||
#define _JOURNALEDSIGNAL_H_
|
||||
|
||||
#ifndef _UTIL_JOURNAL_JOURNAL_H_
|
||||
#include "core/util/journal/journal.h"
|
||||
#endif
|
||||
#ifndef _TSIGNAL_H_
|
||||
#include "core/util/tSignal.h"
|
||||
#endif
|
||||
|
||||
template<typename Signature> class JournaledSignal;
|
||||
|
||||
|
||||
/// A specialized signal object for journaling input.
|
||||
/// @see Journal
|
||||
template<>
|
||||
class JournaledSignal<void()> : public Signal<void()>
|
||||
{
|
||||
typedef Signal<void()> Parent;
|
||||
|
||||
public:
|
||||
JournaledSignal()
|
||||
{
|
||||
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
~JournaledSignal()
|
||||
{
|
||||
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
/// Fires off the bound delegates.
|
||||
/// @see Journal::Call
|
||||
void trigger()
|
||||
{
|
||||
Journal::Call((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
};
|
||||
|
||||
/// @copydoc JournaledSignal<void()>
|
||||
template<class A>
|
||||
class JournaledSignal<void(A)> : public Signal<void(A)>
|
||||
{
|
||||
typedef Signal<void(A)> Parent;
|
||||
|
||||
public:
|
||||
JournaledSignal()
|
||||
{
|
||||
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
~JournaledSignal()
|
||||
{
|
||||
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
void trigger(A a)
|
||||
{
|
||||
Journal::Call((Parent*)this, &Parent::trigger, a);
|
||||
}
|
||||
};
|
||||
|
||||
/// @copydoc JournaledSignal<void()>
|
||||
template<class A, class B>
|
||||
class JournaledSignal<void(A,B)> : public Signal<void(A,B)>
|
||||
{
|
||||
typedef Signal<void(A,B)> Parent;
|
||||
|
||||
public:
|
||||
JournaledSignal()
|
||||
{
|
||||
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
~JournaledSignal()
|
||||
{
|
||||
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
void trigger(A a, B b)
|
||||
{
|
||||
Journal::Call((Parent*)this, &Parent::trigger, a, b);
|
||||
}
|
||||
};
|
||||
|
||||
/// @copydoc JournaledSignal<void()>
|
||||
template<class A, class B, class C>
|
||||
class JournaledSignal<void(A,B,C)> : public Signal<void(A,B,C)>
|
||||
{
|
||||
typedef Signal<void(A,B,C)> Parent;
|
||||
|
||||
public:
|
||||
JournaledSignal()
|
||||
{
|
||||
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
~JournaledSignal()
|
||||
{
|
||||
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
void trigger(A a, B b, C c)
|
||||
{
|
||||
Journal::Call((Parent*)this, &Parent::trigger, a, b, c);
|
||||
}
|
||||
};
|
||||
|
||||
/// @copydoc JournaledSignal<void()>
|
||||
template<class A, class B, class C, class D>
|
||||
class JournaledSignal<void(A,B,C,D)> : public Signal<void(A,B,C,D)>
|
||||
{
|
||||
typedef Signal<void(A,B,C,D)> Parent;
|
||||
|
||||
public:
|
||||
JournaledSignal()
|
||||
{
|
||||
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
~JournaledSignal()
|
||||
{
|
||||
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
void trigger(A a, B b, C c, D d)
|
||||
{
|
||||
Journal::Call((Parent*)this, &Parent::trigger, a, b, c, d);
|
||||
}
|
||||
};
|
||||
|
||||
/// @copydoc JournaledSignal<void()>
|
||||
template<class A, class B, class C, class D, class E>
|
||||
class JournaledSignal<void(A,B,C,D,E)> : public Signal<void(A,B,C,D,E)>
|
||||
{
|
||||
typedef Signal<void(A,B,C,D,E)> Parent;
|
||||
|
||||
public:
|
||||
JournaledSignal()
|
||||
{
|
||||
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
~JournaledSignal()
|
||||
{
|
||||
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
void trigger(A a, B b, C c, D d, E e)
|
||||
{
|
||||
Journal::Call((Parent*)this, &Parent::trigger, a, b, c, d, e);
|
||||
}
|
||||
};
|
||||
|
||||
/// @copydoc JournaledSignal<void()>
|
||||
template<class A, class B, class C, class D, class E, class F>
|
||||
class JournaledSignal<void(A,B,C,D,E,F)> : public Signal<void(A,B,C,D,E,F)>
|
||||
{
|
||||
typedef Signal<void(A,B,C,D,E,F)> Parent;
|
||||
|
||||
public:
|
||||
JournaledSignal()
|
||||
{
|
||||
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
~JournaledSignal()
|
||||
{
|
||||
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
void trigger(A a, B b, C c, D d, E e, F f)
|
||||
{
|
||||
Journal::Call((Parent*)this, &Parent::trigger, a, b, c, d, e, f);
|
||||
}
|
||||
};
|
||||
|
||||
/// @copydoc JournaledSignal<void()>
|
||||
template<class A, class B, class C, class D, class E, class F, class G>
|
||||
class JournaledSignal<void(A,B,C,D,E,F,G)> : public Signal<void(A,B,C,D,E,F,G)>
|
||||
{
|
||||
typedef Signal<void(A,B,C,D,E,F,G)> Parent;
|
||||
|
||||
public:
|
||||
JournaledSignal()
|
||||
{
|
||||
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
~JournaledSignal()
|
||||
{
|
||||
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
void trigger(A a, B b, C c, D d, E e, F f, G g)
|
||||
{
|
||||
Journal::Call((Parent*)this, &Parent::trigger, a, b, c, d, e, f, g);
|
||||
}
|
||||
};
|
||||
|
||||
/// @copydoc JournaledSignal<void()>
|
||||
template<class A, class B, class C, class D, class E, class F, class G, class H>
|
||||
class JournaledSignal<void(A,B,C,D,E,F,G,H)> : public Signal<void(A,B,C,D,E,F,G,H)>
|
||||
{
|
||||
typedef Signal<void(A,B,C,D,E,F,G,H)> Parent;
|
||||
|
||||
public:
|
||||
JournaledSignal()
|
||||
{
|
||||
Journal::DeclareFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
~JournaledSignal()
|
||||
{
|
||||
Journal::RemoveFunction((Parent*)this, &Parent::trigger);
|
||||
}
|
||||
|
||||
void trigger(A a, B b, C c, D d, E e, F f, G g, H h)
|
||||
{
|
||||
Journal::Call((Parent*)this, &Parent::trigger, a, b, c, d, e, f, g, h);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Common event callbacks definitions
|
||||
enum InputModifier {
|
||||
IM_LALT = (1 << 1),
|
||||
IM_RALT = (1 << 2),
|
||||
IM_LSHIFT = (1 << 3),
|
||||
IM_RSHIFT = (1 << 4),
|
||||
IM_LCTRL = (1 << 5),
|
||||
IM_RCTRL = (1 << 6),
|
||||
IM_LOPT = (1 << 7),
|
||||
IM_ROPT = (1 << 8),
|
||||
IM_ALT = IM_LALT | IM_RALT,
|
||||
IM_SHIFT = IM_LSHIFT | IM_RSHIFT,
|
||||
IM_CTRL = IM_LCTRL | IM_RCTRL,
|
||||
IM_OPT = IM_LOPT | IM_ROPT,
|
||||
};
|
||||
|
||||
enum InputAction {
|
||||
IA_MAKE = (1 << 0),
|
||||
IA_BREAK = (1 << 1),
|
||||
IA_REPEAT = (1 << 2),
|
||||
IA_MOVE = (1 << 3),
|
||||
IA_DELTA = (1 << 4),
|
||||
IA_BUTTON = (1 << 5),
|
||||
};
|
||||
|
||||
enum ApplicationMessage {
|
||||
Quit,
|
||||
WindowOpen, ///< Window opened
|
||||
WindowClose, ///< Window closed.
|
||||
WindowShown, ///< Window has been shown on screen
|
||||
WindowHidden, ///< Window has become hidden
|
||||
WindowDestroy, ///< Window was destroyed.
|
||||
GainCapture, ///< Window will capture all input
|
||||
LoseCapture, ///< Window will no longer capture all input
|
||||
GainFocus, ///< Application gains focus
|
||||
LoseFocus, ///< Application loses focus
|
||||
DisplayChange, ///< Desktop Display mode has changed
|
||||
GainScreen, ///< Window will acquire lock on the full screen
|
||||
LoseScreen, ///< Window has released lock on the full screen
|
||||
Timer,
|
||||
};
|
||||
|
||||
typedef U32 WindowId;
|
||||
|
||||
/// void event()
|
||||
typedef JournaledSignal<void()> IdleEvent;
|
||||
|
||||
/// void event(WindowId,U32 modifier,S32 x,S32 y, bool isRelative)
|
||||
typedef JournaledSignal<void(WindowId,U32,S32,S32,bool)> MouseEvent;
|
||||
|
||||
/// void event(WindowId,U32 modifier,S32 wheelDeltaX, S32 wheelDeltaY)
|
||||
typedef JournaledSignal<void(WindowId,U32,S32,S32)> MouseWheelEvent;
|
||||
|
||||
/// void event(WindowId,U32 modifier,U32 action,U16 key)
|
||||
typedef JournaledSignal<void(WindowId,U32,U32,U16)> KeyEvent;
|
||||
|
||||
/// void event(WindowId,U32 modifier,U16 key)
|
||||
typedef JournaledSignal<void(WindowId,U32,U16)> CharEvent;
|
||||
|
||||
/// void event(WindowId,U32 modifier,U32 action,U16 button)
|
||||
typedef JournaledSignal<void(WindowId,U32,U32,U16)> ButtonEvent;
|
||||
|
||||
/// void event(WindowId,U32 modifier,U32 action,U32 axis,F32 value)
|
||||
typedef JournaledSignal<void(WindowId,U32,U32,U32,F32)> LinearEvent;
|
||||
|
||||
/// void event(WindowId,U32 modifier,F32 value)
|
||||
typedef JournaledSignal<void(WindowId,U32,F32)> PovEvent;
|
||||
|
||||
/// void event(WindowId,InputAppMessage)
|
||||
typedef JournaledSignal<void(WindowId,S32)> AppEvent;
|
||||
|
||||
/// void event(WindowId)
|
||||
typedef JournaledSignal<void(WindowId)> DisplayEvent;
|
||||
|
||||
/// void event(WindowId, S32 width, S32 height)
|
||||
typedef JournaledSignal<void(WindowId, S32, S32)> ResizeEvent;
|
||||
|
||||
/// void event(S32 timeDelta)
|
||||
typedef JournaledSignal<void(S32)> TimeManagerEvent;
|
||||
|
||||
// void event(U32 deviceInst,F32 fValue, U16 deviceType, U16 objType, U16 ascii, U16 objInst, U8 action, U8 modifier)
|
||||
typedef JournaledSignal<void(U32,F32,U16,U16,U16,U16,U8,U8)> InputEvent;
|
||||
|
||||
/// void event(U32 popupGUID, U32 commandID, bool& returnValue)
|
||||
typedef JournaledSignal<void(U32, U32)> PopupMenuEvent;
|
||||
|
||||
#endif
|
||||
110
Engine/source/core/util/journal/process.cpp
Normal file
110
Engine/source/core/util/journal/process.cpp
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/util/journal/process.h"
|
||||
#include "core/util/journal/journal.h"
|
||||
#include "core/module.h"
|
||||
|
||||
|
||||
MODULE_BEGIN( Process )
|
||||
|
||||
MODULE_INIT
|
||||
{
|
||||
Process::init();
|
||||
}
|
||||
|
||||
MODULE_SHUTDOWN
|
||||
{
|
||||
Process::shutdown();
|
||||
}
|
||||
|
||||
MODULE_END;
|
||||
|
||||
static Process* _theOneProcess = NULL; ///< the one instance of the Process class
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void Process::requestShutdown()
|
||||
{
|
||||
Process::get()._RequestShutdown = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Process::Process()
|
||||
: _RequestShutdown( false )
|
||||
{
|
||||
}
|
||||
|
||||
Process &Process::get()
|
||||
{
|
||||
struct Cleanup
|
||||
{
|
||||
~Cleanup()
|
||||
{
|
||||
if( _theOneProcess )
|
||||
delete _theOneProcess;
|
||||
}
|
||||
};
|
||||
static Cleanup cleanup;
|
||||
|
||||
// NOTE that this function is not thread-safe
|
||||
// To make it thread safe, use the double-checked locking mechanism for singleton objects
|
||||
|
||||
if ( !_theOneProcess )
|
||||
_theOneProcess = new Process;
|
||||
|
||||
return *_theOneProcess;
|
||||
}
|
||||
|
||||
bool Process::init()
|
||||
{
|
||||
return Process::get()._signalInit.trigger();
|
||||
}
|
||||
|
||||
void Process::handleCommandLine(S32 argc, const char **argv)
|
||||
{
|
||||
Process::get()._signalCommandLine.trigger(argc, argv);
|
||||
}
|
||||
|
||||
bool Process::processEvents()
|
||||
{
|
||||
// Process all the devices. We need to call these even during journal
|
||||
// playback to ensure that the OS event queues are serviced.
|
||||
Process::get()._signalProcess.trigger();
|
||||
|
||||
if (!Process::get()._RequestShutdown)
|
||||
{
|
||||
if (Journal::IsPlaying())
|
||||
return Journal::PlayNext();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reset the Quit flag so the function can be called again.
|
||||
Process::get()._RequestShutdown = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Process::shutdown()
|
||||
{
|
||||
return Process::get()._signalShutdown.trigger();
|
||||
}
|
||||
193
Engine/source/core/util/journal/process.h
Normal file
193
Engine/source/core/util/journal/process.h
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _UTIL_JOURNAL_PROCESS_H_
|
||||
#define _UTIL_JOURNAL_PROCESS_H_
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/util/tVector.h"
|
||||
#include "core/util/delegate.h"
|
||||
#include "core/util/tSignal.h"
|
||||
|
||||
|
||||
#define PROCESS_FIRST_ORDER 0.0f
|
||||
#define PROCESS_NET_ORDER 0.35f
|
||||
#define PROCESS_INPUT_ORDER 0.4f
|
||||
#define PROCESS_DEFAULT_ORDER 0.5f
|
||||
#define PROCESS_TIME_ORDER 0.75f
|
||||
#define PROCESS_RENDER_ORDER 0.8f
|
||||
#define PROCESS_LAST_ORDER 1.0f
|
||||
|
||||
class StandardMainLoop;
|
||||
|
||||
|
||||
/// Event generation signal.
|
||||
///
|
||||
/// Objects that generate events need to register a callback with
|
||||
/// this signal and should only generate events from within the callback.
|
||||
///
|
||||
/// This signal is triggered from the ProcessEvents() method.
|
||||
class Process
|
||||
{
|
||||
public:
|
||||
/// Trigger the ProcessSignal and replay journal events.
|
||||
///
|
||||
/// The ProcessSignal is triggered during which all events are generated,
|
||||
/// journaled, and delivered using the EventSignal classes. Event producers should
|
||||
/// only generate events from within the function they register with ProcessSignal.
|
||||
/// ProcessSignal is also triggered during event playback, though all new events are
|
||||
/// thrown away so as not to interfere with journal playback.
|
||||
/// This function returns false if Process::requestShutdown() has been called, otherwise it
|
||||
/// returns true.
|
||||
///
|
||||
/// NOTE: This should only be called from main loops - it should really be private,
|
||||
/// but we need to sort out how to handle the unit test cases
|
||||
static bool processEvents();
|
||||
|
||||
/// Ask the processEvents() function to shutdown.
|
||||
static void requestShutdown();
|
||||
|
||||
|
||||
static void notifyInit(Delegate<bool()> del, F32 order = PROCESS_DEFAULT_ORDER)
|
||||
{
|
||||
get()._signalInit.notify(del,order);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void notifyInit(T func, F32 order = PROCESS_DEFAULT_ORDER)
|
||||
{
|
||||
get()._signalInit.notify(func,order);
|
||||
}
|
||||
|
||||
|
||||
static void notifyCommandLine(Delegate<void(S32, const char **)> del, F32 order = PROCESS_DEFAULT_ORDER)
|
||||
{
|
||||
get()._signalCommandLine.notify(del,order);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void notifyCommandLine(T func, F32 order = PROCESS_DEFAULT_ORDER)
|
||||
{
|
||||
get()._signalCommandLine.notify(func,order);
|
||||
}
|
||||
|
||||
|
||||
static void notify(Delegate<void()> del, F32 order = PROCESS_DEFAULT_ORDER)
|
||||
{
|
||||
get()._signalProcess.notify(del,order);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void notify(T func, F32 order = PROCESS_DEFAULT_ORDER)
|
||||
{
|
||||
get()._signalProcess.notify(func,order);
|
||||
}
|
||||
|
||||
template <class T,class U>
|
||||
static void notify(T obj,U func, F32 order = PROCESS_DEFAULT_ORDER)
|
||||
{
|
||||
get()._signalProcess.notify(obj,func,order);
|
||||
}
|
||||
|
||||
|
||||
static void remove(Delegate<void()> del)
|
||||
{
|
||||
get()._signalProcess.remove(del);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void remove(T func)
|
||||
{
|
||||
get()._signalProcess.remove(func);
|
||||
}
|
||||
|
||||
template <class T,class U>
|
||||
static void remove(T obj,U func)
|
||||
{
|
||||
get()._signalProcess.remove(obj,func);
|
||||
}
|
||||
|
||||
|
||||
static void notifyShutdown(Delegate<bool(void)> del, F32 order = PROCESS_DEFAULT_ORDER)
|
||||
{
|
||||
get()._signalShutdown.notify(del,order);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void notifyShutdown(T func, F32 order = PROCESS_DEFAULT_ORDER)
|
||||
{
|
||||
get()._signalShutdown.notify(func,order);
|
||||
}
|
||||
|
||||
/// Trigger the registered init functions
|
||||
static bool init();
|
||||
|
||||
/// Trigger the registered shutdown functions
|
||||
static bool shutdown();
|
||||
|
||||
private:
|
||||
friend class StandardMainLoop;
|
||||
|
||||
/// Trigger the registered command line handling functions
|
||||
static void handleCommandLine(S32 argc, const char **argv);
|
||||
|
||||
/// Private constructor
|
||||
Process();
|
||||
|
||||
/// Access method will construct the singleton as necessary
|
||||
static Process &get();
|
||||
|
||||
Signal<bool()> _signalInit;
|
||||
Signal<void(S32, const char **)> _signalCommandLine;
|
||||
Signal<void()> _signalProcess;
|
||||
Signal<bool()> _signalShutdown;
|
||||
|
||||
bool _RequestShutdown;
|
||||
};
|
||||
|
||||
/// Register a command line handling function.
|
||||
///
|
||||
/// To use this, put it as a member variable into your module definition.
|
||||
class ProcessRegisterCommandLine
|
||||
{
|
||||
public:
|
||||
template <class T>
|
||||
ProcessRegisterCommandLine( T func, F32 order = PROCESS_DEFAULT_ORDER )
|
||||
{
|
||||
Process::notifyCommandLine( func, order );
|
||||
}
|
||||
};
|
||||
|
||||
/// Register a processing function
|
||||
///
|
||||
/// To use this, put it as a member variable into your module definition.
|
||||
class ProcessRegisterProcessing
|
||||
{
|
||||
public:
|
||||
template <class T>
|
||||
ProcessRegisterProcessing( T func, F32 order = PROCESS_DEFAULT_ORDER )
|
||||
{
|
||||
Process::notify( func, order );
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
173
Engine/source/core/util/journal/test/testJournal.cpp
Normal file
173
Engine/source/core/util/journal/test/testJournal.cpp
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "unit/test.h"
|
||||
#include "core/util/journal/journaledSignal.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
|
||||
using namespace UnitTesting;
|
||||
|
||||
CreateUnitTest(TestsJournalRecordAndPlayback, "Journal/Basic")
|
||||
{
|
||||
U32 _lastTriggerValue;
|
||||
|
||||
void triggerReceiver(U16 msg)
|
||||
{
|
||||
_lastTriggerValue = msg;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
// Reset the last trigger value just in case...
|
||||
_lastTriggerValue = 0;
|
||||
|
||||
// Set up a journaled signal to test with.
|
||||
JournaledSignal<void(U16)> testEvent;
|
||||
|
||||
testEvent.notify(this, &TestsJournalRecordAndPlayback::triggerReceiver);
|
||||
|
||||
// Initialize journal recording and fire off some events...
|
||||
Journal::Record("test.jrn");
|
||||
|
||||
testEvent.trigger(16);
|
||||
testEvent.trigger(17);
|
||||
testEvent.trigger(18);
|
||||
|
||||
test(_lastTriggerValue == 18, "Should encounter last triggered value (18).");
|
||||
|
||||
Journal::Stop();
|
||||
|
||||
// Clear it...
|
||||
_lastTriggerValue = 0;
|
||||
|
||||
// and play back - should get same thing.
|
||||
Journal::Play("test.jrn");
|
||||
|
||||
// Since we fired 3 events, it should take three loops.
|
||||
test(Journal::PlayNext(), "Should be two more events.");
|
||||
test(Journal::PlayNext(), "Should be one more event.");
|
||||
test(!Journal::PlayNext(), "Should be no more events.");
|
||||
|
||||
test(_lastTriggerValue == 18, "Should encounter last journaled value (18).");
|
||||
}
|
||||
};
|
||||
|
||||
CreateUnitTest(TestsJournalDynamicSignals, "Journal/DynamicSignals")
|
||||
{
|
||||
typedef JournaledSignal<void(U32, U16)> EventA;
|
||||
typedef JournaledSignal<void(U8, S8)> EventB;
|
||||
typedef JournaledSignal<void(U32, S32)> EventC;
|
||||
|
||||
EventA *dynamicA;
|
||||
EventB *dynamicB;
|
||||
EventC *dynamicC;
|
||||
|
||||
// Root, non-dynamic signal receiver.
|
||||
void receiverRoot(U8 msg)
|
||||
{
|
||||
if(msg==1)
|
||||
{
|
||||
dynamicA = new EventA();
|
||||
dynamicA->notify(this, &TestsJournalDynamicSignals::receiverA);
|
||||
}
|
||||
|
||||
if(msg==2)
|
||||
{
|
||||
dynamicB = new EventB();
|
||||
dynamicB->notify(this, &TestsJournalDynamicSignals::receiverB);
|
||||
}
|
||||
|
||||
if(msg==3)
|
||||
{
|
||||
dynamicC = new EventC();
|
||||
dynamicC->notify(this, &TestsJournalDynamicSignals::receiverC);
|
||||
}
|
||||
}
|
||||
|
||||
U32 recvA, recvB, recvC;
|
||||
|
||||
void receiverA(U32, U16 d)
|
||||
{
|
||||
recvA += d;
|
||||
}
|
||||
|
||||
void receiverB(U8, S8 d)
|
||||
{
|
||||
recvB += d;
|
||||
}
|
||||
|
||||
void receiverC(U32, S32 d)
|
||||
{
|
||||
recvC += d;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
// Reset our state values.
|
||||
recvA = recvB = recvC = 0;
|
||||
|
||||
// Set up a signal to start with.
|
||||
JournaledSignal<void(U8)> testEvent;
|
||||
testEvent.notify(this, &TestsJournalDynamicSignals::receiverRoot);
|
||||
|
||||
// Initialize journal recording and fire off some events...
|
||||
Journal::Record("test.jrn");
|
||||
|
||||
testEvent.trigger(1);
|
||||
dynamicA->trigger(8, 100);
|
||||
testEvent.trigger(2);
|
||||
dynamicA->trigger(8, 8);
|
||||
dynamicB->trigger(9, 'a');
|
||||
testEvent.trigger(3);
|
||||
SAFE_DELETE(dynamicB); // Test a deletion.
|
||||
dynamicC->trigger(8, 1);
|
||||
dynamicC->trigger(8, 1);
|
||||
|
||||
// Did we end up with expected values? Check before clearing.
|
||||
test(recvA == 108, "recvA wasn't 108 - something broken in signals?");
|
||||
test(recvB == 'a', "recvB wasn't 'a' - something broken in signals?");
|
||||
test(recvC == 2, "recvC wasn't 2 - something broken in signals?");
|
||||
|
||||
// Reset our state values.
|
||||
recvA = recvB = recvC = 0;
|
||||
|
||||
// And kill the journal...
|
||||
Journal::Stop();
|
||||
|
||||
// Also kill our remaining dynamic signals.
|
||||
SAFE_DELETE(dynamicA);
|
||||
SAFE_DELETE(dynamicB);
|
||||
SAFE_DELETE(dynamicC);
|
||||
|
||||
// Play back - should get same thing.
|
||||
Journal::Play("test.jrn");
|
||||
|
||||
// Since we fired 8 events, it should take 7+1=8 loops.
|
||||
for(S32 i=0; i<7; i++)
|
||||
test(Journal::PlayNext(), "Should be more events.");
|
||||
test(!Journal::PlayNext(), "Should be no more events.");
|
||||
|
||||
test(recvA == 108, "recvA wasn't 108 - something broken in journal?");
|
||||
test(recvB == 'a', "recvB wasn't 'a' - something broken in journal?");
|
||||
test(recvC == 2, "recvC wasn't 2 - something broken in journal?");
|
||||
}
|
||||
};
|
||||
56
Engine/source/core/util/journal/test/testProcess.cpp
Normal file
56
Engine/source/core/util/journal/test/testProcess.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "unit/test.h"
|
||||
#include "core/util/journal/process.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
|
||||
using namespace UnitTesting;
|
||||
|
||||
CreateUnitTest(TestingProcess, "Journal/Process")
|
||||
{
|
||||
// How many ticks remaining?
|
||||
U32 _remainingTicks;
|
||||
|
||||
// Callback for process list.
|
||||
void process()
|
||||
{
|
||||
if(_remainingTicks==0)
|
||||
Process::requestShutdown();
|
||||
|
||||
_remainingTicks--;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
// We'll run 30 ticks, then quit.
|
||||
_remainingTicks = 30;
|
||||
|
||||
// Register with the process list.
|
||||
Process::notify(this, &TestingProcess::process);
|
||||
|
||||
// And do 30 notifies, making sure we end on the 30th.
|
||||
for(S32 i=0; i<30; i++)
|
||||
test(Process::processEvents(), "Should quit after 30 ProcessEvents() calls - not before!");
|
||||
test(!Process::processEvents(), "Should quit after the 30th ProcessEvent() call!");
|
||||
}
|
||||
};
|
||||
259
Engine/source/core/util/md5.cpp
Normal file
259
Engine/source/core/util/md5.cpp
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
* This code implements the MD5 message-digest algorithm.
|
||||
* The algorithm is due to Ron Rivest. This code was
|
||||
* written by Colin Plumb in 1993, no copyright is claimed.
|
||||
* This code is in the public domain; do with it what you wish.
|
||||
*
|
||||
* Equivalent code is available from RSA Data Security, Inc.
|
||||
* This code has been tested against that, and is equivalent,
|
||||
* except that you don't need to include two pages of legalese
|
||||
* with every copy.
|
||||
*
|
||||
* To compute the message digest of a chunk of bytes, declare an
|
||||
* MD5Context structure, pass it to MD5Init, call MD5Update as
|
||||
* needed on buffers full of bytes, and then call MD5Final, which
|
||||
* will fill a supplied 16-byte array with the digest.
|
||||
*/
|
||||
|
||||
/* Brutally hacked by John Walker back from ANSI C to K&R (no
|
||||
prototypes) to maintain the tradition that Netfone will compile
|
||||
with Sun's original "cc". */
|
||||
|
||||
#include <memory.h> /* for memcpy() */
|
||||
#include "md5.h"
|
||||
|
||||
#ifdef sgi
|
||||
#define HIGHFIRST
|
||||
#endif
|
||||
|
||||
#ifdef sun
|
||||
#define HIGHFIRST
|
||||
#endif
|
||||
|
||||
#ifndef HIGHFIRST
|
||||
#define byteReverse(buf, len) /* Nothing */
|
||||
#else
|
||||
/*
|
||||
* Note: this code is harmless on little-endian machines.
|
||||
*/
|
||||
void byteReverse(buf, longs)
|
||||
unsigned char *buf; unsigned longs;
|
||||
{
|
||||
int t;
|
||||
do {
|
||||
t = (int) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
|
||||
((unsigned) buf[1] << 8 | buf[0]);
|
||||
*(int *) buf = t;
|
||||
buf += 4;
|
||||
} while (--longs);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
|
||||
* initialization constants.
|
||||
*/
|
||||
void MD5Init( MD5Context* ctx)
|
||||
{
|
||||
ctx->buf[0] = 0x67452301;
|
||||
ctx->buf[1] = 0xefcdab89;
|
||||
ctx->buf[2] = 0x98badcfe;
|
||||
ctx->buf[3] = 0x10325476;
|
||||
|
||||
ctx->bits[0] = 0;
|
||||
ctx->bits[1] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update context to reflect the concatenation of another buffer full
|
||||
* of bytes.
|
||||
*/
|
||||
void MD5Update( MD5Context* ctx, unsigned char* buf, unsigned int len)
|
||||
{
|
||||
int t;
|
||||
|
||||
/* Update bitcount */
|
||||
|
||||
t = ctx->bits[0];
|
||||
if ((ctx->bits[0] = t + ((int) len << 3)) < t)
|
||||
ctx->bits[1]++; /* Carry from low to high */
|
||||
ctx->bits[1] += len >> 29;
|
||||
|
||||
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
|
||||
|
||||
/* Handle any leading odd-sized chunks */
|
||||
|
||||
if (t) {
|
||||
unsigned char *p = (unsigned char *) ctx->in + t;
|
||||
|
||||
t = 64 - t;
|
||||
if (len < t) {
|
||||
memcpy(p, buf, len);
|
||||
return;
|
||||
}
|
||||
memcpy(p, buf, t);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (int *) ctx->in);
|
||||
buf += t;
|
||||
len -= t;
|
||||
}
|
||||
/* Process data in 64-byte chunks */
|
||||
|
||||
while (len >= 64) {
|
||||
memcpy(ctx->in, buf, 64);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (int *) ctx->in);
|
||||
buf += 64;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
/* Handle any remaining bytes of data. */
|
||||
|
||||
memcpy(ctx->in, buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Final wrapup - pad to 64-byte boundary with the bit pattern
|
||||
* 1 0* (64-bit count of bits processed, MSB-first)
|
||||
*/
|
||||
void MD5Final( unsigned char digest[16], MD5Context* ctx)
|
||||
{
|
||||
unsigned count;
|
||||
unsigned char *p;
|
||||
|
||||
/* Compute number of bytes mod 64 */
|
||||
count = (ctx->bits[0] >> 3) & 0x3F;
|
||||
|
||||
/* Set the first char of padding to 0x80. This is safe since there is
|
||||
always at least one byte free */
|
||||
p = ctx->in + count;
|
||||
*p++ = 0x80;
|
||||
|
||||
/* Bytes of padding needed to make 64 bytes */
|
||||
count = 64 - 1 - count;
|
||||
|
||||
/* Pad out to 56 mod 64 */
|
||||
if (count < 8) {
|
||||
/* Two lots of padding: Pad the first block to 64 bytes */
|
||||
memset(p, 0, count);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (int *) ctx->in);
|
||||
|
||||
/* Now fill the next block with 56 bytes */
|
||||
memset(ctx->in, 0, 56);
|
||||
} else {
|
||||
/* Pad block to 56 bytes */
|
||||
memset(p, 0, count - 8);
|
||||
}
|
||||
byteReverse(ctx->in, 14);
|
||||
|
||||
/* Append length in bits and transform */
|
||||
((int *) ctx->in)[14] = ctx->bits[0];
|
||||
((int *) ctx->in)[15] = ctx->bits[1];
|
||||
|
||||
MD5Transform(ctx->buf, (int *) ctx->in);
|
||||
byteReverse((unsigned char *) ctx->buf, 4);
|
||||
memcpy(digest, ctx->buf, 16);
|
||||
memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
|
||||
}
|
||||
|
||||
|
||||
/* The four core functions - F1 is optimized somewhat */
|
||||
|
||||
/* #define F1(x, y, z) (x & y | ~x & z) */
|
||||
#define F1(x, y, z) (z ^ (x & (y ^ z)))
|
||||
#define F2(x, y, z) F1(z, x, y)
|
||||
#define F3(x, y, z) (x ^ y ^ z)
|
||||
#define F4(x, y, z) (y ^ (x | ~z))
|
||||
|
||||
/* This is the central step in the MD5 algorithm. */
|
||||
#define MD5STEP(f, w, x, y, z, data, s) \
|
||||
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
|
||||
|
||||
/*
|
||||
* The core of the MD5 algorithm, this alters an existing MD5 hash to
|
||||
* reflect the addition of 16 longwords of new data. MD5Update blocks
|
||||
* the data and converts bytes into longwords for this routine.
|
||||
*/
|
||||
void MD5Transform( int buf[4], int in[16])
|
||||
{
|
||||
register int a, b, c, d;
|
||||
|
||||
a = buf[0];
|
||||
b = buf[1];
|
||||
c = buf[2];
|
||||
d = buf[3];
|
||||
|
||||
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
|
||||
|
||||
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
|
||||
|
||||
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
|
||||
|
||||
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
|
||||
|
||||
buf[0] += a;
|
||||
buf[1] += b;
|
||||
buf[2] += c;
|
||||
buf[3] += d;
|
||||
}
|
||||
18
Engine/source/core/util/md5.h
Normal file
18
Engine/source/core/util/md5.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef MD5_H
|
||||
#define MD5_H
|
||||
|
||||
|
||||
struct MD5Context {
|
||||
int buf[4];
|
||||
int bits[2];
|
||||
unsigned char in[64];
|
||||
};
|
||||
|
||||
extern void MD5Init(struct MD5Context *ctx);
|
||||
extern void MD5Update(struct MD5Context *ctx, unsigned char *buf, unsigned len);
|
||||
extern void MD5Final(unsigned char digest[16], struct MD5Context *ctx);
|
||||
extern void MD5Transform(int but[4], int in[16]);
|
||||
|
||||
typedef MD5Context MD5_CTX;
|
||||
|
||||
#endif /* !MD5_H */
|
||||
145
Engine/source/core/util/namedSingleton.h
Normal file
145
Engine/source/core/util/namedSingleton.h
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TORQUE_CORE_UTIL_NAMEDSINGLETON_H_
|
||||
#define _TORQUE_CORE_UTIL_NAMEDSINGLETON_H_
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/util/safeCast.h"
|
||||
#include "console/console.h"
|
||||
#include "core/stringTable.h"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// StaticNamedSingleton.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Collection of statically registered named singletons.
|
||||
///
|
||||
/// This class is useful as a mix-in for classes that are supposed to
|
||||
/// represent a range of named singleton instances from which a specific
|
||||
/// instance is then selected at run-time.
|
||||
///
|
||||
/// @param T Arbitrary type parameter; identical types will share
|
||||
/// static data.
|
||||
|
||||
template< class T >
|
||||
struct StaticNamedSingleton
|
||||
{
|
||||
typedef StaticNamedSingleton This;
|
||||
|
||||
StaticNamedSingleton( const char* name );
|
||||
virtual ~StaticNamedSingleton() {}
|
||||
|
||||
const char* getName();
|
||||
T* getNext();
|
||||
|
||||
static T* staticGetFirst();
|
||||
static T* staticFindSingleton( const char* name );
|
||||
static EnumTable* staticCreateEnumTable();
|
||||
static U32 staticGetNumSingletons();
|
||||
|
||||
private:
|
||||
const char* mName;
|
||||
This* mNext;
|
||||
|
||||
static This* smSingletons;
|
||||
};
|
||||
|
||||
template< class T >
|
||||
StaticNamedSingleton< T >* StaticNamedSingleton< T >::smSingletons;
|
||||
|
||||
template< class T >
|
||||
StaticNamedSingleton< T >::StaticNamedSingleton( const char* name )
|
||||
: mName( name )
|
||||
{
|
||||
mNext = smSingletons;
|
||||
smSingletons = this;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline const char* StaticNamedSingleton< T >::getName()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline T* StaticNamedSingleton< T >::getNext()
|
||||
{
|
||||
return static_cast< T* >( mNext );
|
||||
}
|
||||
|
||||
template< class T >
|
||||
T* StaticNamedSingleton< T >::staticGetFirst()
|
||||
{
|
||||
return static_cast< T* >( smSingletons );
|
||||
}
|
||||
|
||||
/// Find the instance with the given name. Returns NULL if no such
|
||||
/// instance exists.
|
||||
|
||||
template< class T >
|
||||
T* StaticNamedSingleton< T >::staticFindSingleton( const char* name )
|
||||
{
|
||||
for( This* ptr = smSingletons; ptr != 0; ptr = ptr->mNext )
|
||||
if( dStricmp( name, ptr->mName ) == 0 )
|
||||
return static_cast< T* >( ptr );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Create a TorqueScript EnumTable that contains all registered
|
||||
/// instance names.
|
||||
|
||||
template< class T >
|
||||
EnumTable* StaticNamedSingleton< T >::staticCreateEnumTable()
|
||||
{
|
||||
U32 numSingletons = staticGetNumSingletons();
|
||||
|
||||
// Create the enums.
|
||||
|
||||
EnumTable::Enums* enums = new EnumTable::Enums[ numSingletons ];
|
||||
This* ptr = smSingletons;
|
||||
for( U32 i = 0; i < numSingletons; ++ i )
|
||||
{
|
||||
enums[ i ].index = i;
|
||||
enums[ i ].label = StringTable->insert( ptr->getName() );
|
||||
|
||||
ptr = ptr->mNext;
|
||||
}
|
||||
|
||||
// Create the table.
|
||||
|
||||
return new EnumTable( numSingletons, enums );
|
||||
}
|
||||
|
||||
/// Return the number of registered named singletons.
|
||||
|
||||
template< class T >
|
||||
U32 StaticNamedSingleton< T >::staticGetNumSingletons()
|
||||
{
|
||||
U32 numSingletons = 0;
|
||||
for( This* ptr = smSingletons; ptr != 0; ptr = ptr->mNext )
|
||||
numSingletons ++;
|
||||
return numSingletons;
|
||||
}
|
||||
|
||||
#endif // _TORQUE_CORE_UTIL_NAMEDSINGLETON_H_
|
||||
37
Engine/source/core/util/noncopyable.h
Normal file
37
Engine/source/core/util/noncopyable.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CORE_NONCOPYABLE_H_
|
||||
#define _CORE_NONCOPYABLE_H_
|
||||
|
||||
class Noncopyable
|
||||
{
|
||||
protected:
|
||||
Noncopyable() {}
|
||||
~Noncopyable() {}
|
||||
|
||||
private:
|
||||
Noncopyable(const Noncopyable&);
|
||||
const Noncopyable& operator=(const Noncopyable&);
|
||||
};
|
||||
|
||||
#endif
|
||||
436
Engine/source/core/util/path.cpp
Normal file
436
Engine/source/core/util/path.cpp
Normal 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
|
||||
|
||||
147
Engine/source/core/util/path.h
Normal file
147
Engine/source/core/util/path.h
Normal 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
|
||||
|
||||
38
Engine/source/core/util/preprocessorHelpers.h
Normal file
38
Engine/source/core/util/preprocessorHelpers.h
Normal 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
|
||||
157
Engine/source/core/util/rawData.h
Normal file
157
Engine/source/core/util/rawData.h
Normal 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_
|
||||
473
Engine/source/core/util/refBase.h
Normal file
473
Engine/source/core/util/refBase.h
Normal 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
|
||||
58
Engine/source/core/util/rgb2luv.cpp
Normal file
58
Engine/source/core/util/rgb2luv.cpp
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
||||
37
Engine/source/core/util/rgb2luv.h
Normal file
37
Engine/source/core/util/rgb2luv.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _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
|
||||
67
Engine/source/core/util/rgb2xyz.cpp
Normal file
67
Engine/source/core/util/rgb2xyz.cpp
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
||||
34
Engine/source/core/util/rgb2xyz.h
Normal file
34
Engine/source/core/util/rgb2xyz.h
Normal 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
|
||||
60
Engine/source/core/util/safeCast.h
Normal file
60
Engine/source/core/util/safeCast.h
Normal 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_
|
||||
94
Engine/source/core/util/safeDelete.h
Normal file
94
Engine/source/core/util/safeDelete.h
Normal 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_
|
||||
|
||||
25
Engine/source/core/util/safeRelease.h
Normal file
25
Engine/source/core/util/safeRelease.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef SAFE_RELEASE
|
||||
# define SAFE_RELEASE(x) if( x != NULL ) { x->Release(); x = NULL; }
|
||||
#endif
|
||||
1622
Engine/source/core/util/str.cpp
Normal file
1622
Engine/source/core/util/str.cpp
Normal file
File diff suppressed because it is too large
Load diff
399
Engine/source/core/util/str.h
Normal file
399
Engine/source/core/util/str.h
Normal 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
|
||||
|
||||
164
Engine/source/core/util/swizzle.h
Normal file
164
Engine/source/core/util/swizzle.h
Normal 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
|
||||
112
Engine/source/core/util/swizzleSpec.h
Normal file
112
Engine/source/core/util/swizzleSpec.h
Normal 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
|
||||
203
Engine/source/core/util/tAlignedArray.h
Normal file
203
Engine/source/core/util/tAlignedArray.h
Normal 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_
|
||||
47
Engine/source/core/util/tDictionary.cpp
Normal file
47
Engine/source/core/util/tDictionary.cpp
Normal 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];
|
||||
}
|
||||
|
||||
};
|
||||
867
Engine/source/core/util/tDictionary.h
Normal file
867
Engine/source/core/util/tDictionary.h
Normal 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
|
||||
|
||||
185
Engine/source/core/util/tFixedSizeDeque.h
Normal file
185
Engine/source/core/util/tFixedSizeDeque.h
Normal 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_
|
||||
106
Engine/source/core/util/tFixedSizeVector.h
Normal file
106
Engine/source/core/util/tFixedSizeVector.h
Normal 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_
|
||||
490
Engine/source/core/util/tList.h
Normal file
490
Engine/source/core/util/tList.h
Normal 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_
|
||||
|
||||
83
Engine/source/core/util/tNamedFactory.h
Normal file
83
Engine/source/core/util/tNamedFactory.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _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_
|
||||
|
||||
72
Engine/source/core/util/tSignal.cpp
Normal file
72
Engine/source/core/util/tSignal.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
666
Engine/source/core/util/tSignal.h
Normal file
666
Engine/source/core/util/tSignal.h
Normal 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_
|
||||
151
Engine/source/core/util/tSingleton.h
Normal file
151
Engine/source/core/util/tSingleton.h
Normal 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_
|
||||
|
||||
95
Engine/source/core/util/tUnmanagedVector.h
Normal file
95
Engine/source/core/util/tUnmanagedVector.h
Normal 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_
|
||||
101
Engine/source/core/util/tVector.cpp
Normal file
101
Engine/source/core/util/tVector.cpp
Normal 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
|
||||
964
Engine/source/core/util/tVector.h
Normal file
964
Engine/source/core/util/tVector.h
Normal 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_
|
||||
|
||||
46
Engine/source/core/util/tVectorSpecializations.h
Normal file
46
Engine/source/core/util/tVectorSpecializations.h
Normal 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_
|
||||
|
||||
54
Engine/source/core/util/test/testFixedSizeDeque.cpp
Normal file
54
Engine/source/core/util/test/testFixedSizeDeque.cpp
Normal 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() );
|
||||
}
|
||||
};
|
||||
44
Engine/source/core/util/test/testPath.cpp
Normal file
44
Engine/source/core/util/test/testPath.cpp
Normal 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");
|
||||
}
|
||||
};
|
||||
358
Engine/source/core/util/test/testString.cpp
Normal file
358
Engine/source/core/util/test/testString.cpp
Normal 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
|
||||
138
Engine/source/core/util/test/testVector2.cpp
Normal file
138
Engine/source/core/util/test/testVector2.cpp
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#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
|
||||
198
Engine/source/core/util/timeClass.cpp
Normal file
198
Engine/source/core/util/timeClass.cpp
Normal 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
|
||||
278
Engine/source/core/util/timeClass.h
Normal file
278
Engine/source/core/util/timeClass.h
Normal 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
|
||||
168
Engine/source/core/util/timeSource.h
Normal file
168
Engine/source/core/util/timeSource.h
Normal 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_
|
||||
453
Engine/source/core/util/uuid.cpp
Normal file
453
Engine/source/core/util/uuid.cpp
Normal 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(×tamp);
|
||||
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 ] );
|
||||
}
|
||||
}
|
||||
94
Engine/source/core/util/uuid.h
Normal file
94
Engine/source/core/util/uuid.h
Normal 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_
|
||||
320
Engine/source/core/util/zip/centralDir.cpp
Normal file
320
Engine/source/core/util/zip/centralDir.cpp
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/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
|
||||
112
Engine/source/core/util/zip/centralDir.h
Normal file
112
Engine/source/core/util/zip/centralDir.h
Normal 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_
|
||||
70
Engine/source/core/util/zip/compressor.cpp
Normal file
70
Engine/source/core/util/zip/compressor.cpp
Normal 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
|
||||
103
Engine/source/core/util/zip/compressor.h
Normal file
103
Engine/source/core/util/zip/compressor.h
Normal 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_
|
||||
50
Engine/source/core/util/zip/compressors/deflate.cpp
Normal file
50
Engine/source/core/util/zip/compressors/deflate.cpp
Normal 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
|
||||
47
Engine/source/core/util/zip/compressors/stored.cpp
Normal file
47
Engine/source/core/util/zip/compressors/stored.cpp
Normal 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
|
||||
84
Engine/source/core/util/zip/crctab.h
Normal file
84
Engine/source/core/util/zip/crctab.h
Normal 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_
|
||||
|
||||
59
Engine/source/core/util/zip/extraField.cpp
Normal file
59
Engine/source/core/util/zip/extraField.cpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#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
|
||||
84
Engine/source/core/util/zip/extraField.h
Normal file
84
Engine/source/core/util/zip/extraField.h
Normal 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_
|
||||
191
Engine/source/core/util/zip/fileHeader.cpp
Normal file
191
Engine/source/core/util/zip/fileHeader.cpp
Normal 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
|
||||
112
Engine/source/core/util/zip/fileHeader.h
Normal file
112
Engine/source/core/util/zip/fileHeader.h
Normal 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
|
||||
107
Engine/source/core/util/zip/unitTests/zipTest.h
Normal file
107
Engine/source/core/util/zip/unitTests/zipTest.h
Normal 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_
|
||||
196
Engine/source/core/util/zip/unitTests/zipTestMisc.cpp
Normal file
196
Engine/source/core/util/zip/unitTests/zipTestMisc.cpp
Normal 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;
|
||||
}
|
||||
};
|
||||
252
Engine/source/core/util/zip/unitTests/zipTestRead.cpp
Normal file
252
Engine/source/core/util/zip/unitTests/zipTestRead.cpp
Normal 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;
|
||||
}
|
||||
};
|
||||
244
Engine/source/core/util/zip/unitTests/zipTestWrite.cpp
Normal file
244
Engine/source/core/util/zip/unitTests/zipTestWrite.cpp
Normal 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;
|
||||
}
|
||||
};
|
||||
926
Engine/source/core/util/zip/zipArchive.cpp
Normal file
926
Engine/source/core/util/zip/zipArchive.cpp
Normal 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, µsecond);
|
||||
|
||||
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
|
||||
590
Engine/source/core/util/zip/zipArchive.h
Normal file
590
Engine/source/core/util/zip/zipArchive.h
Normal 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_
|
||||
204
Engine/source/core/util/zip/zipCryptStream.cpp
Normal file
204
Engine/source/core/util/zip/zipCryptStream.cpp
Normal 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;
|
||||
}
|
||||
68
Engine/source/core/util/zip/zipCryptStream.h
Normal file
68
Engine/source/core/util/zip/zipCryptStream.h
Normal 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_
|
||||
423
Engine/source/core/util/zip/zipObject.cpp
Normal file
423
Engine/source/core/util/zip/zipObject.cpp
Normal 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);
|
||||
}
|
||||
85
Engine/source/core/util/zip/zipObject.h
Normal file
85
Engine/source/core/util/zip/zipObject.h
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#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_
|
||||
173
Engine/source/core/util/zip/zipStatFilter.h
Normal file
173
Engine/source/core/util/zip/zipStatFilter.h
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#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_
|
||||
479
Engine/source/core/util/zip/zipSubStream.cpp
Normal file
479
Engine/source/core/util/zip/zipSubStream.cpp
Normal 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;
|
||||
}
|
||||
|
||||
121
Engine/source/core/util/zip/zipSubStream.h
Normal file
121
Engine/source/core/util/zip/zipSubStream.h
Normal 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_
|
||||
55
Engine/source/core/util/zip/zipTempStream.cpp
Normal file
55
Engine/source/core/util/zip/zipTempStream.cpp
Normal 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
|
||||
92
Engine/source/core/util/zip/zipTempStream.h
Normal file
92
Engine/source/core/util/zip/zipTempStream.h
Normal 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_
|
||||
475
Engine/source/core/util/zip/zipVolume.cpp
Normal file
475
Engine/source/core/util/zip/zipVolume.cpp
Normal 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();
|
||||
}
|
||||
|
||||
};
|
||||
71
Engine/source/core/util/zip/zipVolume.h
Normal file
71
Engine/source/core/util/zip/zipVolume.h
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue